# HG changeset patch # User Benjamin Golinvaux # Date 1558534426 -7200 # Node ID 92c400a09f1b1d55e201a824800ada3c5a9337c7 # Parent a386bbc955dc772d72bf4f97ff87982056b20f19# Parent ab236bb5dbc7ee9ea120c2cc4785bfc12080cd22 Merge from default diff -r a386bbc955dc -r 92c400a09f1b Applications/Generic/NativeStoneApplicationContext.cpp --- a/Applications/Generic/NativeStoneApplicationContext.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Generic/NativeStoneApplicationContext.cpp Wed May 22 16:13:46 2019 +0200 @@ -24,7 +24,7 @@ namespace OrthancStone { - IWidget& NativeStoneApplicationContext::GlobalMutexLocker::SetCentralWidget(IWidget* widget) + Deprecated::IWidget& NativeStoneApplicationContext::GlobalMutexLocker::SetCentralWidget(Deprecated::IWidget* widget) { that_.centralViewport_.SetCentralWidget(widget); return *widget; diff -r a386bbc955dc -r 92c400a09f1b Applications/Generic/NativeStoneApplicationContext.h --- a/Applications/Generic/NativeStoneApplicationContext.h Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Generic/NativeStoneApplicationContext.h Wed May 22 16:13:46 2019 +0200 @@ -21,9 +21,9 @@ #pragma once -#include "../../Framework/Viewport/WidgetViewport.h" -#include "../../Framework/Volumes/ISlicedVolume.h" -#include "../../Framework/Volumes/IVolumeLoader.h" +#include "../../Framework/Deprecated/Viewport/WidgetViewport.h" +#include "../../Framework/Deprecated/Volumes/ISlicedVolume.h" +#include "../../Framework/Deprecated/Volumes/IVolumeLoader.h" #include #include @@ -37,7 +37,7 @@ static void UpdateThread(NativeStoneApplicationContext* that); boost::recursive_mutex globalMutex_; - WidgetViewport centralViewport_; + Deprecated::WidgetViewport centralViewport_; boost::thread updateThread_; bool stopped_; unsigned int updateDelayInMs_; @@ -56,9 +56,9 @@ { } - IWidget& SetCentralWidget(IWidget* widget); // Takes ownership + Deprecated::IWidget& SetCentralWidget(Deprecated::IWidget* widget); // Takes ownership - IViewport& GetCentralViewport() + Deprecated::IViewport& GetCentralViewport() { return that_.centralViewport_; } diff -r a386bbc955dc -r 92c400a09f1b Applications/Generic/NativeStoneApplicationRunner.cpp --- a/Applications/Generic/NativeStoneApplicationRunner.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Generic/NativeStoneApplicationRunner.cpp Wed May 22 16:13:46 2019 +0200 @@ -19,8 +19,8 @@ **/ -#if ORTHANC_ENABLE_NATIVE != 1 -#error this file shall be included only with the ORTHANC_ENABLE_NATIVE set to 1 +#if ORTHANC_ENABLE_THREADS != 1 +#error this file shall be included only with the ORTHANC_ENABLE_THREADS set to 1 #endif #include "NativeStoneApplicationRunner.h" @@ -43,7 +43,7 @@ // Anonymous namespace to avoid clashes against other compilation modules namespace { - class LogStatusBar : public IStatusBar + class LogStatusBar : public Deprecated::IStatusBar { public: virtual void ClearMessage() @@ -202,17 +202,17 @@ { // use multiple threads to execute asynchronous tasks like // download content - Oracle oracle(6); + Deprecated::Oracle oracle(6); oracle.Start(); { - OracleWebService webService( + Deprecated::OracleWebService webService( broker_, oracle, webServiceParameters, context); context.SetWebService(webService); context.SetOrthancBaseUrl(webServiceParameters.GetUrl()); - OracleDelayedCallExecutor delayedExecutor(broker_, oracle, context); + Deprecated::OracleDelayedCallExecutor delayedExecutor(broker_, oracle, context); context.SetDelayedCallExecutor(delayedExecutor); application_.Initialize(&context, statusBar, parameters); diff -r a386bbc955dc -r 92c400a09f1b Applications/Generic/NativeStoneApplicationRunner.h --- a/Applications/Generic/NativeStoneApplicationRunner.h Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Generic/NativeStoneApplicationRunner.h Wed May 22 16:13:46 2019 +0200 @@ -23,8 +23,8 @@ #include "../IStoneApplication.h" -#if ORTHANC_ENABLE_NATIVE != 1 -#error this file shall be included only with the ORTHANC_ENABLE_NATIVE set to 1 +#if ORTHANC_ENABLE_THREADS != 1 +#error this file shall be included only with the ORTHANC_ENABLE_THREADS set to 1 #endif namespace OrthancStone diff -r a386bbc955dc -r 92c400a09f1b Applications/IStoneApplication.h --- a/Applications/IStoneApplication.h Wed May 22 16:01:34 2019 +0200 +++ b/Applications/IStoneApplication.h Wed May 22 16:13:46 2019 +0200 @@ -22,9 +22,10 @@ #pragma once #include "StoneApplicationContext.h" +#include "../Framework/Deprecated/Viewport/WidgetViewport.h" + #include -#include "../Framework/Viewport/WidgetViewport.h" -#include "json/json.h" +#include namespace OrthancStone { @@ -47,7 +48,7 @@ virtual void DeclareStartupOptions(boost::program_options::options_description& options) = 0; virtual void Initialize(StoneApplicationContext* context, - IStatusBar& statusBar, + Deprecated::IStatusBar& statusBar, const boost::program_options::variables_map& parameters) = 0; /** @@ -63,7 +64,7 @@ #endif virtual std::string GetTitle() const = 0; - virtual IWidget* GetCentralWidget() = 0; + virtual Deprecated::IWidget* GetCentralWidget() = 0; virtual void Finalize() = 0; }; } diff -r a386bbc955dc -r 92c400a09f1b Applications/Qt/QCairoWidget.cpp --- a/Applications/Qt/QCairoWidget.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Qt/QCairoWidget.cpp Wed May 22 16:13:46 2019 +0200 @@ -27,14 +27,14 @@ QCairoWidget::StoneObserver::StoneObserver(QCairoWidget& that, - OrthancStone::IViewport& viewport, + Deprecated::IViewport& viewport, OrthancStone::MessageBroker& broker) : OrthancStone::IObserver(broker), that_(that) { // get notified each time the content of the central viewport changes viewport.RegisterObserverCallback( - new OrthancStone::Callable + new OrthancStone::Callable (*this, &StoneObserver::OnViewportChanged)); } @@ -68,7 +68,7 @@ context_ != NULL) { OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_); - OrthancStone::IViewport& viewport = locker.GetCentralViewport(); + Deprecated::IViewport& viewport = locker.GetCentralViewport(); Orthanc::ImageAccessor a; surface_.GetWriteableAccessor(a); viewport.Render(a); @@ -125,7 +125,7 @@ { OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_); - locker.GetCentralViewport().MouseDown(button, event->pos().x(), event->pos().y(), stoneModifiers, std::vector()); + locker.GetCentralViewport().MouseDown(button, event->pos().x(), event->pos().y(), stoneModifiers, std::vector()); } } @@ -140,7 +140,7 @@ void QCairoWidget::mouseMoveEvent(QMouseEvent* event) { OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_); - locker.GetCentralViewport().MouseMove(event->pos().x(), event->pos().y(), std::vector()); + locker.GetCentralViewport().MouseMove(event->pos().x(), event->pos().y(), std::vector()); } diff -r a386bbc955dc -r 92c400a09f1b Applications/Qt/QCairoWidget.h --- a/Applications/Qt/QCairoWidget.h Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Qt/QCairoWidget.h Wed May 22 16:13:46 2019 +0200 @@ -40,10 +40,10 @@ public: StoneObserver(QCairoWidget& that, - OrthancStone::IViewport& viewport, + Deprecated::IViewport& viewport, OrthancStone::MessageBroker& broker); - void OnViewportChanged(const OrthancStone::IViewport::ViewportChangedMessage& message) + void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message) { that_.OnViewportChanged(); } diff -r a386bbc955dc -r 92c400a09f1b Applications/Samples/CMakeLists.txt --- a/Applications/Samples/CMakeLists.txt Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Samples/CMakeLists.txt Wed May 22 16:13:46 2019 +0200 @@ -8,6 +8,9 @@ include(../../Resources/CMake/OrthancStoneParameters.cmake) +set(ENABLE_STONE_DEPRECATED ON) # Need deprecated classes for these samples + + if (OPENSSL_NO_CAPIENG) add_definitions(-DOPENSSL_NO_CAPIENG=1) endif() @@ -208,7 +211,6 @@ ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/AppStatus.h ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/MainWidgetInteractor.h - ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Messages.h ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/SimpleViewerApplication.h ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/ThumbnailInteractor.cpp diff -r a386bbc955dc -r 92c400a09f1b Applications/Samples/SampleApplicationBase.h --- a/Applications/Samples/SampleApplicationBase.h Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Samples/SampleApplicationBase.h Wed May 22 16:13:46 2019 +0200 @@ -22,7 +22,7 @@ #pragma once #include "../../Applications/IStoneApplication.h" -#include "../../Framework/Widgets/WorldSceneWidget.h" +#include "../../Framework/Deprecated/Widgets/WorldSceneWidget.h" #if ORTHANC_ENABLE_WASM==1 #include "../../Platforms/Wasm/WasmPlatformApplicationAdapter.h" @@ -42,11 +42,11 @@ { protected: // ownership is transferred to the application context - WorldSceneWidget* mainWidget_; + Deprecated::WorldSceneWidget* mainWidget_; public: virtual void Initialize(StoneApplicationContext* context, - IStatusBar& statusBar, + Deprecated::IStatusBar& statusBar, const boost::program_options::variables_map& parameters) ORTHANC_OVERRIDE { } @@ -64,7 +64,7 @@ virtual void Finalize() ORTHANC_OVERRIDE {} - virtual IWidget* GetCentralWidget() ORTHANC_OVERRIDE {return mainWidget_;} + virtual Deprecated::IWidget* GetCentralWidget() ORTHANC_OVERRIDE {return mainWidget_;} #if ORTHANC_ENABLE_WASM==1 // default implementations for a single canvas named "canvas" in the HTML and an emtpy WasmApplicationAdapter diff -r a386bbc955dc -r 92c400a09f1b Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp --- a/Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp Wed May 22 16:13:46 2019 +0200 @@ -24,27 +24,27 @@ namespace SimpleViewer { - IWorldSceneMouseTracker* MainWidgetInteractor::CreateMouseTracker(WorldSceneWidget& widget, - const ViewportGeometry& view, + Deprecated::IWorldSceneMouseTracker* MainWidgetInteractor::CreateMouseTracker(Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, MouseButton button, KeyboardModifiers modifiers, int viewportX, int viewportY, double x, double y, - IStatusBar* statusBar, - const std::vector& displayTouches) + Deprecated::IStatusBar* statusBar, + const std::vector& displayTouches) { if (button == MouseButton_Left) { if (application_.GetCurrentTool() == Tool_LineMeasure) { - return new LineMeasureTracker(statusBar, dynamic_cast(widget).GetSlice(), + return new Deprecated::LineMeasureTracker(statusBar, dynamic_cast(widget).GetSlice(), x, y, 255, 0, 0, application_.GetFont()); } else if (application_.GetCurrentTool() == Tool_CircleMeasure) { - return new CircleMeasureTracker(statusBar, dynamic_cast(widget).GetSlice(), + return new Deprecated::CircleMeasureTracker(statusBar, dynamic_cast(widget).GetSlice(), x, y, 255, 0, 0, application_.GetFont()); } else if (application_.GetCurrentTool() == Tool_Crop) @@ -68,15 +68,15 @@ } void MainWidgetInteractor::MouseOver(CairoContext& context, - WorldSceneWidget& widget, - const ViewportGeometry& view, + Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, double x, double y, - IStatusBar* statusBar) + Deprecated::IStatusBar* statusBar) { if (statusBar != NULL) { - Vector p = dynamic_cast(widget).GetSlice().MapSliceToWorldCoordinates(x, y); + Vector p = dynamic_cast(widget).GetSlice().MapSliceToWorldCoordinates(x, y); char buf[64]; sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", @@ -85,18 +85,18 @@ } } - void MainWidgetInteractor::MouseWheel(WorldSceneWidget& widget, + void MainWidgetInteractor::MouseWheel(Deprecated::WorldSceneWidget& widget, MouseWheelDirection direction, KeyboardModifiers modifiers, - IStatusBar* statusBar) + Deprecated::IStatusBar* statusBar) { } - void MainWidgetInteractor::KeyPressed(WorldSceneWidget& widget, + void MainWidgetInteractor::KeyPressed(Deprecated::WorldSceneWidget& widget, KeyboardKeys key, char keyChar, KeyboardModifiers modifiers, - IStatusBar* statusBar) + Deprecated::IStatusBar* statusBar) { switch (keyChar) { diff -r a386bbc955dc -r 92c400a09f1b Applications/Samples/SimpleViewer/MainWidgetInteractor.h --- a/Applications/Samples/SimpleViewer/MainWidgetInteractor.h Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Samples/SimpleViewer/MainWidgetInteractor.h Wed May 22 16:13:46 2019 +0200 @@ -20,7 +20,7 @@ #pragma once -#include "Framework/Widgets/IWorldSceneInteractor.h" +#include "../../../Framework/Deprecated/Widgets/IWorldSceneInteractor.h" using namespace OrthancStone; @@ -28,7 +28,7 @@ class SimpleViewerApplication; - class MainWidgetInteractor : public IWorldSceneInteractor + class MainWidgetInteractor : public Deprecated::IWorldSceneInteractor { private: SimpleViewerApplication& application_; @@ -42,34 +42,34 @@ /** WorldSceneWidget: */ - virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, - const ViewportGeometry& view, - MouseButton button, - KeyboardModifiers modifiers, - int viewportX, - int viewportY, - double x, - double y, - IStatusBar* statusBar, - const std::vector& displayTouches); + virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, + MouseButton button, + KeyboardModifiers modifiers, + int viewportX, + int viewportY, + double x, + double y, + Deprecated::IStatusBar* statusBar, + const std::vector& displayTouches); virtual void MouseOver(CairoContext& context, - WorldSceneWidget& widget, - const ViewportGeometry& view, + Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, double x, double y, - IStatusBar* statusBar); + Deprecated::IStatusBar* statusBar); - virtual void MouseWheel(WorldSceneWidget& widget, + virtual void MouseWheel(Deprecated::WorldSceneWidget& widget, MouseWheelDirection direction, KeyboardModifiers modifiers, - IStatusBar* statusBar); + Deprecated::IStatusBar* statusBar); - virtual void KeyPressed(WorldSceneWidget& widget, + virtual void KeyPressed(Deprecated::WorldSceneWidget& widget, KeyboardKeys key, char keyChar, KeyboardModifiers modifiers, - IStatusBar* statusBar); + Deprecated::IStatusBar* statusBar); }; diff -r a386bbc955dc -r 92c400a09f1b Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp --- a/Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp Wed May 22 16:13:46 2019 +0200 @@ -33,28 +33,26 @@ { void SimpleViewerApplication::Initialize(StoneApplicationContext* context, - IStatusBar& statusBar, + Deprecated::IStatusBar& statusBar, const boost::program_options::variables_map& parameters) { - using namespace OrthancStone; - context_ = context; statusBar_ = &statusBar; {// initialize viewports and layout - mainLayout_ = new LayoutWidget("main-layout"); + mainLayout_ = new Deprecated::LayoutWidget("main-layout"); mainLayout_->SetPadding(10); mainLayout_->SetBackgroundCleared(true); mainLayout_->SetBackgroundColor(0, 0, 0); mainLayout_->SetHorizontal(); - thumbnailsLayout_ = new LayoutWidget("thumbnail-layout"); + thumbnailsLayout_ = new Deprecated::LayoutWidget("thumbnail-layout"); thumbnailsLayout_->SetPadding(10); thumbnailsLayout_->SetBackgroundCleared(true); thumbnailsLayout_->SetBackgroundColor(50, 50, 50); thumbnailsLayout_->SetVertical(); - mainWidget_ = new SliceViewerWidget(IObserver::GetBroker(), "main-viewport"); + mainWidget_ = new Deprecated::SliceViewerWidget(IObserver::GetBroker(), "main-viewport"); //mainWidget_->RegisterObserver(*this); // hierarchy @@ -62,8 +60,8 @@ mainLayout_->AddWidget(mainWidget_); // sources - smartLoader_.reset(new SmartLoader(IObserver::GetBroker(), context->GetOrthancApiClient())); - smartLoader_->SetImageQuality(SliceImageQuality_FullPam); + smartLoader_.reset(new Deprecated::SmartLoader(IObserver::GetBroker(), context->GetOrthancApiClient())); + smartLoader_->SetImageQuality(Deprecated::SliceImageQuality_FullPam); mainLayout_->SetTransmitMouseOver(true); mainWidgetInteractor_.reset(new MainWidgetInteractor(*this)); @@ -78,7 +76,7 @@ if (parameters.count("studyId") < 1) { LOG(WARNING) << "The study ID is missing, will take the first studyId found in Orthanc"; - context->GetOrthancApiClient().GetJsonAsync("/studies", new Callable(*this, &SimpleViewerApplication::OnStudyListReceived)); + context->GetOrthancApiClient().GetJsonAsync("/studies", new Callable(*this, &SimpleViewerApplication::OnStudyListReceived)); } else { @@ -98,7 +96,7 @@ options.add(generic); } - void SimpleViewerApplication::OnStudyListReceived(const OrthancApiClient::JsonResponseReadyMessage& message) + void SimpleViewerApplication::OnStudyListReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) { const Json::Value& response = message.GetJson(); @@ -108,7 +106,7 @@ SelectStudy(response[0].asString()); } } - void SimpleViewerApplication::OnStudyReceived(const OrthancApiClient::JsonResponseReadyMessage& message) + void SimpleViewerApplication::OnStudyReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) { const Json::Value& response = message.GetJson(); @@ -116,12 +114,12 @@ { for (size_t i=0; i < response["Series"].size(); i++) { - context_->GetOrthancApiClient().GetJsonAsync("/series/" + response["Series"][(int)i].asString(), new Callable(*this, &SimpleViewerApplication::OnSeriesReceived)); + context_->GetOrthancApiClient().GetJsonAsync("/series/" + response["Series"][(int)i].asString(), new Callable(*this, &SimpleViewerApplication::OnSeriesReceived)); } } } - void SimpleViewerApplication::OnSeriesReceived(const OrthancApiClient::JsonResponseReadyMessage& message) + void SimpleViewerApplication::OnSeriesReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) { const Json::Value& response = message.GetJson(); @@ -154,13 +152,13 @@ { LOG(INFO) << "Loading thumbnail for series " << seriesId; - SliceViewerWidget* thumbnailWidget = - new SliceViewerWidget(IObserver::GetBroker(), "thumbnail-series-" + seriesId); + Deprecated::SliceViewerWidget* thumbnailWidget = + new Deprecated::SliceViewerWidget(IObserver::GetBroker(), "thumbnail-series-" + seriesId); thumbnails_.push_back(thumbnailWidget); thumbnailsLayout_->AddWidget(thumbnailWidget); thumbnailWidget->RegisterObserverCallback( - new Callable + new Callable (*this, &SimpleViewerApplication::OnWidgetGeometryChanged)); smartLoader_->SetFrameInWidget(*thumbnailWidget, 0, instanceId, 0); @@ -169,13 +167,13 @@ void SimpleViewerApplication::SelectStudy(const std::string& studyId) { - context_->GetOrthancApiClient().GetJsonAsync("/studies/" + studyId, new Callable(*this, &SimpleViewerApplication::OnStudyReceived)); + context_->GetOrthancApiClient().GetJsonAsync("/studies/" + studyId, new Callable(*this, &SimpleViewerApplication::OnStudyReceived)); } - void SimpleViewerApplication::OnWidgetGeometryChanged(const SliceViewerWidget::GeometryChangedMessage& message) + void SimpleViewerApplication::OnWidgetGeometryChanged(const Deprecated::SliceViewerWidget::GeometryChangedMessage& message) { // TODO: The "const_cast" could probably be replaced by "mainWidget_" - const_cast(message.GetOrigin()).FitContent(); + const_cast(message.GetOrigin()).FitContent(); } void SimpleViewerApplication::SelectSeriesInMainViewport(const std::string& seriesId) diff -r a386bbc955dc -r 92c400a09f1b Applications/Samples/SimpleViewer/SimpleViewerApplication.h --- a/Applications/Samples/SimpleViewer/SimpleViewerApplication.h Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Samples/SimpleViewer/SimpleViewerApplication.h Wed May 22 16:13:46 2019 +0200 @@ -29,12 +29,12 @@ #include "Applications/IStoneApplication.h" -#include "Framework/Layers/CircleMeasureTracker.h" -#include "Framework/Layers/LineMeasureTracker.h" -#include "Framework/Widgets/SliceViewerWidget.h" -#include "Framework/Widgets/LayoutWidget.h" -#include "Framework/Messages/IObserver.h" -#include "Framework/SmartLoader.h" +#include "../../../Framework/Deprecated/Layers/CircleMeasureTracker.h" +#include "../../../Framework/Deprecated/Layers/LineMeasureTracker.h" +#include "../../../Framework/Deprecated/SmartLoader.h" +#include "../../../Framework/Deprecated/Widgets/LayoutWidget.h" +#include "../../../Framework/Deprecated/Widgets/SliceViewerWidget.h" +#include "../../../Framework/Messages/IObserver.h" #if ORTHANC_ENABLE_WASM==1 #include "Platforms/Wasm/WasmPlatformApplicationAdapter.h" @@ -83,18 +83,18 @@ std::auto_ptr mainWidgetInteractor_; std::auto_ptr thumbnailInteractor_; - LayoutWidget* mainLayout_; - LayoutWidget* thumbnailsLayout_; - SliceViewerWidget* mainWidget_; - std::vector thumbnails_; + Deprecated::LayoutWidget* mainLayout_; + Deprecated::LayoutWidget* thumbnailsLayout_; + Deprecated::SliceViewerWidget* mainWidget_; + std::vector thumbnails_; std::map > instancesIdsPerSeriesId_; std::map seriesTags_; unsigned int currentInstanceIndex_; - OrthancStone::WidgetViewport* wasmViewport1_; - OrthancStone::WidgetViewport* wasmViewport2_; + Deprecated::WidgetViewport* wasmViewport1_; + Deprecated::WidgetViewport* wasmViewport2_; - IStatusBar* statusBar_; - std::auto_ptr smartLoader_; + Deprecated::IStatusBar* statusBar_; + std::auto_ptr smartLoader_; Orthanc::Font font_; @@ -112,24 +112,24 @@ } virtual void Finalize() ORTHANC_OVERRIDE {} - virtual IWidget* GetCentralWidget() ORTHANC_OVERRIDE {return mainLayout_;} + virtual Deprecated::IWidget* GetCentralWidget() ORTHANC_OVERRIDE {return mainLayout_;} virtual void DeclareStartupOptions(boost::program_options::options_description& options) ORTHANC_OVERRIDE; virtual void Initialize(StoneApplicationContext* context, - IStatusBar& statusBar, + Deprecated::IStatusBar& statusBar, const boost::program_options::variables_map& parameters) ORTHANC_OVERRIDE; - void OnStudyListReceived(const OrthancApiClient::JsonResponseReadyMessage& message); + void OnStudyListReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message); - void OnStudyReceived(const OrthancApiClient::JsonResponseReadyMessage& message); + void OnStudyReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message); - void OnSeriesReceived(const OrthancApiClient::JsonResponseReadyMessage& message); + void OnSeriesReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message); void LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId); void SelectStudy(const std::string& studyId); - void OnWidgetGeometryChanged(const SliceViewerWidget::GeometryChangedMessage& message); + void OnWidgetGeometryChanged(const Deprecated::SliceViewerWidget::GeometryChangedMessage& message); void SelectSeriesInMainViewport(const std::string& seriesId); diff -r a386bbc955dc -r 92c400a09f1b Applications/Samples/SimpleViewer/ThumbnailInteractor.cpp --- a/Applications/Samples/SimpleViewer/ThumbnailInteractor.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Samples/SimpleViewer/ThumbnailInteractor.cpp Wed May 22 16:13:46 2019 +0200 @@ -24,16 +24,16 @@ namespace SimpleViewer { - IWorldSceneMouseTracker* ThumbnailInteractor::CreateMouseTracker(WorldSceneWidget& widget, - const ViewportGeometry& view, + Deprecated::IWorldSceneMouseTracker* ThumbnailInteractor::CreateMouseTracker(Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, MouseButton button, KeyboardModifiers modifiers, int viewportX, int viewportY, double x, double y, - IStatusBar* statusBar, - const std::vector& displayTouches) + Deprecated::IStatusBar* statusBar, + const std::vector& displayTouches) { if (button == MouseButton_Left) { diff -r a386bbc955dc -r 92c400a09f1b Applications/Samples/SimpleViewer/ThumbnailInteractor.h --- a/Applications/Samples/SimpleViewer/ThumbnailInteractor.h Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Samples/SimpleViewer/ThumbnailInteractor.h Wed May 22 16:13:46 2019 +0200 @@ -21,7 +21,7 @@ #pragma once -#include "Framework/Widgets/IWorldSceneInteractor.h" +#include "../../../Framework/Deprecated/Widgets/IWorldSceneInteractor.h" using namespace OrthancStone; @@ -29,7 +29,7 @@ class SimpleViewerApplication; - class ThumbnailInteractor : public IWorldSceneInteractor + class ThumbnailInteractor : public Deprecated::IWorldSceneInteractor { private: SimpleViewerApplication& application_; @@ -39,36 +39,36 @@ { } - virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, - const ViewportGeometry& view, - MouseButton button, - KeyboardModifiers modifiers, - int viewportX, - int viewportY, - double x, - double y, - IStatusBar* statusBar, - const std::vector& displayTouches); + virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, + MouseButton button, + KeyboardModifiers modifiers, + int viewportX, + int viewportY, + double x, + double y, + Deprecated::IStatusBar* statusBar, + const std::vector& displayTouches); virtual void MouseOver(CairoContext& context, - WorldSceneWidget& widget, - const ViewportGeometry& view, + Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, double x, double y, - IStatusBar* statusBar) + Deprecated::IStatusBar* statusBar) {} - virtual void MouseWheel(WorldSceneWidget& widget, + virtual void MouseWheel(Deprecated::WorldSceneWidget& widget, MouseWheelDirection direction, KeyboardModifiers modifiers, - IStatusBar* statusBar) + Deprecated::IStatusBar* statusBar) {} - virtual void KeyPressed(WorldSceneWidget& widget, + virtual void KeyPressed(Deprecated::WorldSceneWidget& widget, KeyboardKeys key, char keyChar, KeyboardModifiers modifiers, - IStatusBar* statusBar) + Deprecated::IStatusBar* statusBar) {} }; diff -r a386bbc955dc -r 92c400a09f1b Applications/Samples/SimpleViewerApplicationSingleFile.h --- a/Applications/Samples/SimpleViewerApplicationSingleFile.h Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Samples/SimpleViewerApplicationSingleFile.h Wed May 22 16:13:46 2019 +0200 @@ -23,12 +23,12 @@ #include "SampleApplicationBase.h" -#include "../../Framework/Layers/CircleMeasureTracker.h" -#include "../../Framework/Layers/LineMeasureTracker.h" -#include "../../Framework/Widgets/SliceViewerWidget.h" -#include "../../Framework/Widgets/LayoutWidget.h" +#include "../../Framework/Deprecated/Layers/CircleMeasureTracker.h" +#include "../../Framework/Deprecated/Layers/LineMeasureTracker.h" +#include "../../Framework/Deprecated/SmartLoader.h" +#include "../../Framework/Deprecated/Widgets/LayoutWidget.h" +#include "../../Framework/Deprecated/Widgets/SliceViewerWidget.h" #include "../../Framework/Messages/IObserver.h" -#include "../../Framework/SmartLoader.h" #if ORTHANC_ENABLE_WASM==1 #include "../../Platforms/Wasm/WasmPlatformApplicationAdapter.h" @@ -47,7 +47,7 @@ public IObserver { private: - class ThumbnailInteractor : public IWorldSceneInteractor + class ThumbnailInteractor : public Deprecated::IWorldSceneInteractor { private: SimpleViewerApplication& application_; @@ -58,16 +58,16 @@ { } - virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, - const ViewportGeometry& view, + virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, MouseButton button, KeyboardModifiers modifiers, int viewportX, int viewportY, double x, double y, - IStatusBar* statusBar, - const std::vector& displayTouches) + Deprecated::IStatusBar* statusBar, + const std::vector& displayTouches) { if (button == MouseButton_Left) { @@ -79,31 +79,31 @@ } virtual void MouseOver(CairoContext& context, - WorldSceneWidget& widget, - const ViewportGeometry& view, + Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, double x, double y, - IStatusBar* statusBar) + Deprecated::IStatusBar* statusBar) { } - virtual void MouseWheel(WorldSceneWidget& widget, + virtual void MouseWheel(Deprecated::WorldSceneWidget& widget, MouseWheelDirection direction, KeyboardModifiers modifiers, - IStatusBar* statusBar) + Deprecated::IStatusBar* statusBar) { } - virtual void KeyPressed(WorldSceneWidget& widget, + virtual void KeyPressed(Deprecated::WorldSceneWidget& widget, KeyboardKeys key, char keyChar, KeyboardModifiers modifiers, - IStatusBar* statusBar) + Deprecated::IStatusBar* statusBar) { } }; - class MainWidgetInteractor : public IWorldSceneInteractor + class MainWidgetInteractor : public Deprecated::IWorldSceneInteractor { private: SimpleViewerApplication& application_; @@ -114,27 +114,27 @@ { } - virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, - const ViewportGeometry& view, + virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, MouseButton button, KeyboardModifiers modifiers, int viewportX, int viewportY, double x, double y, - IStatusBar* statusBar, - const std::vector& displayTouches) + Deprecated::IStatusBar* statusBar, + const std::vector& displayTouches) { if (button == MouseButton_Left) { if (application_.currentTool_ == Tool_LineMeasure) { - return new LineMeasureTracker(statusBar, dynamic_cast(widget).GetSlice(), + return new Deprecated::LineMeasureTracker(statusBar, dynamic_cast(widget).GetSlice(), x, y, 255, 0, 0, application_.GetFont()); } else if (application_.currentTool_ == Tool_CircleMeasure) { - return new CircleMeasureTracker(statusBar, dynamic_cast(widget).GetSlice(), + return new Deprecated::CircleMeasureTracker(statusBar, dynamic_cast(widget).GetSlice(), x, y, 255, 0, 0, application_.GetFont()); } } @@ -142,15 +142,15 @@ } virtual void MouseOver(CairoContext& context, - WorldSceneWidget& widget, - const ViewportGeometry& view, + Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, double x, double y, - IStatusBar* statusBar) + Deprecated::IStatusBar* statusBar) { if (statusBar != NULL) { - Vector p = dynamic_cast(widget).GetSlice().MapSliceToWorldCoordinates(x, y); + Vector p = dynamic_cast(widget).GetSlice().MapSliceToWorldCoordinates(x, y); char buf[64]; sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", @@ -159,18 +159,18 @@ } } - virtual void MouseWheel(WorldSceneWidget& widget, + virtual void MouseWheel(Deprecated::WorldSceneWidget& widget, MouseWheelDirection direction, KeyboardModifiers modifiers, - IStatusBar* statusBar) + Deprecated::IStatusBar* statusBar) { } - virtual void KeyPressed(WorldSceneWidget& widget, + virtual void KeyPressed(Deprecated::WorldSceneWidget& widget, KeyboardKeys key, char keyChar, KeyboardModifiers modifiers, - IStatusBar* statusBar) + Deprecated::IStatusBar* statusBar) { switch (keyChar) { @@ -241,19 +241,19 @@ Tool currentTool_; std::auto_ptr mainWidgetInteractor_; std::auto_ptr thumbnailInteractor_; - LayoutWidget* mainLayout_; - LayoutWidget* thumbnailsLayout_; - std::vector thumbnails_; + Deprecated::LayoutWidget* mainLayout_; + Deprecated::LayoutWidget* thumbnailsLayout_; + std::vector thumbnails_; std::map > instancesIdsPerSeriesId_; std::map seriesTags_; unsigned int currentInstanceIndex_; - OrthancStone::WidgetViewport* wasmViewport1_; - OrthancStone::WidgetViewport* wasmViewport2_; + Deprecated::WidgetViewport* wasmViewport1_; + Deprecated::WidgetViewport* wasmViewport2_; - IStatusBar* statusBar_; - std::auto_ptr smartLoader_; + Deprecated::IStatusBar* statusBar_; + std::auto_ptr smartLoader_; Orthanc::Font font_; @@ -282,7 +282,7 @@ } virtual void Initialize(StoneApplicationContext* context, - IStatusBar& statusBar, + Deprecated::IStatusBar& statusBar, const boost::program_options::variables_map& parameters) { using namespace OrthancStone; @@ -291,19 +291,19 @@ statusBar_ = &statusBar; {// initialize viewports and layout - mainLayout_ = new LayoutWidget("main-layout"); + mainLayout_ = new Deprecated::LayoutWidget("main-layout"); mainLayout_->SetPadding(10); mainLayout_->SetBackgroundCleared(true); mainLayout_->SetBackgroundColor(0, 0, 0); mainLayout_->SetHorizontal(); - thumbnailsLayout_ = new LayoutWidget("thumbnail-layout"); + thumbnailsLayout_ = new Deprecated::LayoutWidget("thumbnail-layout"); thumbnailsLayout_->SetPadding(10); thumbnailsLayout_->SetBackgroundCleared(true); thumbnailsLayout_->SetBackgroundColor(50, 50, 50); thumbnailsLayout_->SetVertical(); - mainWidget_ = new SliceViewerWidget(GetBroker(), "main-viewport"); + mainWidget_ = new Deprecated::SliceViewerWidget(GetBroker(), "main-viewport"); //mainWidget_->RegisterObserver(*this); // hierarchy @@ -311,8 +311,8 @@ mainLayout_->AddWidget(mainWidget_); // sources - smartLoader_.reset(new SmartLoader(GetBroker(), context->GetOrthancApiClient())); - smartLoader_->SetImageQuality(SliceImageQuality_FullPam); + smartLoader_.reset(new Deprecated::SmartLoader(GetBroker(), context->GetOrthancApiClient())); + smartLoader_->SetImageQuality(Deprecated::SliceImageQuality_FullPam); mainLayout_->SetTransmitMouseOver(true); mainWidgetInteractor_.reset(new MainWidgetInteractor(*this)); @@ -329,7 +329,7 @@ LOG(WARNING) << "The study ID is missing, will take the first studyId found in Orthanc"; context->GetOrthancApiClient().GetJsonAsync( "/studies", - new Callable + new Callable (*this, &SimpleViewerApplication::OnStudyListReceived)); } else @@ -338,7 +338,7 @@ } } - void OnStudyListReceived(const OrthancApiClient::JsonResponseReadyMessage& message) + void OnStudyListReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) { const Json::Value& response = message.GetJson(); @@ -349,7 +349,7 @@ } } - void OnStudyReceived(const OrthancApiClient::JsonResponseReadyMessage& message) + void OnStudyReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) { const Json::Value& response = message.GetJson(); @@ -359,13 +359,13 @@ { context_->GetOrthancApiClient().GetJsonAsync( "/series/" + response["Series"][(int)i].asString(), - new Callable + new Callable (*this, &SimpleViewerApplication::OnSeriesReceived)); } } } - void OnSeriesReceived(const OrthancApiClient::JsonResponseReadyMessage& message) + void OnSeriesReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) { const Json::Value& response = message.GetJson(); @@ -387,7 +387,7 @@ LoadThumbnailForSeries(seriesId, instancesIdsPerSeriesId_[seriesId][0]); // if this is the first thumbnail loaded, load the first instance in the mainWidget - SliceViewerWidget& widget = *dynamic_cast(mainWidget_); + Deprecated::SliceViewerWidget& widget = *dynamic_cast(mainWidget_); if (widget.GetLayerCount() == 0) { smartLoader_->SetFrameInWidget(widget, 0, instancesIdsPerSeriesId_[seriesId][0], 0); @@ -398,10 +398,10 @@ void LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId) { LOG(INFO) << "Loading thumbnail for series " << seriesId; - SliceViewerWidget* thumbnailWidget = new SliceViewerWidget(GetBroker(), "thumbnail-series-" + seriesId); + Deprecated::SliceViewerWidget* thumbnailWidget = new Deprecated::SliceViewerWidget(GetBroker(), "thumbnail-series-" + seriesId); thumbnails_.push_back(thumbnailWidget); thumbnailsLayout_->AddWidget(thumbnailWidget); - thumbnailWidget->RegisterObserverCallback(new Callable(*this, &SimpleViewerApplication::OnWidgetGeometryChanged)); + thumbnailWidget->RegisterObserverCallback(new Callable(*this, &SimpleViewerApplication::OnWidgetGeometryChanged)); smartLoader_->SetFrameInWidget(*thumbnailWidget, 0, instanceId, 0); thumbnailWidget->SetInteractor(*thumbnailInteractor_); } @@ -410,19 +410,19 @@ { LOG(INFO) << "Selecting study: " << studyId; context_->GetOrthancApiClient().GetJsonAsync( - "/studies/" + studyId, new Callable + "/studies/" + studyId, new Callable (*this, &SimpleViewerApplication::OnStudyReceived)); } - void OnWidgetGeometryChanged(const SliceViewerWidget::GeometryChangedMessage& message) + void OnWidgetGeometryChanged(const Deprecated::SliceViewerWidget::GeometryChangedMessage& message) { // TODO: The "const_cast" could probably be replaced by "mainWidget" - const_cast(message.GetOrigin()).FitContent(); + const_cast(message.GetOrigin()).FitContent(); } void SelectSeriesInMainViewport(const std::string& seriesId) { - SliceViewerWidget& widget = *dynamic_cast(mainWidget_); + Deprecated::SliceViewerWidget& widget = *dynamic_cast(mainWidget_); smartLoader_->SetFrameInWidget(widget, 0, instancesIdsPerSeriesId_[seriesId][0], 0); } diff -r a386bbc955dc -r 92c400a09f1b Applications/Samples/SingleFrameApplication.h --- a/Applications/Samples/SingleFrameApplication.h Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Samples/SingleFrameApplication.h Wed May 22 16:13:46 2019 +0200 @@ -23,8 +23,8 @@ #include "SampleApplicationBase.h" -#include "../../Framework/Layers/DicomSeriesVolumeSlicer.h" -#include "../../Framework/Widgets/SliceViewerWidget.h" +#include "../../Framework/Deprecated/Layers/DicomSeriesVolumeSlicer.h" +#include "../../Framework/Deprecated/Widgets/SliceViewerWidget.h" #include #include @@ -41,7 +41,7 @@ public IObserver { private: - class Interactor : public IWorldSceneInteractor + class Interactor : public Deprecated::IWorldSceneInteractor { private: SingleFrameApplication& application_; @@ -52,30 +52,30 @@ { } - virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, - const ViewportGeometry& view, - MouseButton button, - KeyboardModifiers modifiers, - int viewportX, - int viewportY, - double x, - double y, - IStatusBar* statusBar, - const std::vector& displayTouches) + virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, + MouseButton button, + KeyboardModifiers modifiers, + int viewportX, + int viewportY, + double x, + double y, + Deprecated::IStatusBar* statusBar, + const std::vector& displayTouches) { return NULL; } virtual void MouseOver(CairoContext& context, - WorldSceneWidget& widget, - const ViewportGeometry& view, + Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, double x, double y, - IStatusBar* statusBar) + Deprecated::IStatusBar* statusBar) { if (statusBar != NULL) { - Vector p = dynamic_cast(widget).GetSlice().MapSliceToWorldCoordinates(x, y); + Vector p = dynamic_cast(widget).GetSlice().MapSliceToWorldCoordinates(x, y); char buf[64]; sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", @@ -84,10 +84,10 @@ } } - virtual void MouseWheel(WorldSceneWidget& widget, + virtual void MouseWheel(Deprecated::WorldSceneWidget& widget, MouseWheelDirection direction, KeyboardModifiers modifiers, - IStatusBar* statusBar) + Deprecated::IStatusBar* statusBar) { int scale = (modifiers & KeyboardModifiers_Control ? 10 : 1); @@ -106,11 +106,11 @@ } } - virtual void KeyPressed(WorldSceneWidget& widget, + virtual void KeyPressed(Deprecated::WorldSceneWidget& widget, KeyboardKeys key, char keyChar, KeyboardModifiers modifiers, - IStatusBar* statusBar) + Deprecated::IStatusBar* statusBar) { switch (keyChar) { @@ -149,9 +149,9 @@ } - SliceViewerWidget& GetMainWidget() + Deprecated::SliceViewerWidget& GetMainWidget() { - return *dynamic_cast(mainWidget_); + return *dynamic_cast(mainWidget_); } @@ -184,7 +184,7 @@ } - void OnMainWidgetGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message) + void OnMainWidgetGeometryReady(const Deprecated::IVolumeSlicer::GeometryReadyMessage& message) { // Once the geometry of the series is downloaded from Orthanc, // display its middle slice, and adapt the viewport to fit this @@ -198,7 +198,7 @@ } std::auto_ptr mainWidgetInteractor_; - const DicomSeriesVolumeSlicer* source_; + const Deprecated::DicomSeriesVolumeSlicer* source_; unsigned int slice_; public: @@ -225,7 +225,7 @@ } virtual void Initialize(StoneApplicationContext* context, - IStatusBar& statusBar, + Deprecated::IStatusBar& statusBar, const boost::program_options::variables_map& parameters) { using namespace OrthancStone; @@ -243,15 +243,15 @@ std::string instance = parameters["instance"].as(); int frame = parameters["frame"].as(); - mainWidget_ = new SliceViewerWidget(GetBroker(), "main-widget"); + mainWidget_ = new Deprecated::SliceViewerWidget(GetBroker(), "main-widget"); - std::auto_ptr layer(new DicomSeriesVolumeSlicer(GetBroker(), context->GetOrthancApiClient())); + std::auto_ptr layer(new Deprecated::DicomSeriesVolumeSlicer(GetBroker(), context->GetOrthancApiClient())); source_ = layer.get(); layer->LoadFrame(instance, frame); - layer->RegisterObserverCallback(new Callable(*this, &SingleFrameApplication::OnMainWidgetGeometryReady)); + layer->RegisterObserverCallback(new Callable(*this, &SingleFrameApplication::OnMainWidgetGeometryReady)); GetMainWidget().AddLayer(layer.release()); - RenderStyle s; + Deprecated::RenderStyle s; if (parameters["smooth"].as()) { diff -r a386bbc955dc -r 92c400a09f1b Applications/Samples/SingleFrameEditorApplication.h --- a/Applications/Samples/SingleFrameEditorApplication.h Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Samples/SingleFrameEditorApplication.h Wed May 22 16:13:46 2019 +0200 @@ -52,7 +52,7 @@ namespace Samples { class RadiographyEditorInteractor : - public IWorldSceneInteractor, + public Deprecated::IWorldSceneInteractor, public IObserver { private: @@ -97,16 +97,16 @@ { maskLayer_ = maskLayer; } - virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& worldWidget, - const ViewportGeometry& view, - MouseButton button, - KeyboardModifiers modifiers, - int viewportX, - int viewportY, - double x, - double y, - IStatusBar* statusBar, - const std::vector& displayTouches) + virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& worldWidget, + const Deprecated::ViewportGeometry& view, + MouseButton button, + KeyboardModifiers modifiers, + int viewportX, + int viewportY, + double x, + double y, + Deprecated::IStatusBar* statusBar, + const std::vector& displayTouches) { RadiographyWidget& widget = dynamic_cast(worldWidget); @@ -226,11 +226,11 @@ } virtual void MouseOver(CairoContext& context, - WorldSceneWidget& worldWidget, - const ViewportGeometry& view, + Deprecated::WorldSceneWidget& worldWidget, + const Deprecated::ViewportGeometry& view, double x, double y, - IStatusBar* statusBar) + Deprecated::IStatusBar* statusBar) { RadiographyWidget& widget = dynamic_cast(worldWidget); @@ -270,18 +270,18 @@ } } - virtual void MouseWheel(WorldSceneWidget& widget, + virtual void MouseWheel(Deprecated::WorldSceneWidget& widget, MouseWheelDirection direction, KeyboardModifiers modifiers, - IStatusBar* statusBar) + Deprecated::IStatusBar* statusBar) { } - virtual void KeyPressed(WorldSceneWidget& worldWidget, + virtual void KeyPressed(Deprecated::WorldSceneWidget& worldWidget, KeyboardKeys key, char keyChar, KeyboardModifiers modifiers, - IStatusBar* statusBar) + Deprecated::IStatusBar* statusBar) { RadiographyWidget& widget = dynamic_cast(worldWidget); @@ -455,7 +455,7 @@ } virtual void Initialize(StoneApplicationContext* context, - IStatusBar& statusBar, + Deprecated::IStatusBar& statusBar, const boost::program_options::variables_map& parameters) { using namespace OrthancStone; @@ -485,7 +485,7 @@ } std::string instance = parameters["instance"].as(); - int frame = parameters["frame"].as(); + //int frame = parameters["frame"].as(); fontRegistry_.AddFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16); diff -r a386bbc955dc -r 92c400a09f1b Applications/Sdl/SdlCairoSurface.cpp --- a/Applications/Sdl/SdlCairoSurface.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Sdl/SdlCairoSurface.cpp Wed May 22 16:13:46 2019 +0200 @@ -74,7 +74,7 @@ } - void SdlCairoSurface::Render(IViewport& viewport) + void SdlCairoSurface::Render(Deprecated::IViewport& viewport) { if (cairoSurface_.get() == NULL) { diff -r a386bbc955dc -r 92c400a09f1b Applications/Sdl/SdlCairoSurface.h --- a/Applications/Sdl/SdlCairoSurface.h Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Sdl/SdlCairoSurface.h Wed May 22 16:13:46 2019 +0200 @@ -25,7 +25,7 @@ #include "SdlWindow.h" #include "../../Framework/Viewport/CairoSurface.h" -#include "../../Framework/Viewport/IViewport.h" +#include "../../Framework/Deprecated/Viewport/IViewport.h" #include @@ -46,7 +46,7 @@ void SetSize(unsigned int width, unsigned int height); - void Render(IViewport& viewport); + void Render(Deprecated::IViewport& viewport); }; } diff -r a386bbc955dc -r 92c400a09f1b Applications/Sdl/SdlEngine.cpp --- a/Applications/Sdl/SdlEngine.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Sdl/SdlEngine.cpp Wed May 22 16:13:46 2019 +0200 @@ -146,15 +146,15 @@ switch (event.button.button) { case SDL_BUTTON_LEFT: - locker.GetCentralViewport().MouseDown(MouseButton_Left, event.button.x, event.button.y, modifiers, std::vector()); + locker.GetCentralViewport().MouseDown(MouseButton_Left, event.button.x, event.button.y, modifiers, std::vector()); break; case SDL_BUTTON_RIGHT: - locker.GetCentralViewport().MouseDown(MouseButton_Right, event.button.x, event.button.y, modifiers, std::vector()); + locker.GetCentralViewport().MouseDown(MouseButton_Right, event.button.x, event.button.y, modifiers, std::vector()); break; case SDL_BUTTON_MIDDLE: - locker.GetCentralViewport().MouseDown(MouseButton_Middle, event.button.x, event.button.y, modifiers, std::vector()); + locker.GetCentralViewport().MouseDown(MouseButton_Middle, event.button.x, event.button.y, modifiers, std::vector()); break; default: @@ -163,7 +163,7 @@ } else if (event.type == SDL_MOUSEMOTION) { - locker.GetCentralViewport().MouseMove(event.button.x, event.button.y, std::vector()); + locker.GetCentralViewport().MouseMove(event.button.x, event.button.y, std::vector()); } else if (event.type == SDL_MOUSEBUTTONUP) { diff -r a386bbc955dc -r 92c400a09f1b Applications/Sdl/SdlEngine.h --- a/Applications/Sdl/SdlEngine.h Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Sdl/SdlEngine.h Wed May 22 16:13:46 2019 +0200 @@ -49,7 +49,7 @@ NativeStoneApplicationContext& context, MessageBroker& broker); - void OnViewportChanged(const IViewport::ViewportChangedMessage& message) + void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message) { viewportChanged_ = true; } diff -r a386bbc955dc -r 92c400a09f1b Applications/Sdl/SdlStoneApplicationRunner.cpp --- a/Applications/Sdl/SdlStoneApplicationRunner.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Applications/Sdl/SdlStoneApplicationRunner.cpp Wed May 22 16:13:46 2019 +0200 @@ -110,7 +110,7 @@ NativeStoneApplicationContext::GlobalMutexLocker locker(context); locker.GetCentralViewport().RegisterObserverCallback( - new Callable + new Callable (sdl, &SdlEngine::OnViewportChanged)); //context.GetCentralViewport().Register(sdl); // (*) diff -r a386bbc955dc -r 92c400a09f1b Applications/StoneApplicationContext.cpp --- a/Applications/StoneApplicationContext.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Applications/StoneApplicationContext.cpp Wed May 22 16:13:46 2019 +0200 @@ -32,11 +32,11 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } - orthanc_.reset(new OrthancApiClient(broker_, *webService_, orthancBaseUrl_)); + orthanc_.reset(new Deprecated::OrthancApiClient(broker_, *webService_, orthancBaseUrl_)); } - IWebService& StoneApplicationContext::GetWebService() + Deprecated::IWebService& StoneApplicationContext::GetWebService() { if (webService_ == NULL) { @@ -47,7 +47,7 @@ } - OrthancApiClient& StoneApplicationContext::GetOrthancApiClient() + Deprecated::OrthancApiClient& StoneApplicationContext::GetOrthancApiClient() { if (orthanc_.get() == NULL) { @@ -58,7 +58,7 @@ } - void StoneApplicationContext::SetWebService(IWebService& webService) + void StoneApplicationContext::SetWebService(Deprecated::IWebService& webService) { webService_ = &webService; InitializeOrthanc(); diff -r a386bbc955dc -r 92c400a09f1b Applications/StoneApplicationContext.h --- a/Applications/StoneApplicationContext.h Wed May 22 16:01:34 2019 +0200 +++ b/Applications/StoneApplicationContext.h Wed May 22 16:13:46 2019 +0200 @@ -21,10 +21,10 @@ #pragma once -#include "../Framework/Toolbox/IWebService.h" -#include "../Framework/Toolbox/IDelayedCallExecutor.h" -#include "../Framework/Toolbox/OrthancApiClient.h" -#include "../Framework/Viewport/WidgetViewport.h" +#include "../Framework/Deprecated/Toolbox/IWebService.h" +#include "../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h" +#include "../Framework/Deprecated/Toolbox/OrthancApiClient.h" +#include "../Framework/Deprecated/Viewport/WidgetViewport.h" #ifdef _MSC_VER @@ -60,9 +60,9 @@ { private: MessageBroker& broker_; - IWebService* webService_; - IDelayedCallExecutor* delayedCallExecutor_; - std::auto_ptr orthanc_; + Deprecated::IWebService* webService_; + Deprecated::IDelayedCallExecutor* delayedCallExecutor_; + std::auto_ptr orthanc_; std::string orthancBaseUrl_; void InitializeOrthanc(); @@ -89,20 +89,20 @@ return webService_ != NULL; } - IWebService& GetWebService(); + Deprecated::IWebService& GetWebService(); - OrthancApiClient& GetOrthancApiClient(); + Deprecated::OrthancApiClient& GetOrthancApiClient(); - void SetWebService(IWebService& webService); + void SetWebService(Deprecated::IWebService& webService); void SetOrthancBaseUrl(const std::string& baseUrl); - void SetDelayedCallExecutor(IDelayedCallExecutor& delayedCallExecutor) + void SetDelayedCallExecutor(Deprecated::IDelayedCallExecutor& delayedCallExecutor) { delayedCallExecutor_ = &delayedCallExecutor; } - IDelayedCallExecutor& GetDelayedCallExecutor() + Deprecated::IDelayedCallExecutor& GetDelayedCallExecutor() { return *delayedCallExecutor_; } diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/CircleMeasureTracker.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/CircleMeasureTracker.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,106 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "CircleMeasureTracker.h" + +#include +#include + +namespace Deprecated +{ + CircleMeasureTracker::CircleMeasureTracker(IStatusBar* statusBar, + const OrthancStone::CoordinateSystem3D& slice, + double x, + double y, + uint8_t red, + uint8_t green, + uint8_t blue, + const Orthanc::Font& font) : + statusBar_(statusBar), + slice_(slice), + x1_(x), + y1_(y), + x2_(x), + y2_(y), + font_(font) + { + color_[0] = red; + color_[1] = green; + color_[2] = blue; + } + + + void CircleMeasureTracker::Render(OrthancStone::CairoContext& context, + double zoom) + { + double x = (x1_ + x2_) / 2.0; + double y = (y1_ + y2_) / 2.0; + + OrthancStone::Vector tmp; + OrthancStone::LinearAlgebra::AssignVector(tmp, x2_ - x1_, y2_ - y1_); + double r = boost::numeric::ublas::norm_2(tmp) / 2.0; + + context.SetSourceColor(color_[0], color_[1], color_[2]); + + cairo_t* cr = context.GetObject(); + cairo_save(cr); + cairo_set_line_width(cr, 2.0 / zoom); + cairo_translate(cr, x, y); + cairo_arc(cr, 0, 0, r, 0, 2.0 * boost::math::constants::pi()); + cairo_stroke_preserve(cr); + cairo_stroke(cr); + cairo_restore(cr); + + context.DrawText(font_, FormatRadius(), x, y, OrthancStone::BitmapAnchor_Center); + } + + + double CircleMeasureTracker::GetRadius() const // In millimeters + { + OrthancStone::Vector a = slice_.MapSliceToWorldCoordinates(x1_, y1_); + OrthancStone::Vector b = slice_.MapSliceToWorldCoordinates(x2_, y2_); + return boost::numeric::ublas::norm_2(b - a) / 2.0; + } + + + std::string CircleMeasureTracker::FormatRadius() const + { + char buf[64]; + sprintf(buf, "%0.01f cm", GetRadius() / 10.0); + return buf; + } + + void CircleMeasureTracker::MouseMove(int displayX, + int displayY, + double x, + double y, + const std::vector& displayTouches, + const std::vector& sceneTouches) + { + x2_ = x; + y2_ = y; + + if (statusBar_ != NULL) + { + statusBar_->SetMessage("Circle radius: " + FormatRadius()); + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/CircleMeasureTracker.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/CircleMeasureTracker.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,78 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../Widgets/IWorldSceneMouseTracker.h" +#include "../Viewport/IStatusBar.h" +#include "../../Toolbox/CoordinateSystem3D.h" + +#include + +namespace Deprecated +{ + class CircleMeasureTracker : public IWorldSceneMouseTracker + { + private: + IStatusBar* statusBar_; + OrthancStone::CoordinateSystem3D slice_; + double x1_; + double y1_; + double x2_; + double y2_; + uint8_t color_[3]; + const Orthanc::Font& font_; + + public: + CircleMeasureTracker(IStatusBar* statusBar, + const OrthancStone::CoordinateSystem3D& slice, + double x, + double y, + uint8_t red, + uint8_t green, + uint8_t blue, + const Orthanc::Font& font); + + virtual bool HasRender() const + { + return true; + } + + virtual void Render(OrthancStone::CairoContext& context, + double zoom); + + double GetRadius() const; // In millimeters + + std::string FormatRadius() const; + + virtual void MouseUp() + { + // Possibly create a new landmark "volume" with the circle in subclasses + } + + virtual void MouseMove(int displayX, + int displayY, + double x, + double y, + const std::vector& displayTouches, + const std::vector& sceneTouches); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/ColorFrameRenderer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/ColorFrameRenderer.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,62 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "ColorFrameRenderer.h" + +#include +#include +#include + +namespace Deprecated +{ + OrthancStone::CairoSurface* ColorFrameRenderer::GenerateDisplay(const RenderStyle& style) + { + std::auto_ptr display + (new OrthancStone::CairoSurface(frame_->GetWidth(), frame_->GetHeight(), false /* no alpha */)); + + Orthanc::ImageAccessor target; + display->GetWriteableAccessor(target); + + Orthanc::ImageProcessing::Convert(target, *frame_); + + return display.release(); + } + + + ColorFrameRenderer::ColorFrameRenderer(const Orthanc::ImageAccessor& frame, + const OrthancStone::CoordinateSystem3D& framePlane, + double pixelSpacingX, + double pixelSpacingY, + bool isFullQuality) : + FrameRenderer(framePlane, pixelSpacingX, pixelSpacingY, isFullQuality), + frame_(Orthanc::Image::Clone(frame)) + { + if (frame_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + if (frame_->GetFormat() != Orthanc::PixelFormat_RGB24) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/ColorFrameRenderer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/ColorFrameRenderer.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,43 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "FrameRenderer.h" + +namespace Deprecated +{ + class ColorFrameRenderer : public FrameRenderer + { + private: + std::auto_ptr frame_; // In RGB24 + + protected: + virtual OrthancStone::CairoSurface* GenerateDisplay(const RenderStyle& style); + + public: + ColorFrameRenderer(const Orthanc::ImageAccessor& frame, + const OrthancStone::CoordinateSystem3D& framePlane, + double pixelSpacingX, + double pixelSpacingY, + bool isFullQuality); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/DicomSeriesVolumeSlicer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/DicomSeriesVolumeSlicer.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,162 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "DicomSeriesVolumeSlicer.h" + +#include "FrameRenderer.h" +#include "../Toolbox/DicomFrameConverter.h" + +#include +#include + +#include + +namespace Deprecated +{ + + void DicomSeriesVolumeSlicer::OnSliceGeometryReady(const OrthancSlicesLoader::SliceGeometryReadyMessage& message) + { + if (message.GetOrigin().GetSlicesCount() > 0) + { + BroadcastMessage(IVolumeSlicer::GeometryReadyMessage(*this)); + } + else + { + BroadcastMessage(IVolumeSlicer::GeometryErrorMessage(*this)); + } + } + + void DicomSeriesVolumeSlicer::OnSliceGeometryError(const OrthancSlicesLoader::SliceGeometryErrorMessage& message) + { + BroadcastMessage(IVolumeSlicer::GeometryErrorMessage(*this)); + } + + + class DicomSeriesVolumeSlicer::RendererFactory : public LayerReadyMessage::IRendererFactory + { + private: + const OrthancSlicesLoader::SliceImageReadyMessage& message_; + + public: + RendererFactory(const OrthancSlicesLoader::SliceImageReadyMessage& message) : + message_(message) + { + } + + virtual ILayerRenderer* CreateRenderer() const + { + bool isFull = (message_.GetEffectiveQuality() == SliceImageQuality_FullPng || + message_.GetEffectiveQuality() == SliceImageQuality_FullPam); + + return FrameRenderer::CreateRenderer(message_.GetImage(), message_.GetSlice(), isFull); + } + }; + + void DicomSeriesVolumeSlicer::OnSliceImageReady(const OrthancSlicesLoader::SliceImageReadyMessage& message) + { + // first notify that the pixel data of the frame is ready (targeted to, i.e: an image cache) + BroadcastMessage(FrameReadyMessage(*this, message.GetImage(), + message.GetEffectiveQuality(), message.GetSlice())); + + // then notify that the layer is ready for rendering + RendererFactory factory(message); + BroadcastMessage(IVolumeSlicer::LayerReadyMessage(*this, factory, message.GetSlice().GetGeometry())); + } + + void DicomSeriesVolumeSlicer::OnSliceImageError(const OrthancSlicesLoader::SliceImageErrorMessage& message) + { + BroadcastMessage(IVolumeSlicer::LayerErrorMessage(*this, message.GetSlice().GetGeometry())); + } + + + DicomSeriesVolumeSlicer::DicomSeriesVolumeSlicer(OrthancStone::MessageBroker& broker, + OrthancApiClient& orthanc) : + IVolumeSlicer(broker), + IObserver(broker), + loader_(broker, orthanc), + quality_(SliceImageQuality_FullPng) + { + loader_.RegisterObserverCallback( + new OrthancStone::Callable + (*this, &DicomSeriesVolumeSlicer::OnSliceGeometryReady)); + + loader_.RegisterObserverCallback( + new OrthancStone::Callable + (*this, &DicomSeriesVolumeSlicer::OnSliceGeometryError)); + + loader_.RegisterObserverCallback( + new OrthancStone::Callable + (*this, &DicomSeriesVolumeSlicer::OnSliceImageReady)); + + loader_.RegisterObserverCallback( + new OrthancStone::Callable + (*this, &DicomSeriesVolumeSlicer::OnSliceImageError)); + } + + + void DicomSeriesVolumeSlicer::LoadSeries(const std::string& seriesId) + { + loader_.ScheduleLoadSeries(seriesId); + } + + + void DicomSeriesVolumeSlicer::LoadInstance(const std::string& instanceId) + { + loader_.ScheduleLoadInstance(instanceId); + } + + + void DicomSeriesVolumeSlicer::LoadFrame(const std::string& instanceId, + unsigned int frame) + { + loader_.ScheduleLoadFrame(instanceId, frame); + } + + + bool DicomSeriesVolumeSlicer::GetExtent(std::vector& points, + const OrthancStone::CoordinateSystem3D& viewportSlice) + { + size_t index; + + if (loader_.IsGeometryReady() && + loader_.LookupSlice(index, viewportSlice)) + { + loader_.GetSlice(index).GetExtent(points); + return true; + } + else + { + return false; + } + } + + + void DicomSeriesVolumeSlicer::ScheduleLayerCreation(const OrthancStone::CoordinateSystem3D& viewportSlice) + { + size_t index; + + if (loader_.IsGeometryReady() && + loader_.LookupSlice(index, viewportSlice)) + { + loader_.ScheduleLoadSliceImage(index, quality_); + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/DicomSeriesVolumeSlicer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/DicomSeriesVolumeSlicer.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,127 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IVolumeSlicer.h" +#include "../Toolbox/IWebService.h" +#include "../Toolbox/OrthancSlicesLoader.h" +#include "../Toolbox/OrthancApiClient.h" + +namespace Deprecated +{ + // this class is in charge of loading a Frame. + // once it's been loaded (first the geometry and then the image), + // messages are sent to observers so they can use it + class DicomSeriesVolumeSlicer : + public IVolumeSlicer, + public OrthancStone::IObserver + //private OrthancSlicesLoader::ISliceLoaderObserver + { + public: + // TODO: Add "frame" and "instanceId" + class FrameReadyMessage : public OrthancStone::OriginMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + const Orthanc::ImageAccessor& frame_; + SliceImageQuality imageQuality_; + const Slice& slice_; + + public: + FrameReadyMessage(DicomSeriesVolumeSlicer& origin, + const Orthanc::ImageAccessor& frame, + SliceImageQuality imageQuality, + const Slice& slice) : + OriginMessage(origin), + frame_(frame), + imageQuality_(imageQuality), + slice_(slice) + { + } + + const Orthanc::ImageAccessor& GetFrame() const + { + return frame_; + } + + SliceImageQuality GetImageQuality() const + { + return imageQuality_; + } + + const Slice& GetSlice() const + { + return slice_; + } + }; + + + private: + class RendererFactory; + + OrthancSlicesLoader loader_; + SliceImageQuality quality_; + + public: + DicomSeriesVolumeSlicer(OrthancStone::MessageBroker& broker, + OrthancApiClient& orthanc); + + void LoadSeries(const std::string& seriesId); + + void LoadInstance(const std::string& instanceId); + + void LoadFrame(const std::string& instanceId, + unsigned int frame); + + void SetImageQuality(SliceImageQuality quality) + { + quality_ = quality; + } + + SliceImageQuality GetImageQuality() const + { + return quality_; + } + + size_t GetSlicesCount() const + { + return loader_.GetSlicesCount(); + } + + const Slice& GetSlice(size_t slice) const + { + return loader_.GetSlice(slice); + } + + virtual bool GetExtent(std::vector& points, + const OrthancStone::CoordinateSystem3D& viewportSlice); + + virtual void ScheduleLayerCreation(const OrthancStone::CoordinateSystem3D& viewportSlice); + +protected: + void OnSliceGeometryReady(const OrthancSlicesLoader::SliceGeometryReadyMessage& message); + void OnSliceGeometryError(const OrthancSlicesLoader::SliceGeometryErrorMessage& message); + void OnSliceImageReady(const OrthancSlicesLoader::SliceImageReadyMessage& message); + void OnSliceImageError(const OrthancSlicesLoader::SliceImageErrorMessage& message); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/DicomStructureSetSlicer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/DicomStructureSetSlicer.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,170 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "DicomStructureSetSlicer.h" + +namespace Deprecated +{ + class DicomStructureSetSlicer::Renderer : public ILayerRenderer + { + private: + class Structure + { + private: + bool visible_; + uint8_t red_; + uint8_t green_; + uint8_t blue_; + std::string name_; + std::vector< std::vector > polygons_; + + public: + Structure(OrthancStone::DicomStructureSet& structureSet, + const OrthancStone::CoordinateSystem3D& plane, + size_t index) : + name_(structureSet.GetStructureName(index)) + { + structureSet.GetStructureColor(red_, green_, blue_, index); + visible_ = structureSet.ProjectStructure(polygons_, index, plane); + } + + void Render(OrthancStone::CairoContext& context) + { + if (visible_) + { + cairo_t* cr = context.GetObject(); + + context.SetSourceColor(red_, green_, blue_); + + for (size_t i = 0; i < polygons_.size(); i++) + { + cairo_move_to(cr, polygons_[i][0].first, polygons_[i][0].second); + + for (size_t j = 1; j < polygons_[i].size(); j++) + { + cairo_line_to(cr, polygons_[i][j].first, polygons_[i][j].second); + } + + cairo_line_to(cr, polygons_[i][0].first, polygons_[i][0].second); + cairo_stroke(cr); + } + } + } + }; + + typedef std::list Structures; + + OrthancStone::CoordinateSystem3D plane_; + Structures structures_; + + public: + Renderer(OrthancStone::DicomStructureSet& structureSet, + const OrthancStone::CoordinateSystem3D& plane) : + plane_(plane) + { + for (size_t k = 0; k < structureSet.GetStructureCount(); k++) + { + structures_.push_back(new Structure(structureSet, plane, k)); + } + } + + virtual ~Renderer() + { + for (Structures::iterator it = structures_.begin(); + it != structures_.end(); ++it) + { + delete *it; + } + } + + virtual bool RenderLayer(OrthancStone::CairoContext& context, + const ViewportGeometry& view) + { + cairo_set_line_width(context.GetObject(), 2.0f / view.GetZoom()); + + for (Structures::const_iterator it = structures_.begin(); + it != structures_.end(); ++it) + { + assert(*it != NULL); + (*it)->Render(context); + } + + return true; + } + + virtual const OrthancStone::CoordinateSystem3D& GetLayerPlane() + { + return plane_; + } + + virtual void SetLayerStyle(const RenderStyle& style) + { + } + + virtual bool IsFullQuality() + { + return true; + } + }; + + + class DicomStructureSetSlicer::RendererFactory : public LayerReadyMessage::IRendererFactory + { + private: + OrthancStone::DicomStructureSet& structureSet_; + const OrthancStone::CoordinateSystem3D& plane_; + + public: + RendererFactory(OrthancStone::DicomStructureSet& structureSet, + const OrthancStone::CoordinateSystem3D& plane) : + structureSet_(structureSet), + plane_(plane) + { + } + + virtual ILayerRenderer* CreateRenderer() const + { + return new Renderer(structureSet_, plane_); + } + }; + + + DicomStructureSetSlicer::DicomStructureSetSlicer(OrthancStone::MessageBroker& broker, + StructureSetLoader& loader) : + IVolumeSlicer(broker), + IObserver(broker), + loader_(loader) + { + loader_.RegisterObserverCallback( + new OrthancStone::Callable + (*this, &DicomStructureSetSlicer::OnStructureSetLoaded)); + } + + + void DicomStructureSetSlicer::ScheduleLayerCreation(const OrthancStone::CoordinateSystem3D& viewportPlane) + { + if (loader_.HasStructureSet()) + { + RendererFactory factory(loader_.GetStructureSet(), viewportPlane); + BroadcastMessage(IVolumeSlicer::LayerReadyMessage(*this, factory, viewportPlane)); + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/DicomStructureSetSlicer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/DicomStructureSetSlicer.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,56 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IVolumeSlicer.h" +#include "../Volumes/StructureSetLoader.h" + +namespace Deprecated +{ + class DicomStructureSetSlicer : + public IVolumeSlicer, + public OrthancStone::IObserver + { + private: + class Renderer; + class RendererFactory; + + StructureSetLoader& loader_; + + void OnStructureSetLoaded(const IVolumeLoader::ContentChangedMessage& message) + { + BroadcastMessage(IVolumeSlicer::ContentChangedMessage(*this)); + } + + public: + DicomStructureSetSlicer(OrthancStone::MessageBroker& broker, + StructureSetLoader& loader); + + virtual bool GetExtent(std::vector& points, + const OrthancStone::CoordinateSystem3D& viewportPlane) + { + return false; + } + + virtual void ScheduleLayerCreation(const OrthancStone::CoordinateSystem3D& viewportPlane); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/FrameRenderer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/FrameRenderer.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,140 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "FrameRenderer.h" + +#include "GrayscaleFrameRenderer.h" +#include "ColorFrameRenderer.h" + +#include + +namespace Deprecated +{ + FrameRenderer::FrameRenderer(const OrthancStone::CoordinateSystem3D& framePlane, + double pixelSpacingX, + double pixelSpacingY, + bool isFullQuality) : + framePlane_(framePlane), + pixelSpacingX_(pixelSpacingX), + pixelSpacingY_(pixelSpacingY), + isFullQuality_(isFullQuality) + { + } + + + bool FrameRenderer::RenderLayer(OrthancStone::CairoContext& context, + const ViewportGeometry& view) + { + if (!style_.visible_) + { + return true; + } + + if (display_.get() == NULL) + { + display_.reset(GenerateDisplay(style_)); + } + + assert(display_.get() != NULL); + + cairo_t *cr = context.GetObject(); + + cairo_save(cr); + + cairo_matrix_t transform; + cairo_matrix_init_identity(&transform); + cairo_matrix_scale(&transform, pixelSpacingX_, pixelSpacingY_); + cairo_matrix_translate(&transform, -0.5, -0.5); + cairo_transform(cr, &transform); + + //cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_set_source_surface(cr, display_->GetObject(), 0, 0); + + switch (style_.interpolation_) + { + case OrthancStone::ImageInterpolation_Nearest: + cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST); + break; + + case OrthancStone::ImageInterpolation_Bilinear: + cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_BILINEAR); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + cairo_paint_with_alpha(cr, style_.alpha_); + + if (style_.drawGrid_) + { + context.SetSourceColor(style_.drawColor_); + cairo_set_line_width(cr, 0.5 / view.GetZoom()); + + for (unsigned int x = 0; x <= display_->GetWidth(); x++) + { + cairo_move_to(cr, x, 0); + cairo_line_to(cr, x, display_->GetHeight()); + } + + for (unsigned int y = 0; y <= display_->GetHeight(); y++) + { + cairo_move_to(cr, 0, y); + cairo_line_to(cr, display_->GetWidth(), y); + } + + cairo_stroke(cr); + } + + cairo_restore(cr); + + return true; + } + + + void FrameRenderer::SetLayerStyle(const RenderStyle& style) + { + style_ = style; + display_.reset(NULL); + } + + + ILayerRenderer* FrameRenderer::CreateRenderer(const Orthanc::ImageAccessor& frame, + const Deprecated::Slice& framePlane, + bool isFullQuality) + { + if (frame.GetFormat() == Orthanc::PixelFormat_RGB24) + { + return new ColorFrameRenderer(frame, + framePlane.GetGeometry(), + framePlane.GetPixelSpacingX(), + framePlane.GetPixelSpacingY(), isFullQuality); + } + else + { + return new GrayscaleFrameRenderer(frame, + framePlane.GetConverter(), + framePlane.GetGeometry(), + framePlane.GetPixelSpacingX(), + framePlane.GetPixelSpacingY(), isFullQuality); + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/FrameRenderer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/FrameRenderer.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,69 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "ILayerRenderer.h" + +#include "../Toolbox/Slice.h" + +namespace Deprecated +{ + class FrameRenderer : public ILayerRenderer + { + private: + OrthancStone::CoordinateSystem3D framePlane_; + double pixelSpacingX_; + double pixelSpacingY_; + RenderStyle style_; + bool isFullQuality_; + std::auto_ptr display_; + + protected: + virtual OrthancStone::CairoSurface* GenerateDisplay(const RenderStyle& style) = 0; + + public: + FrameRenderer(const OrthancStone::CoordinateSystem3D& framePlane, + double pixelSpacingX, + double pixelSpacingY, + bool isFullQuality); + + virtual bool RenderLayer(OrthancStone::CairoContext& context, + const ViewportGeometry& view); + + virtual const OrthancStone::CoordinateSystem3D& GetLayerPlane() + { + return framePlane_; + } + + virtual void SetLayerStyle(const RenderStyle& style); + + virtual bool IsFullQuality() + { + return isFullQuality_; + } + + // TODO: Avoid cloning the "frame" + static ILayerRenderer* CreateRenderer(const Orthanc::ImageAccessor& frame, + const Deprecated::Slice& framePlane, + bool isFullQuality); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/GrayscaleFrameRenderer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/GrayscaleFrameRenderer.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,141 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "GrayscaleFrameRenderer.h" + +#include +#include + +namespace Deprecated +{ + OrthancStone::CairoSurface* GrayscaleFrameRenderer::GenerateDisplay(const RenderStyle& style) + { + assert(frame_->GetFormat() == Orthanc::PixelFormat_Float32); + + std::auto_ptr result; + + float windowCenter, windowWidth; + style.ComputeWindowing(windowCenter, windowWidth, + defaultWindowCenter_, defaultWindowWidth_); + + float x0 = windowCenter - windowWidth / 2.0f; + float x1 = windowCenter + windowWidth / 2.0f; + + //LOG(INFO) << "Window: " << x0 << " => " << x1; + + result.reset(new OrthancStone::CairoSurface(frame_->GetWidth(), frame_->GetHeight(), false /* no alpha */)); + + const uint8_t* lut = NULL; + if (style.applyLut_) + { + if (Orthanc::EmbeddedResources::GetFileResourceSize(style.lut_) != 3 * 256) + { + // Invalid colormap + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + lut = reinterpret_cast(Orthanc::EmbeddedResources::GetFileResourceBuffer(style.lut_)); + } + + Orthanc::ImageAccessor target; + result->GetWriteableAccessor(target); + + const unsigned int width = target.GetWidth(); + const unsigned int height = target.GetHeight(); + + for (unsigned int y = 0; y < height; y++) + { + const float* p = reinterpret_cast(frame_->GetConstRow(y)); + uint8_t* q = reinterpret_cast(target.GetRow(y)); + + for (unsigned int x = 0; x < width; x++, p++, q += 4) + { + uint8_t v = 0; + if (windowWidth >= 0.001f) // Avoid division by zero + { + if (*p >= x1) + { + v = 255; + } + else if (*p <= x0) + { + v = 0; + } + else + { + // https://en.wikipedia.org/wiki/Linear_interpolation + v = static_cast(255.0f * (*p - x0) / (x1 - x0)); + } + + if (style.reverse_ ^ (photometric_ == Orthanc::PhotometricInterpretation_Monochrome1)) + { + v = 255 - v; + } + } + + if (style.applyLut_) + { + assert(lut != NULL); + q[3] = 255; + q[2] = lut[3 * v]; + q[1] = lut[3 * v + 1]; + q[0] = lut[3 * v + 2]; + } + else + { + q[3] = 255; + q[2] = v; + q[1] = v; + q[0] = v; + } + } + } + + return result.release(); + } + + + GrayscaleFrameRenderer::GrayscaleFrameRenderer(const Orthanc::ImageAccessor& frame, + const Deprecated::DicomFrameConverter& converter, + const OrthancStone::CoordinateSystem3D& framePlane, + double pixelSpacingX, + double pixelSpacingY, + bool isFullQuality) : + FrameRenderer(framePlane, pixelSpacingX, pixelSpacingY, isFullQuality), + frame_(Orthanc::Image::Clone(frame)), + defaultWindowCenter_(static_cast(converter.GetDefaultWindowCenter())), + defaultWindowWidth_(static_cast(converter.GetDefaultWindowWidth())), + photometric_(converter.GetPhotometricInterpretation()) + { + if (frame_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + converter.ConvertFrameInplace(frame_); + assert(frame_.get() != NULL); + + if (frame_->GetFormat() != Orthanc::PixelFormat_Float32) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/GrayscaleFrameRenderer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/GrayscaleFrameRenderer.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,48 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "FrameRenderer.h" +#include "../Toolbox/DicomFrameConverter.h" + +namespace Deprecated +{ + class GrayscaleFrameRenderer : public FrameRenderer + { + private: + std::auto_ptr frame_; // In Float32 + float defaultWindowCenter_; + float defaultWindowWidth_; + Orthanc::PhotometricInterpretation photometric_; + + protected: + virtual OrthancStone::CairoSurface* GenerateDisplay(const RenderStyle& style); + + public: + GrayscaleFrameRenderer(const Orthanc::ImageAccessor& frame, + const Deprecated::DicomFrameConverter& converter, + const OrthancStone::CoordinateSystem3D& framePlane, + double pixelSpacingX, + double pixelSpacingY, + bool isFullQuality); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/ILayerRenderer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/ILayerRenderer.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,47 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../../Viewport/CairoContext.h" +#include "../../Toolbox/CoordinateSystem3D.h" +#include "../Toolbox/ViewportGeometry.h" +#include "RenderStyle.h" + +namespace Deprecated +{ + class ILayerRenderer : public boost::noncopyable + { + public: + virtual ~ILayerRenderer() + { + } + + virtual bool RenderLayer(OrthancStone::CairoContext& context, + const ViewportGeometry& view) = 0; + + virtual void SetLayerStyle(const RenderStyle& style) = 0; + + virtual const OrthancStone::CoordinateSystem3D& GetLayerPlane() = 0; + + virtual bool IsFullQuality() = 0; + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/IVolumeSlicer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/IVolumeSlicer.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,139 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "ILayerRenderer.h" +#include "../Toolbox/Slice.h" +#include "../../Messages/IObservable.h" +#include "../../Messages/IMessage.h" +#include "Core/Images/Image.h" +#include + +namespace Deprecated +{ + class IVolumeSlicer : public OrthancStone::IObservable + { + public: + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryReadyMessage, IVolumeSlicer); + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryErrorMessage, IVolumeSlicer); + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentChangedMessage, IVolumeSlicer); + + class SliceContentChangedMessage : public OrthancStone::OriginMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + const Deprecated::Slice& slice_; + + public: + SliceContentChangedMessage(IVolumeSlicer& origin, + const Deprecated::Slice& slice) : + OriginMessage(origin), + slice_(slice) + { + } + + const Deprecated::Slice& GetSlice() const + { + return slice_; + } + }; + + + class LayerReadyMessage : public OrthancStone::OriginMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + public: + class IRendererFactory : public boost::noncopyable + { + public: + virtual ~IRendererFactory() + { + } + + virtual ILayerRenderer* CreateRenderer() const = 0; + }; + + private: + const IRendererFactory& factory_; + const OrthancStone::CoordinateSystem3D& slice_; + + public: + LayerReadyMessage(IVolumeSlicer& origin, + const IRendererFactory& rendererFactory, + const OrthancStone::CoordinateSystem3D& slice) : + OriginMessage(origin), + factory_(rendererFactory), + slice_(slice) + { + } + + ILayerRenderer* CreateRenderer() const + { + return factory_.CreateRenderer(); + } + + const OrthancStone::CoordinateSystem3D& GetSlice() const + { + return slice_; + } + }; + + + class LayerErrorMessage : public OrthancStone::OriginMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + const OrthancStone::CoordinateSystem3D& slice_; + + public: + LayerErrorMessage(IVolumeSlicer& origin, + const OrthancStone::CoordinateSystem3D& slice) : + OriginMessage(origin), + slice_(slice) + { + } + + const OrthancStone::CoordinateSystem3D& GetSlice() const + { + return slice_; + } + }; + + + IVolumeSlicer(OrthancStone::MessageBroker& broker) : + IObservable(broker) + { + } + + virtual ~IVolumeSlicer() + { + } + + virtual bool GetExtent(std::vector& points, + const OrthancStone::CoordinateSystem3D& viewportSlice) = 0; + + virtual void ScheduleLayerCreation(const OrthancStone::CoordinateSystem3D& viewportSlice) = 0; + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/LineLayerRenderer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/LineLayerRenderer.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,67 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "LineLayerRenderer.h" + +namespace Deprecated +{ + LineLayerRenderer::LineLayerRenderer(double x1, + double y1, + double x2, + double y2, + const OrthancStone::CoordinateSystem3D& plane) : + x1_(x1), + y1_(y1), + x2_(x2), + y2_(y2), + plane_(plane) + { + RenderStyle style; + SetLayerStyle(style); + } + + + bool LineLayerRenderer::RenderLayer(OrthancStone::CairoContext& context, + const ViewportGeometry& view) + { + if (visible_) + { + context.SetSourceColor(color_); + + cairo_t *cr = context.GetObject(); + cairo_set_line_width(cr, 1.0 / view.GetZoom()); + cairo_move_to(cr, x1_, y1_); + cairo_line_to(cr, x2_, y2_); + cairo_stroke(cr); + } + + return true; + } + + + void LineLayerRenderer::SetLayerStyle(const RenderStyle& style) + { + visible_ = style.visible_; + color_[0] = style.drawColor_[0]; + color_[1] = style.drawColor_[1]; + color_[2] = style.drawColor_[2]; + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/LineLayerRenderer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/LineLayerRenderer.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,61 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "ILayerRenderer.h" + +namespace Deprecated +{ + class LineLayerRenderer : public ILayerRenderer + { + private: + double x1_; + double y1_; + double x2_; + double y2_; + OrthancStone::CoordinateSystem3D plane_; + bool visible_; + uint8_t color_[3]; + + public: + LineLayerRenderer(double x1, + double y1, + double x2, + double y2, + const OrthancStone::CoordinateSystem3D& plane); + + virtual bool RenderLayer(OrthancStone::CairoContext& context, + const ViewportGeometry& view); + + virtual void SetLayerStyle(const RenderStyle& style); + + virtual const OrthancStone::CoordinateSystem3D& GetLayerPlane() + { + return plane_; + } + + virtual bool IsFullQuality() + { + return true; + } + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/LineMeasureTracker.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/LineMeasureTracker.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,102 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "LineMeasureTracker.h" + +#include + +namespace Deprecated +{ + LineMeasureTracker::LineMeasureTracker(IStatusBar* statusBar, + const OrthancStone::CoordinateSystem3D& slice, + double x, + double y, + uint8_t red, + uint8_t green, + uint8_t blue, + const Orthanc::Font& font) : + statusBar_(statusBar), + slice_(slice), + x1_(x), + y1_(y), + x2_(x), + y2_(y), + font_(font) + { + color_[0] = red; + color_[1] = green; + color_[2] = blue; + } + + + void LineMeasureTracker::Render(OrthancStone::CairoContext& context, + double zoom) + { + context.SetSourceColor(color_[0], color_[1], color_[2]); + + cairo_t* cr = context.GetObject(); + cairo_set_line_width(cr, 2.0 / zoom); + cairo_move_to(cr, x1_, y1_); + cairo_line_to(cr, x2_, y2_); + cairo_stroke(cr); + + if (y2_ - y1_ < 0) + { + context.DrawText(font_, FormatLength(), x2_, y2_ - 5, OrthancStone::BitmapAnchor_BottomCenter); + } + else + { + context.DrawText(font_, FormatLength(), x2_, y2_ + 5, OrthancStone::BitmapAnchor_TopCenter); + } + } + + + double LineMeasureTracker::GetLength() const // In millimeters + { + OrthancStone::Vector a = slice_.MapSliceToWorldCoordinates(x1_, y1_); + OrthancStone::Vector b = slice_.MapSliceToWorldCoordinates(x2_, y2_); + return boost::numeric::ublas::norm_2(b - a); + } + + + std::string LineMeasureTracker::FormatLength() const + { + char buf[64]; + sprintf(buf, "%0.01f cm", GetLength() / 10.0); + return buf; + } + + void LineMeasureTracker::MouseMove(int displayX, + int displayY, + double x, + double y, + const std::vector& displayTouches, + const std::vector& sceneTouches) + { + x2_ = x; + y2_ = y; + + if (statusBar_ != NULL) + { + statusBar_->SetMessage("Line length: " + FormatLength()); + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/LineMeasureTracker.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/LineMeasureTracker.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,78 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../Widgets/IWorldSceneMouseTracker.h" + +#include "../Viewport/IStatusBar.h" +#include "../../Toolbox/CoordinateSystem3D.h" + +namespace Deprecated +{ + class LineMeasureTracker : public IWorldSceneMouseTracker + { + private: + IStatusBar* statusBar_; + OrthancStone::CoordinateSystem3D slice_; + double x1_; + double y1_; + double x2_; + double y2_; + uint8_t color_[3]; + unsigned int fontSize_; + const Orthanc::Font& font_; + + public: + LineMeasureTracker(IStatusBar* statusBar, + const OrthancStone::CoordinateSystem3D& slice, + double x, + double y, + uint8_t red, + uint8_t green, + uint8_t blue, + const Orthanc::Font& font); + + virtual bool HasRender() const + { + return true; + } + + virtual void Render(OrthancStone::CairoContext& context, + double zoom); + + double GetLength() const; // In millimeters + + std::string FormatLength() const; + + virtual void MouseUp() + { + // Possibly create a new landmark "volume" with the line in subclasses + } + + virtual void MouseMove(int displayX, + int displayY, + double x, + double y, + const std::vector& displayTouches, + const std::vector& sceneTouches); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/RenderStyle.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/RenderStyle.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,106 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "RenderStyle.h" + +#include "../../Volumes/ImageBuffer3D.h" +#include "../Toolbox/DicomFrameConverter.h" + +#include + +namespace Deprecated +{ + RenderStyle::RenderStyle() + { + visible_ = true; + reverse_ = false; + windowing_ = OrthancStone::ImageWindowing_Custom; + alpha_ = 1; + applyLut_ = false; + lut_ = Orthanc::EmbeddedResources::COLORMAP_HOT; + drawGrid_ = false; + drawColor_[0] = 255; + drawColor_[1] = 255; + drawColor_[2] = 255; + customWindowCenter_ = 128; + customWindowWidth_ = 256; + interpolation_ = OrthancStone::ImageInterpolation_Nearest; + fontSize_ = 14; + } + + + void RenderStyle::ComputeWindowing(float& targetCenter, + float& targetWidth, + float defaultCenter, + float defaultWidth) const + { + if (windowing_ == OrthancStone::ImageWindowing_Custom) + { + targetCenter = customWindowCenter_; + targetWidth = customWindowWidth_; + } + else + { + return ::OrthancStone::ComputeWindowing + (targetCenter, targetWidth, windowing_, defaultCenter, defaultWidth); + } + } + + + void RenderStyle::SetColor(uint8_t red, + uint8_t green, + uint8_t blue) + { + drawColor_[0] = red; + drawColor_[1] = green; + drawColor_[2] = blue; + } + + + bool RenderStyle::FitRange(const OrthancStone::ImageBuffer3D& image, + const DicomFrameConverter& converter) + { + float minValue, maxValue; + + windowing_ = OrthancStone::ImageWindowing_Custom; + + if (image.GetRange(minValue, maxValue)) + { + // casting the narrower type to wider before calling the + operator + // will prevent overflowing (this is why the cast to double is only + // done on the first operand) + customWindowCenter_ = static_cast( + converter.Apply((static_cast(minValue) + maxValue) / 2.0)); + + customWindowWidth_ = static_cast( + converter.Apply(static_cast(maxValue) - minValue)); + + if (customWindowWidth_ > 1) + { + return true; + } + } + + customWindowCenter_ = 128.0; + customWindowWidth_ = 256.0; + return false; + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/RenderStyle.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/RenderStyle.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,63 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../../StoneEnumerations.h" +#include "../../Volumes/ImageBuffer3D.h" +#include "../Toolbox/DicomFrameConverter.h" + +#include + +#include + +namespace Deprecated +{ + struct RenderStyle + { + bool visible_; + bool reverse_; + OrthancStone::ImageWindowing windowing_; + float alpha_; // In [0,1] + bool applyLut_; + Orthanc::EmbeddedResources::FileResourceId lut_; + bool drawGrid_; + uint8_t drawColor_[3]; + float customWindowCenter_; + float customWindowWidth_; + OrthancStone::ImageInterpolation interpolation_; + unsigned int fontSize_; + + RenderStyle(); + + void ComputeWindowing(float& targetCenter, + float& targetWidth, + float defaultCenter, + float defaultWidth) const; + + void SetColor(uint8_t red, + uint8_t green, + uint8_t blue); + + bool FitRange(const OrthancStone::ImageBuffer3D& image, + const DicomFrameConverter& converter); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/SeriesFrameRendererFactory.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/SeriesFrameRendererFactory.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,177 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "SeriesFrameRendererFactory.h" + +#include "FrameRenderer.h" + +#include +#include +#include +#include +#include + + +namespace Deprecated +{ + void SeriesFrameRendererFactory::ReadCurrentFrameDataset(size_t frame) + { + if (currentDataset_.get() != NULL && + (fast_ || currentFrame_ == frame)) + { + // The frame has not changed since the previous call, no need to + // update the DICOM dataset + return; + } + + currentDataset_.reset(loader_->DownloadDicom(frame)); + currentFrame_ = frame; + + if (currentDataset_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + + void SeriesFrameRendererFactory::GetCurrentPixelSpacing(double& spacingX, + double& spacingY) const + { + if (currentDataset_.get() == NULL) + { + // There was no previous call "ReadCurrentFrameDataset()" + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + GeometryToolbox::GetPixelSpacing(spacingX, spacingY, *currentDataset_); + } + + + double SeriesFrameRendererFactory::GetCurrentSliceThickness() const + { + if (currentDataset_.get() == NULL) + { + // There was no previous call "ReadCurrentFrameDataset()" + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + try + { + OrthancPlugins::DicomDatasetReader reader(*currentDataset_); + + double thickness; + if (reader.GetDoubleValue(thickness, OrthancPlugins::DICOM_TAG_SLICE_THICKNESS)) + { + return thickness; + } + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) + { + } + + // Some arbitrary large slice thickness + return std::numeric_limits::infinity(); + } + + + SeriesFrameRendererFactory::SeriesFrameRendererFactory(ISeriesLoader* loader, // Takes ownership + bool fast) : + loader_(loader), + currentFrame_(0), + fast_(fast) + { + if (loader == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + + bool SeriesFrameRendererFactory::GetExtent(double& x1, + double& y1, + double& x2, + double& y2, + const SliceGeometry& viewportSlice) + { + if (currentDataset_.get() == NULL) + { + // There has been no previous call to + // "CreateLayerRenderer". Read some arbitrary DICOM frame, the + // one at the middle of the series. + unsigned int depth = loader_->GetGeometry().GetSliceCount(); + ReadCurrentFrameDataset(depth / 2); + } + + double spacingX, spacingY; + GetCurrentPixelSpacing(spacingX, spacingY); + + return FrameRenderer::ComputeFrameExtent(x1, y1, x2, y2, + viewportSlice, + loader_->GetGeometry().GetSlice(0), + loader_->GetWidth(), + loader_->GetHeight(), + spacingX, spacingY); + } + + + ILayerRenderer* SeriesFrameRendererFactory::CreateLayerRenderer(const SliceGeometry& viewportSlice) + { + size_t closest; + double distance; + + bool isOpposite; + if (!GeometryToolbox::IsParallelOrOpposite(isOpposite, loader_->GetGeometry().GetNormal(), viewportSlice.GetNormal()) || + !loader_->GetGeometry().ComputeClosestSlice(closest, distance, viewportSlice.GetOrigin())) + { + // Unable to compute the slice in the series that is the + // closest to the slice displayed by the viewport + return NULL; + } + + ReadCurrentFrameDataset(closest); + assert(currentDataset_.get() != NULL); + + double spacingX, spacingY; + GetCurrentPixelSpacing(spacingX, spacingY); + + if (distance <= GetCurrentSliceThickness() / 2.0) + { + SliceGeometry frameSlice(*currentDataset_); + return FrameRenderer::CreateRenderer(loader_->DownloadFrame(closest), + frameSlice, + *currentDataset_, + spacingX, spacingY, + true); + } + else + { + // The closest slice of the series is too far away from the + // slice displayed by the viewport + return NULL; + } + } + + + ISliceableVolume& SeriesFrameRendererFactory::GetSourceVolume() const + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/SeriesFrameRendererFactory.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/SeriesFrameRendererFactory.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,65 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "ILayerRendererFactory.h" + +#include "../Toolbox/ISeriesLoader.h" + +namespace Deprecated +{ + class SeriesFrameRendererFactory : public ILayerRendererFactory + { + private: + std::auto_ptr loader_; + size_t currentFrame_; + bool fast_; + + std::auto_ptr currentDataset_; + + void ReadCurrentFrameDataset(size_t frame); + + void GetCurrentPixelSpacing(double& spacingX, + double& spacingY) const; + + double GetCurrentSliceThickness() const; + + public: + SeriesFrameRendererFactory(ISeriesLoader* loader, // Takes ownership + bool fast); + + virtual bool GetExtent(double& x1, + double& y1, + double& x2, + double& y2, + const SliceGeometry& viewportSlice); + + virtual ILayerRenderer* CreateLayerRenderer(const SliceGeometry& viewportSlice); + + virtual bool HasSourceVolume() const + { + return false; + } + + virtual ISliceableVolume& GetSourceVolume() const; + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/SingleFrameRendererFactory.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/SingleFrameRendererFactory.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,88 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "SingleFrameRendererFactory.h" + +#include "FrameRenderer.h" +#include "../Toolbox/MessagingToolbox.h" +#include "../Toolbox/DicomFrameConverter.h" + +#include +#include +#include + +namespace Deprecated +{ + SingleFrameRendererFactory::SingleFrameRendererFactory(OrthancPlugins::IOrthancConnection& orthanc, + const std::string& instanceId, + unsigned int frame) : + orthanc_(orthanc), + instance_(instanceId), + frame_(frame) + { + dicom_.reset(new OrthancPlugins::FullOrthancDataset(orthanc, "/instances/" + instanceId + "/tags")); + + DicomFrameConverter converter; + converter.ReadParameters(*dicom_); + format_ = converter.GetExpectedPixelFormat(); + } + + + bool SingleFrameRendererFactory::GetExtent(double& x1, + double& y1, + double& x2, + double& y2, + const SliceGeometry& viewportSlice) + { + // Assume that PixelSpacingX == PixelSpacingY == 1 + + OrthancPlugins::DicomDatasetReader reader(*dicom_); + + unsigned int width, height; + + if (!reader.GetUnsignedIntegerValue(width, OrthancPlugins::DICOM_TAG_COLUMNS) || + !reader.GetUnsignedIntegerValue(height, OrthancPlugins::DICOM_TAG_ROWS)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + x1 = 0; + y1 = 0; + x2 = static_cast(width); + y2 = static_cast(height); + + return true; + } + + + ILayerRenderer* SingleFrameRendererFactory::CreateLayerRenderer(const SliceGeometry& viewportSlice) + { + SliceGeometry frameSlice(*dicom_); + return FrameRenderer::CreateRenderer(MessagingToolbox::DecodeFrame(orthanc_, instance_, frame_, format_), + frameSlice, *dicom_, 1, 1, true); + } + + + ISliceableVolume& SingleFrameRendererFactory::GetSourceVolume() const + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/SingleFrameRendererFactory.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/SingleFrameRendererFactory.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,69 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "ILayerRendererFactory.h" +#include + +namespace Deprecated +{ + class SingleFrameRendererFactory : public ILayerRendererFactory + { + private: + OrthancPlugins::IOrthancConnection& orthanc_; + std::auto_ptr dicom_; + + std::string instance_; + unsigned int frame_; + Orthanc::PixelFormat format_; + + public: + SingleFrameRendererFactory(OrthancPlugins::IOrthancConnection& orthanc, + const std::string& instanceId, + unsigned int frame); + + const OrthancPlugins::IDicomDataset& GetDataset() const + { + return *dicom_; + } + + SliceGeometry GetSliceGeometry() + { + return SliceGeometry(*dicom_); + } + + virtual bool GetExtent(double& x1, + double& y1, + double& x2, + double& y2, + const SliceGeometry& viewportSlice); + + virtual ILayerRenderer* CreateLayerRenderer(const SliceGeometry& viewportSlice); + + virtual bool HasSourceVolume() const + { + return false; + } + + virtual ISliceableVolume& GetSourceVolume() const; + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/SliceOutlineRenderer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/SliceOutlineRenderer.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,54 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "SliceOutlineRenderer.h" + +namespace Deprecated +{ + bool SliceOutlineRenderer::RenderLayer(OrthancStone::CairoContext& context, + const ViewportGeometry& view) + { + if (style_.visible_) + { + cairo_t *cr = context.GetObject(); + cairo_save(cr); + + context.SetSourceColor(style_.drawColor_); + + double x1 = -0.5 * pixelSpacingX_; + double y1 = -0.5 * pixelSpacingY_; + + cairo_set_line_width(cr, 1.0 / view.GetZoom()); + cairo_rectangle(cr, x1, y1, + static_cast(width_) * pixelSpacingX_, + static_cast(height_) * pixelSpacingY_); + + double handleSize = 10.0f / view.GetZoom(); + cairo_move_to(cr, x1 + handleSize, y1); + cairo_line_to(cr, x1, y1 + handleSize); + + cairo_stroke(cr); + cairo_restore(cr); + } + + return true; + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Layers/SliceOutlineRenderer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Layers/SliceOutlineRenderer.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,67 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "ILayerRenderer.h" +#include "../Toolbox/Slice.h" + +namespace Deprecated +{ + class SliceOutlineRenderer : public ILayerRenderer + { + private: + OrthancStone::CoordinateSystem3D geometry_; + double pixelSpacingX_; + double pixelSpacingY_; + unsigned int width_; + unsigned int height_; + RenderStyle style_; + + public: + SliceOutlineRenderer(const Slice& slice) : + geometry_(slice.GetGeometry()), + pixelSpacingX_(slice.GetPixelSpacingX()), + pixelSpacingY_(slice.GetPixelSpacingY()), + width_(slice.GetWidth()), + height_(slice.GetHeight()) + { + } + + virtual bool RenderLayer(OrthancStone::CairoContext& context, + const ViewportGeometry& view); + + virtual void SetLayerStyle(const RenderStyle& style) + { + style_ = style; + } + + virtual const OrthancStone::CoordinateSystem3D& GetLayerSlice() + { + return geometry_; + } + + virtual bool IsFullQuality() + { + return true; + } + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/SmartLoader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/SmartLoader.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,292 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "SmartLoader.h" + +#include "../Messages/MessageForwarder.h" +#include "../StoneException.h" +#include "Core/Images/Image.h" +#include "Core/Logging.h" +#include "Layers/DicomSeriesVolumeSlicer.h" +#include "Layers/FrameRenderer.h" +#include "Widgets/SliceViewerWidget.h" + +namespace Deprecated +{ + enum CachedSliceStatus + { + CachedSliceStatus_ScheduledToLoad, + CachedSliceStatus_GeometryLoaded, + CachedSliceStatus_ImageLoaded + }; + + class SmartLoader::CachedSlice : public IVolumeSlicer + { + public: + class RendererFactory : public LayerReadyMessage::IRendererFactory + { + private: + const CachedSlice& that_; + + public: + RendererFactory(const CachedSlice& that) : + that_(that) + { + } + + virtual ILayerRenderer* CreateRenderer() const + { + bool isFull = (that_.effectiveQuality_ == SliceImageQuality_FullPng || + that_.effectiveQuality_ == SliceImageQuality_FullPam); + + return FrameRenderer::CreateRenderer(*that_.image_, *that_.slice_, isFull); + } + }; + + unsigned int sliceIndex_; + std::auto_ptr slice_; + boost::shared_ptr image_; + SliceImageQuality effectiveQuality_; + CachedSliceStatus status_; + + public: + CachedSlice(OrthancStone::MessageBroker& broker) : + IVolumeSlicer(broker) + { + } + + virtual ~CachedSlice() + { + } + + virtual bool GetExtent(std::vector& points, + const OrthancStone::CoordinateSystem3D& viewportSlice) + { + // TODO: viewportSlice is not used !!!! + slice_->GetExtent(points); + return true; + } + + virtual void ScheduleLayerCreation(const OrthancStone::CoordinateSystem3D& viewportSlice) + { + // TODO: viewportSlice is not used !!!! + + // it has already been loaded -> trigger the "layer ready" message immediately otherwise, do nothing now. The LayerReady will be triggered + // once the VolumeSlicer is ready + if (status_ == CachedSliceStatus_ImageLoaded) + { + LOG(WARNING) << "ScheduleLayerCreation for CachedSlice (image is loaded): " << slice_->GetOrthancInstanceId(); + + RendererFactory factory(*this); + BroadcastMessage(IVolumeSlicer::LayerReadyMessage(*this, factory, slice_->GetGeometry())); + } + else + { + LOG(WARNING) << "ScheduleLayerCreation for CachedSlice (image is not loaded yet): " << slice_->GetOrthancInstanceId(); + } + } + + CachedSlice* Clone() const + { + CachedSlice* output = new CachedSlice(GetBroker()); + output->sliceIndex_ = sliceIndex_; + output->slice_.reset(slice_->Clone()); + output->image_ = image_; + output->effectiveQuality_ = effectiveQuality_; + output->status_ = status_; + + return output; + } + + }; + + + SmartLoader::SmartLoader(OrthancStone::MessageBroker& broker, + OrthancApiClient& orthancApiClient) : + IObservable(broker), + IObserver(broker), + imageQuality_(SliceImageQuality_FullPam), + orthancApiClient_(orthancApiClient) + { + } + + void SmartLoader::SetFrameInWidget(SliceViewerWidget& sliceViewer, + size_t layerIndex, + const std::string& instanceId, + unsigned int frame) + { + // TODO: check if this frame has already been loaded or is already being loaded. + // - if already loaded: create a "clone" that will emit the GeometryReady/ImageReady messages "immediately" + // (it can not be immediate because Observers needs to register first and this is done after this method returns) + // - if currently loading, we need to return an object that will observe the existing VolumeSlicer and forward + // the messages to its observables + // in both cases, we must be carefull about objects lifecycle !!! + + std::auto_ptr layerSource; + std::string sliceKeyId = instanceId + ":" + boost::lexical_cast(frame); + SmartLoader::CachedSlice* cachedSlice = NULL; + + if (cachedSlices_.find(sliceKeyId) != cachedSlices_.end()) // && cachedSlices_[sliceKeyId]->status_ == CachedSliceStatus_Loaded) + { + layerSource.reset(cachedSlices_[sliceKeyId]->Clone()); + cachedSlice = dynamic_cast(layerSource.get()); + } + else + { + layerSource.reset(new DicomSeriesVolumeSlicer(IObserver::GetBroker(), orthancApiClient_)); + dynamic_cast(layerSource.get())->SetImageQuality(imageQuality_); + layerSource->RegisterObserverCallback(new OrthancStone::Callable(*this, &SmartLoader::OnLayerGeometryReady)); + layerSource->RegisterObserverCallback(new OrthancStone::Callable(*this, &SmartLoader::OnFrameReady)); + layerSource->RegisterObserverCallback(new OrthancStone::Callable(*this, &SmartLoader::OnLayerReady)); + dynamic_cast(layerSource.get())->LoadFrame(instanceId, frame); + } + + // make sure that the widget registers the events before we trigger them + if (sliceViewer.GetLayerCount() == layerIndex) + { + sliceViewer.AddLayer(layerSource.release()); + } + else if (sliceViewer.GetLayerCount() > layerIndex) + { + sliceViewer.ReplaceLayer(layerIndex, layerSource.release()); + } + else + { + throw OrthancStone::StoneException(OrthancStone::ErrorCode_CanOnlyAddOneLayerAtATime); + } + + if (cachedSlice != NULL) + { + BroadcastMessage(IVolumeSlicer::GeometryReadyMessage(*cachedSlice)); + } + + } + + void SmartLoader::PreloadSlice(const std::string instanceId, + unsigned int frame) + { + // TODO: reactivate -> need to be able to ScheduleLayerLoading in IVolumeSlicer without calling ScheduleLayerCreation + return; + // TODO: check if it is already in the cache + + + + // create the slice in the cache with "empty" data + boost::shared_ptr cachedSlice(new CachedSlice(IObserver::GetBroker())); + cachedSlice->slice_.reset(new Slice(instanceId, frame)); + cachedSlice->status_ = CachedSliceStatus_ScheduledToLoad; + std::string sliceKeyId = instanceId + ":" + boost::lexical_cast(frame); + + LOG(WARNING) << "Will preload: " << sliceKeyId; + + cachedSlices_[sliceKeyId] = boost::shared_ptr(cachedSlice); + + std::auto_ptr layerSource(new DicomSeriesVolumeSlicer(IObserver::GetBroker(), orthancApiClient_)); + + dynamic_cast(layerSource.get())->SetImageQuality(imageQuality_); + layerSource->RegisterObserverCallback(new OrthancStone::Callable(*this, &SmartLoader::OnLayerGeometryReady)); + layerSource->RegisterObserverCallback(new OrthancStone::Callable(*this, &SmartLoader::OnFrameReady)); + layerSource->RegisterObserverCallback(new OrthancStone::Callable(*this, &SmartLoader::OnLayerReady)); + dynamic_cast(layerSource.get())->LoadFrame(instanceId, frame); + + // keep a ref to the VolumeSlicer until the slice is fully loaded and saved to cache + preloadingInstances_[sliceKeyId] = boost::shared_ptr(layerSource.release()); + } + + +// void PreloadStudy(const std::string studyId) +// { +// /* TODO */ +// } + +// void PreloadSeries(const std::string seriesId) +// { +// /* TODO */ +// } + + + void SmartLoader::OnLayerGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message) + { + const DicomSeriesVolumeSlicer& source = + dynamic_cast(message.GetOrigin()); + + // save/replace the slice in cache + const Slice& slice = source.GetSlice(0); // TODO handle GetSliceCount() + std::string sliceKeyId = (slice.GetOrthancInstanceId() + ":" + + boost::lexical_cast(slice.GetFrame())); + + LOG(WARNING) << "Geometry ready: " << sliceKeyId; + + boost::shared_ptr cachedSlice(new CachedSlice(IObserver::GetBroker())); + cachedSlice->slice_.reset(slice.Clone()); + cachedSlice->effectiveQuality_ = source.GetImageQuality(); + cachedSlice->status_ = CachedSliceStatus_GeometryLoaded; + + cachedSlices_[sliceKeyId] = boost::shared_ptr(cachedSlice); + + // re-emit original Layer message to observers + BroadcastMessage(message); + } + + + void SmartLoader::OnFrameReady(const DicomSeriesVolumeSlicer::FrameReadyMessage& message) + { + // save/replace the slice in cache + const Slice& slice = message.GetSlice(); + std::string sliceKeyId = (slice.GetOrthancInstanceId() + ":" + + boost::lexical_cast(slice.GetFrame())); + + LOG(WARNING) << "Image ready: " << sliceKeyId; + + boost::shared_ptr cachedSlice(new CachedSlice(IObserver::GetBroker())); + cachedSlice->image_.reset(Orthanc::Image::Clone(message.GetFrame())); + cachedSlice->effectiveQuality_ = message.GetImageQuality(); + cachedSlice->slice_.reset(message.GetSlice().Clone()); + cachedSlice->status_ = CachedSliceStatus_ImageLoaded; + + cachedSlices_[sliceKeyId] = cachedSlice; + + // re-emit original Layer message to observers + BroadcastMessage(message); + } + + + void SmartLoader::OnLayerReady(const IVolumeSlicer::LayerReadyMessage& message) + { + const DicomSeriesVolumeSlicer& source = + dynamic_cast(message.GetOrigin()); + + const Slice& slice = source.GetSlice(0); // TODO handle GetSliceCount() ? + std::string sliceKeyId = (slice.GetOrthancInstanceId() + ":" + + boost::lexical_cast(slice.GetFrame())); + + LOG(WARNING) << "Layer ready: " << sliceKeyId; + + // remove the slice from the preloading slices now that it has been fully loaded and it is referenced in the cache + if (preloadingInstances_.find(sliceKeyId) != preloadingInstances_.end()) + { + preloadingInstances_.erase(sliceKeyId); + } + + // re-emit original Layer message to observers + BroadcastMessage(message); + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/SmartLoader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/SmartLoader.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,67 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once +#include + +#include "Layers/DicomSeriesVolumeSlicer.h" +#include "../Messages/IObservable.h" +#include "Toolbox/OrthancApiClient.h" + +namespace Deprecated +{ + class SliceViewerWidget; + + class SmartLoader : public OrthancStone::IObservable, public OrthancStone::IObserver + { + class CachedSlice; + + protected: + typedef std::map > CachedSlices; + CachedSlices cachedSlices_; + + typedef std::map > PreloadingInstances; + PreloadingInstances preloadingInstances_; + + SliceImageQuality imageQuality_; + OrthancApiClient& orthancApiClient_; + + public: + SmartLoader(OrthancStone::MessageBroker& broker, OrthancApiClient& orthancApiClient); // TODO: add maxPreloadStorageSizeInBytes + +// void PreloadStudy(const std::string studyId); +// void PreloadSeries(const std::string seriesId); + void PreloadSlice(const std::string instanceId, unsigned int frame); + + void SetImageQuality(SliceImageQuality imageQuality) { imageQuality_ = imageQuality; } + + void SetFrameInWidget(SliceViewerWidget& sliceViewer, size_t layerIndex, const std::string& instanceId, unsigned int frame); + + void GetFirstInstanceIdForSeries(std::string& output, const std::string& seriesId); + + private: + void OnLayerGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message); + void OnFrameReady(const DicomSeriesVolumeSlicer::FrameReadyMessage& message); + void OnLayerReady(const IVolumeSlicer::LayerReadyMessage& message); + + }; + +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Toolbox/BaseWebService.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Toolbox/BaseWebService.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,145 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "BaseWebService.h" + +#include "../../Messages/IObservable.h" +#include "../../../Platforms/Generic/IOracleCommand.h" + +#include + +#include + +namespace Deprecated +{ + + + class BaseWebService::BaseWebServicePayload : public Orthanc::IDynamicObject + { + private: + std::auto_ptr< OrthancStone::MessageHandler > userSuccessHandler_; + std::auto_ptr< OrthancStone::MessageHandler > userFailureHandler_; + std::auto_ptr< Orthanc::IDynamicObject> userPayload_; + + public: + BaseWebServicePayload(OrthancStone::MessageHandler* userSuccessHandler, + OrthancStone::MessageHandler* userFailureHandler, + Orthanc::IDynamicObject* userPayload) : + userSuccessHandler_(userSuccessHandler), + userFailureHandler_(userFailureHandler), + userPayload_(userPayload) + { + } + + void HandleSuccess(const IWebService::HttpRequestSuccessMessage& message) const + { + if (userSuccessHandler_.get() != NULL) + { + // recreate a success message with the user payload + IWebService::HttpRequestSuccessMessage successMessage(message.GetUri(), + message.GetAnswer(), + message.GetAnswerSize(), + message.GetAnswerHttpHeaders(), + userPayload_.get()); + userSuccessHandler_->Apply(successMessage); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + void HandleFailure(const IWebService::HttpRequestErrorMessage& message) const + { + if (userFailureHandler_.get() != NULL) + { + // recreate a failure message with the user payload + IWebService::HttpRequestErrorMessage failureMessage(message.GetUri(), + userPayload_.get()); + + userFailureHandler_->Apply(failureMessage); + } + } + + }; + + + void BaseWebService::GetAsync(const std::string& uri, + const HttpHeaders& headers, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback, + unsigned int timeoutInSeconds) + { + if (cache_.find(uri) == cache_.end()) + { + GetAsyncInternal(uri, headers, + new BaseWebService::BaseWebServicePayload(successCallback, failureCallback, payload), // ownership is transfered + new OrthancStone::Callable + (*this, &BaseWebService::CacheAndNotifyHttpSuccess), + new OrthancStone::Callable + (*this, &BaseWebService::NotifyHttpError), + timeoutInSeconds); + } + else + { + // create a command and "post" it to the Oracle so it is executed and commited "later" + NotifyHttpSuccessLater(cache_[uri], payload, successCallback); + } + + } + + + + void BaseWebService::NotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message) + { + if (message.HasPayload()) + { + dynamic_cast(message.GetPayload()).HandleSuccess(message); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + void BaseWebService::CacheAndNotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message) + { + cache_[message.GetUri()] = boost::shared_ptr(new CachedHttpRequestSuccessMessage(message)); + NotifyHttpSuccess(message); + } + + void BaseWebService::NotifyHttpError(const IWebService::HttpRequestErrorMessage& message) + { + if (message.HasPayload()) + { + dynamic_cast(message.GetPayload()).HandleFailure(message); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + + + +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Toolbox/BaseWebService.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Toolbox/BaseWebService.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,131 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IWebService.h" + +#include +#include + +namespace Deprecated +{ + // This is an intermediate of IWebService that implements some caching on + // the HTTP GET requests + class BaseWebService : public IWebService, public OrthancStone::IObserver + { + public: + class CachedHttpRequestSuccessMessage + { + protected: + std::string uri_; + void* answer_; + size_t answerSize_; + IWebService::HttpHeaders answerHeaders_; + + public: + CachedHttpRequestSuccessMessage(const IWebService::HttpRequestSuccessMessage& message) : + uri_(message.GetUri()), + answerSize_(message.GetAnswerSize()), + answerHeaders_(message.GetAnswerHttpHeaders()) + { + answer_ = malloc(answerSize_); + memcpy(answer_, message.GetAnswer(), answerSize_); + } + + ~CachedHttpRequestSuccessMessage() + { + free(answer_); + } + + const std::string& GetUri() const + { + return uri_; + } + + const void* GetAnswer() const + { + return answer_; + } + + size_t GetAnswerSize() const + { + return answerSize_; + } + + const IWebService::HttpHeaders& GetAnswerHttpHeaders() const + { + return answerHeaders_; + } + + }; + protected: + class BaseWebServicePayload; + + bool cacheEnabled_; + std::map > cache_; // TODO: this is currently an infinite cache ! + + public: + + BaseWebService(OrthancStone::MessageBroker& broker) : + IWebService(broker), + IObserver(broker), + cacheEnabled_(true) + { + } + + virtual ~BaseWebService() + { + } + + virtual void EnableCache(bool enable) + { + cacheEnabled_ = enable; + } + + virtual void GetAsync(const std::string& uri, + const HttpHeaders& headers, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback = NULL, + unsigned int timeoutInSeconds = 60); + + protected: + virtual void GetAsyncInternal(const std::string& uri, + const HttpHeaders& headers, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback = NULL, + unsigned int timeoutInSeconds = 60) = 0; + + virtual void NotifyHttpSuccessLater(boost::shared_ptr cachedHttpMessage, + Orthanc::IDynamicObject* payload, // takes ownership + OrthancStone::MessageHandler* successCallback) = 0; + + private: + void NotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message); + + void NotifyHttpError(const IWebService::HttpRequestErrorMessage& message); + + void CacheAndNotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message); + + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Toolbox/DicomFrameConverter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Toolbox/DicomFrameConverter.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,282 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "DicomFrameConverter.h" + +#include "../../Toolbox/LinearAlgebra.h" + +#include +#include +#include +#include + +namespace Deprecated +{ + static const Orthanc::DicomTag IMAGE_TAGS[] = + { + Orthanc::DICOM_TAG_BITS_STORED, + Orthanc::DICOM_TAG_DOSE_GRID_SCALING, + Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION, + Orthanc::DICOM_TAG_PIXEL_REPRESENTATION, + Orthanc::DICOM_TAG_RESCALE_INTERCEPT, + Orthanc::DICOM_TAG_RESCALE_SLOPE, + Orthanc::DICOM_TAG_WINDOW_CENTER, + Orthanc::DICOM_TAG_WINDOW_WIDTH + }; + + + void DicomFrameConverter::SetDefaultParameters() + { + isSigned_ = true; + isColor_ = false; + hasRescale_ = false; + rescaleIntercept_ = 0; + rescaleSlope_ = 1; + hasDefaultWindow_ = false; + defaultWindowCenter_ = 128; + defaultWindowWidth_ = 256; + expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16; + } + + + void DicomFrameConverter::ReadParameters(const Orthanc::DicomMap& dicom) + { + SetDefaultParameters(); + + OrthancStone::Vector c, w; + if (OrthancStone::LinearAlgebra::ParseVector(c, dicom, Orthanc::DICOM_TAG_WINDOW_CENTER) && + OrthancStone::LinearAlgebra::ParseVector(w, dicom, Orthanc::DICOM_TAG_WINDOW_WIDTH) && + c.size() > 0 && + w.size() > 0) + { + hasDefaultWindow_ = true; + defaultWindowCenter_ = static_cast(c[0]); + defaultWindowWidth_ = static_cast(w[0]); + } + + int32_t tmp; + if (!dicom.ParseInteger32(tmp, Orthanc::DICOM_TAG_PIXEL_REPRESENTATION)) + { + // Type 1 tag, must be present + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + isSigned_ = (tmp == 1); + + double doseGridScaling; + bool isRTDose = false; + + if (dicom.ParseDouble(rescaleIntercept_, Orthanc::DICOM_TAG_RESCALE_INTERCEPT) && + dicom.ParseDouble(rescaleSlope_, Orthanc::DICOM_TAG_RESCALE_SLOPE)) + { + hasRescale_ = true; + } + else if (dicom.ParseDouble(doseGridScaling, Orthanc::DICOM_TAG_DOSE_GRID_SCALING)) + { + // This is for RT-DOSE + hasRescale_ = true; + isRTDose = true; + rescaleIntercept_ = 0; + rescaleSlope_ = doseGridScaling; + + if (!dicom.ParseInteger32(tmp, Orthanc::DICOM_TAG_BITS_STORED)) + { + // Type 1 tag, must be present + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + switch (tmp) + { + case 16: + expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16; + break; + + case 32: + expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale32; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } + + std::string photometric; + if (dicom.CopyToString(photometric, Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION, false)) + { + photometric = Orthanc::Toolbox::StripSpaces(photometric); + } + else + { + // Type 1 tag, must be present + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + photometric_ = Orthanc::StringToPhotometricInterpretation(photometric.c_str()); + + isColor_ = (photometric != "MONOCHROME1" && + photometric != "MONOCHROME2"); + + // TODO Add more checks, e.g. on the number of bytes per value + // (cf. DicomImageInformation.h in Orthanc) + + if (!isRTDose) + { + if (isColor_) + { + expectedPixelFormat_ = Orthanc::PixelFormat_RGB24; + } + else if (isSigned_) + { + expectedPixelFormat_ = Orthanc::PixelFormat_SignedGrayscale16; + } + else + { + expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16; + } + } + } + + + void DicomFrameConverter::ReadParameters(const OrthancPlugins::IDicomDataset& dicom) + { + Orthanc::DicomMap converted; + + for (size_t i = 0; i < sizeof(IMAGE_TAGS) / sizeof(Orthanc::DicomTag); i++) + { + OrthancPlugins::DicomTag tag(IMAGE_TAGS[i].GetGroup(), IMAGE_TAGS[i].GetElement()); + + std::string value; + if (dicom.GetStringValue(value, tag)) + { + converted.SetValue(IMAGE_TAGS[i], value, false); + } + } + + ReadParameters(converted); + } + + + void DicomFrameConverter::ConvertFrameInplace(std::auto_ptr& source) const + { + assert(sizeof(float) == 4); + + if (source.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + if (source->GetFormat() == GetExpectedPixelFormat() && + source->GetFormat() == Orthanc::PixelFormat_RGB24) + { + // No conversion has to be done, check out (*) + return; + } + else + { + source.reset(ConvertFrame(*source)); + } + } + + + Orthanc::ImageAccessor* DicomFrameConverter::ConvertFrame(const Orthanc::ImageAccessor& source) const + { + assert(sizeof(float) == 4); + + Orthanc::PixelFormat sourceFormat = source.GetFormat(); + + if (sourceFormat != GetExpectedPixelFormat()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + + if (sourceFormat == Orthanc::PixelFormat_RGB24) + { + // This is the case of a color image. No conversion has to be done (*) + std::auto_ptr converted(new Orthanc::Image(Orthanc::PixelFormat_RGB24, + source.GetWidth(), + source.GetHeight(), + false)); + Orthanc::ImageProcessing::Copy(*converted, source); + return converted.release(); + } + else + { + assert(sourceFormat == Orthanc::PixelFormat_Grayscale16 || + sourceFormat == Orthanc::PixelFormat_Grayscale32 || + sourceFormat == Orthanc::PixelFormat_SignedGrayscale16); + + // This is the case of a grayscale frame. Convert it to Float32. + std::auto_ptr converted(new Orthanc::Image(Orthanc::PixelFormat_Float32, + source.GetWidth(), + source.GetHeight(), + false)); + Orthanc::ImageProcessing::Convert(*converted, source); + + // Correct rescale slope/intercept if need be + ApplyRescale(*converted, sourceFormat != Orthanc::PixelFormat_Grayscale32); + + return converted.release(); + } + } + + + void DicomFrameConverter::ApplyRescale(Orthanc::ImageAccessor& image, + bool useDouble) const + { + if (image.GetFormat() != Orthanc::PixelFormat_Float32) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + + if (hasRescale_) + { + for (unsigned int y = 0; y < image.GetHeight(); y++) + { + float* p = reinterpret_cast(image.GetRow(y)); + + if (useDouble) + { + // Slower, accurate implementation using double + for (unsigned int x = 0; x < image.GetWidth(); x++, p++) + { + double value = static_cast(*p); + *p = static_cast(value * rescaleSlope_ + rescaleIntercept_); + } + } + else + { + // Fast, approximate implementation using float + for (unsigned int x = 0; x < image.GetWidth(); x++, p++) + { + *p = (*p) * static_cast(rescaleSlope_) + static_cast(rescaleIntercept_); + } + } + } + } + } + + + double DicomFrameConverter::Apply(double x) const + { + return x * rescaleSlope_ + rescaleIntercept_; + } + +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Toolbox/DicomFrameConverter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Toolbox/DicomFrameConverter.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,169 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include +#include +#include + +#include + +namespace Deprecated +{ + /** + * This class is responsible for converting the pixel format of a + * DICOM frame coming from Orthanc, into a pixel format that is + * suitable for Stone, given the relevant DICOM tags: + * - Color frames will stay in the RGB24 format. + * - Grayscale frames will be converted to the Float32 format. + **/ + class DicomFrameConverter + { + private: + bool isSigned_; + bool isColor_; + bool hasRescale_; + double rescaleIntercept_; + double rescaleSlope_; + bool hasDefaultWindow_; + double defaultWindowCenter_; + double defaultWindowWidth_; + + Orthanc::PhotometricInterpretation photometric_; + Orthanc::PixelFormat expectedPixelFormat_; + + void SetDefaultParameters(); + + public: + DicomFrameConverter() + { + SetDefaultParameters(); + } + + ~DicomFrameConverter() + { + // TODO: check whether this dtor is called or not + // An MSVC warning explains that declaring an + // std::auto_ptr with a forward-declared type + // prevents its dtor from being called. Does not + // seem an issue here (only POD types inside), but + // definitely something to keep an eye on. + (void)0; + } + + // AM: this is required to serialize/deserialize it + DicomFrameConverter( + bool isSigned, + bool isColor, + bool hasRescale, + double rescaleIntercept, + double rescaleSlope, + bool hasDefaultWindow, + double defaultWindowCenter, + double defaultWindowWidth, + Orthanc::PhotometricInterpretation photometric, + Orthanc::PixelFormat expectedPixelFormat + ): + isSigned_(isSigned), + isColor_(isColor), + hasRescale_(hasRescale), + rescaleIntercept_(rescaleIntercept), + rescaleSlope_(rescaleSlope), + hasDefaultWindow_(hasDefaultWindow), + defaultWindowCenter_(defaultWindowCenter), + defaultWindowWidth_(defaultWindowWidth), + photometric_(photometric), + expectedPixelFormat_(expectedPixelFormat) + {} + + void GetParameters(bool& isSigned, + bool& isColor, + bool& hasRescale, + double& rescaleIntercept, + double& rescaleSlope, + bool& hasDefaultWindow, + double& defaultWindowCenter, + double& defaultWindowWidth, + Orthanc::PhotometricInterpretation& photometric, + Orthanc::PixelFormat& expectedPixelFormat) const + { + isSigned = isSigned_; + isColor = isColor_; + hasRescale = hasRescale_; + rescaleIntercept = rescaleIntercept_; + rescaleSlope = rescaleSlope_; + hasDefaultWindow = hasDefaultWindow_; + defaultWindowCenter = defaultWindowCenter_; + defaultWindowWidth = defaultWindowWidth_; + photometric = photometric_; + expectedPixelFormat = expectedPixelFormat_; + } + + Orthanc::PixelFormat GetExpectedPixelFormat() const + { + return expectedPixelFormat_; + } + + Orthanc::PhotometricInterpretation GetPhotometricInterpretation() const + { + return photometric_; + } + + void ReadParameters(const Orthanc::DicomMap& dicom); + + void ReadParameters(const OrthancPlugins::IDicomDataset& dicom); + + bool HasDefaultWindow() const + { + return hasDefaultWindow_; + } + + double GetDefaultWindowCenter() const + { + return defaultWindowCenter_; + } + + double GetDefaultWindowWidth() const + { + return defaultWindowWidth_; + } + + double GetRescaleIntercept() const + { + return rescaleIntercept_; + } + + double GetRescaleSlope() const + { + return rescaleSlope_; + } + + void ConvertFrameInplace(std::auto_ptr& source) const; + + Orthanc::ImageAccessor* ConvertFrame(const Orthanc::ImageAccessor& source) const; + + void ApplyRescale(Orthanc::ImageAccessor& image, + bool useDouble) const; + + double Apply(double x) const; + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Toolbox/DownloadStack.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Toolbox/DownloadStack.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,196 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "DownloadStack.h" + +#include + +#include + +namespace Deprecated +{ + bool DownloadStack::CheckInvariants() const + { + std::vector dequeued(nodes_.size(), true); + + int i = firstNode_; + while (i != NIL) + { + const Node& node = nodes_[i]; + + dequeued[i] = false; + + if (node.next_ != NIL && + nodes_[node.next_].prev_ != i) + { + return false; + } + + if (node.prev_ != NIL && + nodes_[node.prev_].next_ != i) + { + return false; + } + + i = nodes_[i].next_; + } + + for (size_t i = 0; i < nodes_.size(); i++) + { + if (nodes_[i].dequeued_ != dequeued[i]) + { + return false; + } + } + + return true; + } + + + DownloadStack::DownloadStack(unsigned int size) + { + nodes_.resize(size); + + if (size == 0) + { + firstNode_ = NIL; + } + else + { + for (size_t i = 0; i < size; i++) + { + nodes_[i].prev_ = static_cast(i - 1); + nodes_[i].next_ = static_cast(i + 1); + nodes_[i].dequeued_ = false; + } + + nodes_.front().prev_ = NIL; + nodes_.back().next_ = NIL; + firstNode_ = 0; + } + + assert(CheckInvariants()); + } + + + DownloadStack::~DownloadStack() + { + assert(CheckInvariants()); + } + + + bool DownloadStack::Pop(unsigned int& value) + { + assert(CheckInvariants()); + + if (firstNode_ == NIL) + { + for (size_t i = 0; i < nodes_.size(); i++) + { + assert(nodes_[i].dequeued_); + } + + return false; + } + else + { + assert(firstNode_ >= 0 && firstNode_ < static_cast(nodes_.size())); + value = firstNode_; + + Node& node = nodes_[firstNode_]; + assert(node.prev_ == NIL); + assert(!node.dequeued_); + + node.dequeued_ = true; + firstNode_ = node.next_; + + if (firstNode_ != NIL) + { + nodes_[firstNode_].prev_ = NIL; + } + + return true; + } + } + + + void DownloadStack::SetTopNodeInternal(unsigned int value) + { + assert(CheckInvariants()); + + Node& node = nodes_[value]; + + if (node.dequeued_) + { + // This node has already been processed by the download thread, nothing to do + return; + } + + // Remove the node from the list + if (node.prev_ == NIL) + { + assert(firstNode_ == static_cast(value)); + + // This is already the top node in the list, nothing to do + return; + } + + nodes_[node.prev_].next_ = node.next_; + + if (node.next_ != NIL) + { + nodes_[node.next_].prev_ = node.prev_; + } + + // Add back the node at the top of the list + assert(firstNode_ != NIL); + + Node& old = nodes_[firstNode_]; + assert(old.prev_ == NIL); + assert(!old.dequeued_); + node.prev_ = NIL; + node.next_ = firstNode_; + old.prev_ = value; + + firstNode_ = value; + } + + + void DownloadStack::SetTopNode(unsigned int value) + { + if (value >= nodes_.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + SetTopNodeInternal(value); + } + + + void DownloadStack::SetTopNodePermissive(int value) + { + if (value >= 0 && + value < static_cast(nodes_.size())) + { + SetTopNodeInternal(value); + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Toolbox/DownloadStack.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Toolbox/DownloadStack.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,60 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include +#include + +namespace Deprecated +{ + class DownloadStack : public boost::noncopyable + { + private: + static const int NIL = -1; + + // This is a doubly-linked list + struct Node + { + int next_; + int prev_; + bool dequeued_; + }; + + std::vector nodes_; + int firstNode_; + + bool CheckInvariants() const; + + void SetTopNodeInternal(unsigned int value); + + public: + DownloadStack(unsigned int size); + + ~DownloadStack(); + + bool Pop(unsigned int& value); + + void SetTopNode(unsigned int value); + + void SetTopNodePermissive(int value); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Toolbox/IDelayedCallExecutor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Toolbox/IDelayedCallExecutor.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,58 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../../Messages/IObserver.h" +#include "../../Messages/ICallable.h" + +#include +#include + +#include +#include + +namespace Deprecated +{ + // The IDelayedCall executes a callback after a delay (equivalent to timeout() function in javascript). + class IDelayedCallExecutor : public boost::noncopyable + { + protected: + OrthancStone::MessageBroker& broker_; + + public: + ORTHANC_STONE_DEFINE_EMPTY_MESSAGE(__FILE__, __LINE__, TimeoutMessage); + + IDelayedCallExecutor(OrthancStone::MessageBroker& broker) : + broker_(broker) + { + } + + + virtual ~IDelayedCallExecutor() + { + } + + + virtual void Schedule(OrthancStone::MessageHandler* callback, + unsigned int timeoutInMs = 1000) = 0; + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Toolbox/IWebService.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Toolbox/IWebService.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,55 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "IWebService.h" + +#include + + +namespace Deprecated +{ + const Orthanc::IDynamicObject& + IWebService::HttpRequestSuccessMessage::GetPayload() const + { + if (HasPayload()) + { + return *payload_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + + const Orthanc::IDynamicObject& + IWebService::HttpRequestErrorMessage::GetPayload() const + { + if (HasPayload()) + { + return *payload_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Toolbox/IWebService.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Toolbox/IWebService.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,166 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../../Messages/IObserver.h" +#include "../../Messages/ICallable.h" + +#include +#include + +#include +#include + +namespace Deprecated +{ + // The IWebService performs HTTP requests. + // Since applications can run in native or WASM environment and, since + // in a WASM environment, the WebService is asynchronous, the IWebservice + // also implements an asynchronous interface: you must schedule a request + // and you'll be notified when the response/error is ready. + class IWebService : public boost::noncopyable + { + protected: + OrthancStone::MessageBroker& broker_; + + public: + typedef std::map HttpHeaders; + + class HttpRequestSuccessMessage : public OrthancStone::IMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + const std::string& uri_; + const void* answer_; + size_t answerSize_; + const HttpHeaders& answerHeaders_; + const Orthanc::IDynamicObject* payload_; + + public: + HttpRequestSuccessMessage(const std::string& uri, + const void* answer, + size_t answerSize, + const HttpHeaders& answerHeaders, + const Orthanc::IDynamicObject* payload) : + uri_(uri), + answer_(answer), + answerSize_(answerSize), + answerHeaders_(answerHeaders), + payload_(payload) + { + } + + const std::string& GetUri() const + { + return uri_; + } + + const void* GetAnswer() const + { + return answer_; + } + + size_t GetAnswerSize() const + { + return answerSize_; + } + + const HttpHeaders& GetAnswerHttpHeaders() const + { + return answerHeaders_; + } + + bool HasPayload() const + { + return payload_ != NULL; + } + + const Orthanc::IDynamicObject& GetPayload() const; + }; + + + class HttpRequestErrorMessage : public OrthancStone::IMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + const std::string& uri_; + const Orthanc::IDynamicObject* payload_; + + public: + HttpRequestErrorMessage(const std::string& uri, + const Orthanc::IDynamicObject* payload) : + uri_(uri), + payload_(payload) + { + } + + const std::string& GetUri() const + { + return uri_; + } + + bool HasPayload() const + { + return payload_ != NULL; + } + + const Orthanc::IDynamicObject& GetPayload() const; + }; + + + IWebService(OrthancStone::MessageBroker& broker) : + broker_(broker) + { + } + + + virtual ~IWebService() + { + } + + virtual void EnableCache(bool enable) = 0; + + virtual void GetAsync(const std::string& uri, + const HttpHeaders& headers, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback = NULL, + unsigned int timeoutInSeconds = 60) = 0; + + virtual void PostAsync(const std::string& uri, + const HttpHeaders& headers, + const std::string& body, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback = NULL, + unsigned int timeoutInSeconds = 60) = 0; + + virtual void DeleteAsync(const std::string& uri, + const HttpHeaders& headers, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback = NULL, + unsigned int timeoutInSeconds = 60) = 0; + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Toolbox/OrthancApiClient.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Toolbox/OrthancApiClient.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,336 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + +#include "OrthancApiClient.h" + +#include "../../Toolbox/MessagingToolbox.h" + +#include + +namespace Deprecated +{ + const Orthanc::IDynamicObject& OrthancApiClient::JsonResponseReadyMessage::GetPayload() const + { + if (HasPayload()) + { + return *payload_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + + const Orthanc::IDynamicObject& OrthancApiClient::BinaryResponseReadyMessage::GetPayload() const + { + if (HasPayload()) + { + return *payload_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + + const Orthanc::IDynamicObject& OrthancApiClient::EmptyResponseReadyMessage::GetPayload() const + { + if (HasPayload()) + { + return *payload_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + + class OrthancApiClient::WebServicePayload : public Orthanc::IDynamicObject + { + private: + std::auto_ptr< OrthancStone::MessageHandler > emptyHandler_; + std::auto_ptr< OrthancStone::MessageHandler > jsonHandler_; + std::auto_ptr< OrthancStone::MessageHandler > binaryHandler_; + std::auto_ptr< OrthancStone::MessageHandler > failureHandler_; + std::auto_ptr< Orthanc::IDynamicObject > userPayload_; + + void NotifyConversionError(const IWebService::HttpRequestSuccessMessage& message) const + { + if (failureHandler_.get() != NULL) + { + failureHandler_->Apply(IWebService::HttpRequestErrorMessage + (message.GetUri(), userPayload_.get())); + } + } + + public: + WebServicePayload(OrthancStone::MessageHandler* handler, + OrthancStone::MessageHandler* failureHandler, + Orthanc::IDynamicObject* userPayload) : + emptyHandler_(handler), + failureHandler_(failureHandler), + userPayload_(userPayload) + { + if (handler == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + WebServicePayload(OrthancStone::MessageHandler* handler, + OrthancStone::MessageHandler* failureHandler, + Orthanc::IDynamicObject* userPayload) : + binaryHandler_(handler), + failureHandler_(failureHandler), + userPayload_(userPayload) + { + if (handler == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + WebServicePayload(OrthancStone::MessageHandler* handler, + OrthancStone::MessageHandler* failureHandler, + Orthanc::IDynamicObject* userPayload) : + jsonHandler_(handler), + failureHandler_(failureHandler), + userPayload_(userPayload) + { + if (handler == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + void HandleSuccess(const IWebService::HttpRequestSuccessMessage& message) const + { + if (emptyHandler_.get() != NULL) + { + emptyHandler_->Apply(OrthancApiClient::EmptyResponseReadyMessage + (message.GetUri(), userPayload_.get())); + } + else if (binaryHandler_.get() != NULL) + { + binaryHandler_->Apply(OrthancApiClient::BinaryResponseReadyMessage + (message.GetUri(), message.GetAnswer(), + message.GetAnswerSize(), userPayload_.get())); + } + else if (jsonHandler_.get() != NULL) + { + Json::Value response; + if (OrthancStone::MessagingToolbox::ParseJson(response, message.GetAnswer(), message.GetAnswerSize())) + { + jsonHandler_->Apply(OrthancApiClient::JsonResponseReadyMessage + (message.GetUri(), response, userPayload_.get())); + } + else + { + NotifyConversionError(message); + } + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + void HandleFailure(const IWebService::HttpRequestErrorMessage& message) const + { + if (failureHandler_.get() != NULL) + { + failureHandler_->Apply(IWebService::HttpRequestErrorMessage + (message.GetUri(), userPayload_.get())); + } + } + }; + + + OrthancApiClient::OrthancApiClient(OrthancStone::MessageBroker& broker, + IWebService& web, + const std::string& baseUrl) : + IObservable(broker), + IObserver(broker), + web_(web), + baseUrl_(baseUrl) + { + } + + + void OrthancApiClient::GetJsonAsync( + const std::string& uri, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback, + Orthanc::IDynamicObject* payload) + { + IWebService::HttpHeaders emptyHeaders; + web_.GetAsync(baseUrl_ + uri, + emptyHeaders, + new WebServicePayload(successCallback, failureCallback, payload), + new OrthancStone::Callable + (*this, &OrthancApiClient::NotifyHttpSuccess), + new OrthancStone::Callable + (*this, &OrthancApiClient::NotifyHttpError)); + } + + + void OrthancApiClient::GetBinaryAsync( + const std::string& uri, + const std::string& contentType, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback, + Orthanc::IDynamicObject* payload) + { + IWebService::HttpHeaders headers; + headers["Accept"] = contentType; + GetBinaryAsync(uri, headers, successCallback, failureCallback, payload); + } + + void OrthancApiClient::GetBinaryAsync( + const std::string& uri, + const IWebService::HttpHeaders& headers, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback, + Orthanc::IDynamicObject* payload) + { + // printf("GET [%s] [%s]\n", baseUrl_.c_str(), uri.c_str()); + + web_.GetAsync(baseUrl_ + uri, headers, + new WebServicePayload(successCallback, failureCallback, payload), + new OrthancStone::Callable + (*this, &OrthancApiClient::NotifyHttpSuccess), + new OrthancStone::Callable + (*this, &OrthancApiClient::NotifyHttpError)); + } + + + void OrthancApiClient::PostBinaryAsyncExpectJson( + const std::string& uri, + const std::string& body, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback, + Orthanc::IDynamicObject* payload) + { + web_.PostAsync(baseUrl_ + uri, IWebService::HttpHeaders(), body, + new WebServicePayload(successCallback, failureCallback, payload), + new OrthancStone::Callable + (*this, &OrthancApiClient::NotifyHttpSuccess), + new OrthancStone::Callable + (*this, &OrthancApiClient::NotifyHttpError)); + + } + + void OrthancApiClient::PostBinaryAsync( + const std::string& uri, + const std::string& body) + { + web_.PostAsync(baseUrl_ + uri, IWebService::HttpHeaders(), body, NULL, NULL, NULL); + } + + void OrthancApiClient::PostBinaryAsync( + const std::string& uri, + const std::string& body, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback, + Orthanc::IDynamicObject* payload /* takes ownership */) + { + web_.PostAsync(baseUrl_ + uri, IWebService::HttpHeaders(), body, + new WebServicePayload(successCallback, failureCallback, payload), + new OrthancStone::Callable + (*this, &OrthancApiClient::NotifyHttpSuccess), + new OrthancStone::Callable + (*this, &OrthancApiClient::NotifyHttpError)); + } + + void OrthancApiClient::PostJsonAsyncExpectJson( + const std::string& uri, + const Json::Value& data, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback, + Orthanc::IDynamicObject* payload) + { + std::string body; + OrthancStone::MessagingToolbox::JsonToString(body, data); + return PostBinaryAsyncExpectJson(uri, body, successCallback, failureCallback, payload); + } + + void OrthancApiClient::PostJsonAsync( + const std::string& uri, + const Json::Value& data) + { + std::string body; + OrthancStone::MessagingToolbox::JsonToString(body, data); + return PostBinaryAsync(uri, body); + } + + void OrthancApiClient::PostJsonAsync( + const std::string& uri, + const Json::Value& data, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback, + Orthanc::IDynamicObject* payload /* takes ownership */) + { + std::string body; + OrthancStone::MessagingToolbox::JsonToString(body, data); + return PostBinaryAsync(uri, body, successCallback, failureCallback, payload); + } + + void OrthancApiClient::DeleteAsync( + const std::string& uri, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback, + Orthanc::IDynamicObject* payload) + { + web_.DeleteAsync(baseUrl_ + uri, IWebService::HttpHeaders(), + new WebServicePayload(successCallback, failureCallback, payload), + new OrthancStone::Callable + (*this, &OrthancApiClient::NotifyHttpSuccess), + new OrthancStone::Callable + (*this, &OrthancApiClient::NotifyHttpError)); + } + + + void OrthancApiClient::NotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message) + { + if (message.HasPayload()) + { + dynamic_cast(message.GetPayload()).HandleSuccess(message); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + void OrthancApiClient::NotifyHttpError(const IWebService::HttpRequestErrorMessage& message) + { + if (message.HasPayload()) + { + dynamic_cast(message.GetPayload()).HandleFailure(message); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Toolbox/OrthancApiClient.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Toolbox/OrthancApiClient.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,243 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include +#include + +#include "IWebService.h" +#include "../../Messages/IObservable.h" +#include "../../Messages/Promise.h" + +namespace Deprecated +{ + class OrthancApiClient : + public OrthancStone::IObservable, + public OrthancStone::IObserver + { + public: + class JsonResponseReadyMessage : public OrthancStone::IMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + const std::string& uri_; + const Json::Value& json_; + const Orthanc::IDynamicObject* payload_; + + public: + JsonResponseReadyMessage(const std::string& uri, + const Json::Value& json, + const Orthanc::IDynamicObject* payload) : + uri_(uri), + json_(json), + payload_(payload) + { + } + + const std::string& GetUri() const + { + return uri_; + } + + const Json::Value& GetJson() const + { + return json_; + } + + bool HasPayload() const + { + return payload_ != NULL; + } + + const Orthanc::IDynamicObject& GetPayload() const; + }; + + + class BinaryResponseReadyMessage : public OrthancStone::IMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + const std::string& uri_; + const void* answer_; + size_t answerSize_; + const Orthanc::IDynamicObject* payload_; + + public: + BinaryResponseReadyMessage(const std::string& uri, + const void* answer, + size_t answerSize, + const Orthanc::IDynamicObject* payload) : + uri_(uri), + answer_(answer), + answerSize_(answerSize), + payload_(payload) + { + } + + const std::string& GetUri() const + { + return uri_; + } + + const void* GetAnswer() const + { + return answer_; + } + + size_t GetAnswerSize() const + { + return answerSize_; + } + + bool HasPayload() const + { + return payload_ != NULL; + } + + const Orthanc::IDynamicObject& GetPayload() const; + }; + + + class EmptyResponseReadyMessage : public OrthancStone::IMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + const std::string& uri_; + const Orthanc::IDynamicObject* payload_; + + public: + EmptyResponseReadyMessage(const std::string& uri, + const Orthanc::IDynamicObject* payload) : + uri_(uri), + payload_(payload) + { + } + + const std::string& GetUri() const + { + return uri_; + } + + bool HasPayload() const + { + return payload_ != NULL; + } + + const Orthanc::IDynamicObject& GetPayload() const; + }; + + + + private: + class WebServicePayload; + + protected: + IWebService& web_; + std::string baseUrl_; + + public: + OrthancApiClient(OrthancStone::MessageBroker& broker, + IWebService& web, + const std::string& baseUrl); + + virtual ~OrthancApiClient() + { + } + + const std::string& GetBaseUrl() const {return baseUrl_;} + + // schedule a GET request expecting a JSON response. + void GetJsonAsync(const std::string& uri, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback = NULL, + Orthanc::IDynamicObject* payload = NULL /* takes ownership */); + + // schedule a GET request expecting a binary response. + void GetBinaryAsync(const std::string& uri, + const std::string& contentType, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback = NULL, + Orthanc::IDynamicObject* payload = NULL /* takes ownership */); + + // schedule a GET request expecting a binary response. + void GetBinaryAsync(const std::string& uri, + const IWebService::HttpHeaders& headers, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback = NULL, + Orthanc::IDynamicObject* payload = NULL /* takes ownership */); + + // schedule a POST request expecting a JSON response. + void PostBinaryAsyncExpectJson(const std::string& uri, + const std::string& body, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback = NULL, + Orthanc::IDynamicObject* payload = NULL /* takes ownership */); + + // schedule a POST request expecting a JSON response. + void PostJsonAsyncExpectJson(const std::string& uri, + const Json::Value& data, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback = NULL, + Orthanc::IDynamicObject* payload = NULL /* takes ownership */); + + // schedule a POST request and don't mind the response. + void PostJsonAsync(const std::string& uri, + const Json::Value& data); + + // schedule a POST request and don't expect any response. + void PostJsonAsync(const std::string& uri, + const Json::Value& data, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback = NULL, + Orthanc::IDynamicObject* payload = NULL /* takes ownership */); + + + // schedule a POST request and don't mind the response. + void PostBinaryAsync(const std::string& uri, + const std::string& body); + + // schedule a POST request and don't expect any response. + void PostBinaryAsync(const std::string& uri, + const std::string& body, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback = NULL, + Orthanc::IDynamicObject* payload = NULL /* takes ownership */); + + // schedule a DELETE request expecting an empty response. + void DeleteAsync(const std::string& uri, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback = NULL, + Orthanc::IDynamicObject* payload = NULL /* takes ownership */); + + void NotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message); + + void NotifyHttpError(const IWebService::HttpRequestErrorMessage& message); + + private: + void HandleFromCache(const std::string& uri, + const IWebService::HttpHeaders& headers, + Orthanc::IDynamicObject* payload /* takes ownership */); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Toolbox/OrthancSlicesLoader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Toolbox/OrthancSlicesLoader.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,904 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "OrthancSlicesLoader.h" + +#include "../../Toolbox/MessagingToolbox.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + + +/** + * TODO This is a SLOW implementation of base64 decoding, because + * "Orthanc::Toolbox::DecodeBase64()" does not work properly with + * WASM. UNDERSTAND WHY. + * https://stackoverflow.com/a/34571089/881731 + **/ +static std::string base64_decode(const std::string &in) +{ + std::string out; + + std::vector T(256,-1); + for (int i=0; i<64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i; + + int val=0, valb=-8; + for (size_t i = 0; i < in.size(); i++) { + unsigned char c = in[i]; + if (T[c] == -1) break; + val = (val<<6) + T[c]; + valb += 6; + if (valb>=0) { + out.push_back(char((val>>valb)&0xFF)); + valb-=8; + } + } + return out; +} + + + +namespace Deprecated +{ + class OrthancSlicesLoader::Operation : public Orthanc::IDynamicObject + { + private: + Mode mode_; + unsigned int frame_; + unsigned int sliceIndex_; + const Slice* slice_; + std::string instanceId_; + SliceImageQuality quality_; + + Operation(Mode mode) : + mode_(mode) + { + } + + public: + Mode GetMode() const + { + return mode_; + } + + SliceImageQuality GetQuality() const + { + assert(mode_ == Mode_LoadImage || + mode_ == Mode_LoadRawImage); + return quality_; + } + + unsigned int GetSliceIndex() const + { + assert(mode_ == Mode_LoadImage || + mode_ == Mode_LoadRawImage); + return sliceIndex_; + } + + const Slice& GetSlice() const + { + assert(mode_ == Mode_LoadImage || + mode_ == Mode_LoadRawImage); + assert(slice_ != NULL); + return *slice_; + } + + unsigned int GetFrame() const + { + assert(mode_ == Mode_FrameGeometry); + return frame_; + } + + const std::string& GetInstanceId() const + { + assert(mode_ == Mode_FrameGeometry || + mode_ == Mode_InstanceGeometry); + return instanceId_; + } + + static Operation* DownloadInstanceGeometry(const std::string& instanceId) + { + std::auto_ptr operation(new Operation(Mode_InstanceGeometry)); + operation->instanceId_ = instanceId; + return operation.release(); + } + + static Operation* DownloadFrameGeometry(const std::string& instanceId, + unsigned int frame) + { + std::auto_ptr operation(new Operation(Mode_FrameGeometry)); + operation->instanceId_ = instanceId; + operation->frame_ = frame; + return operation.release(); + } + + static Operation* DownloadSliceImage(unsigned int sliceIndex, + const Slice& slice, + SliceImageQuality quality) + { + std::auto_ptr tmp(new Operation(Mode_LoadImage)); + tmp->sliceIndex_ = sliceIndex; + tmp->slice_ = &slice; + tmp->quality_ = quality; + return tmp.release(); + } + + static Operation* DownloadSliceRawImage(unsigned int sliceIndex, + const Slice& slice) + { + std::auto_ptr tmp(new Operation(Mode_LoadRawImage)); + tmp->sliceIndex_ = sliceIndex; + tmp->slice_ = &slice; + tmp->quality_ = SliceImageQuality_InternalRaw; + return tmp.release(); + } + + static Operation* DownloadDicomFile(const Slice& slice) + { + std::auto_ptr tmp(new Operation(Mode_LoadDicomFile)); + tmp->slice_ = &slice; + return tmp.release(); + } + + }; + + void OrthancSlicesLoader::NotifySliceImageSuccess(const Operation& operation, + const Orthanc::ImageAccessor& image) + { + OrthancSlicesLoader::SliceImageReadyMessage msg + (*this, operation.GetSliceIndex(), operation.GetSlice(), image, operation.GetQuality()); + BroadcastMessage(msg); + } + + + void OrthancSlicesLoader::NotifySliceImageError(const Operation& operation) + { + OrthancSlicesLoader::SliceImageErrorMessage msg + (*this, operation.GetSliceIndex(), operation.GetSlice(), operation.GetQuality()); + BroadcastMessage(msg); + } + + + void OrthancSlicesLoader::SortAndFinalizeSlices() + { + bool ok = slices_.Sort(); + + state_ = State_GeometryReady; + + if (ok) + { + LOG(INFO) << "Loaded a series with " << slices_.GetSlicesCount() << " slice(s)"; + BroadcastMessage(SliceGeometryReadyMessage(*this)); + } + else + { + LOG(ERROR) << "This series is empty"; + BroadcastMessage(SliceGeometryErrorMessage(*this)); + } + } + + void OrthancSlicesLoader::OnGeometryError(const IWebService::HttpRequestErrorMessage& message) + { + BroadcastMessage(SliceGeometryErrorMessage(*this)); + state_ = State_Error; + } + + void OrthancSlicesLoader::OnSliceImageError(const IWebService::HttpRequestErrorMessage& message) + { + NotifySliceImageError(dynamic_cast(message.GetPayload())); + state_ = State_Error; + } + + void OrthancSlicesLoader::ParseSeriesGeometry(const OrthancApiClient::JsonResponseReadyMessage& message) + { + const Json::Value& series = message.GetJson(); + Json::Value::Members instances = series.getMemberNames(); + + slices_.Reserve(instances.size()); + + for (size_t i = 0; i < instances.size(); i++) + { + OrthancPlugins::FullOrthancDataset dataset(series[instances[i]]); + + Orthanc::DicomMap dicom; + OrthancStone::MessagingToolbox::ConvertDataset(dicom, dataset); + + unsigned int frames; + if (!dicom.ParseUnsignedInteger32(frames, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES)) + { + frames = 1; + } + + for (unsigned int frame = 0; frame < frames; frame++) + { + std::auto_ptr slice(new Slice); + if (slice->ParseOrthancFrame(dicom, instances[i], frame)) + { + OrthancStone::CoordinateSystem3D geometry = slice->GetGeometry(); + slices_.AddSlice(geometry, slice.release()); + } + else + { + LOG(WARNING) << "Skipping invalid frame " << frame << " within instance " << instances[i]; + } + } + } + + SortAndFinalizeSlices(); + } + + void OrthancSlicesLoader::ParseInstanceGeometry(const OrthancApiClient::JsonResponseReadyMessage& message) + { + const Json::Value& tags = message.GetJson(); + const std::string& instanceId = dynamic_cast(message.GetPayload()).GetInstanceId(); + + OrthancPlugins::FullOrthancDataset dataset(tags); + + Orthanc::DicomMap dicom; + OrthancStone::MessagingToolbox::ConvertDataset(dicom, dataset); + + unsigned int frames; + if (!dicom.ParseUnsignedInteger32(frames, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES)) + { + frames = 1; + } + + LOG(INFO) << "Instance " << instanceId << " contains " << frames << " frame(s)"; + + for (unsigned int frame = 0; frame < frames; frame++) + { + std::auto_ptr slice(new Slice); + if (slice->ParseOrthancFrame(dicom, instanceId, frame)) + { + OrthancStone::CoordinateSystem3D geometry = slice->GetGeometry(); + slices_.AddSlice(geometry, slice.release()); + } + else + { + LOG(WARNING) << "Skipping invalid multi-frame instance " << instanceId; + BroadcastMessage(SliceGeometryErrorMessage(*this)); + return; + } + } + + SortAndFinalizeSlices(); + } + + + void OrthancSlicesLoader::ParseFrameGeometry(const OrthancApiClient::JsonResponseReadyMessage& message) + { + const Json::Value& tags = message.GetJson(); + const std::string& instanceId = dynamic_cast(message.GetPayload()).GetInstanceId(); + unsigned int frame = dynamic_cast(message.GetPayload()).GetFrame(); + + OrthancPlugins::FullOrthancDataset dataset(tags); + + state_ = State_GeometryReady; + + Orthanc::DicomMap dicom; + OrthancStone::MessagingToolbox::ConvertDataset(dicom, dataset); + + std::auto_ptr slice(new Slice); + if (slice->ParseOrthancFrame(dicom, instanceId, frame)) + { + LOG(INFO) << "Loaded instance geometry " << instanceId; + + OrthancStone::CoordinateSystem3D geometry = slice->GetGeometry(); + slices_.AddSlice(geometry, slice.release()); + + BroadcastMessage(SliceGeometryReadyMessage(*this)); + } + else + { + LOG(WARNING) << "Skipping invalid instance " << instanceId; + BroadcastMessage(SliceGeometryErrorMessage(*this)); + } + } + + + void OrthancSlicesLoader::ParseSliceImagePng(const OrthancApiClient::BinaryResponseReadyMessage& message) + { + const Operation& operation = dynamic_cast(message.GetPayload()); + std::auto_ptr image; + + try + { + image.reset(new Orthanc::PngReader); + dynamic_cast(*image).ReadFromMemory(message.GetAnswer(), message.GetAnswerSize()); + } + catch (Orthanc::OrthancException&) + { + NotifySliceImageError(operation); + return; + } + + if (image->GetWidth() != operation.GetSlice().GetWidth() || + image->GetHeight() != operation.GetSlice().GetHeight()) + { + NotifySliceImageError(operation); + return; + } + + if (operation.GetSlice().GetConverter().GetExpectedPixelFormat() == + Orthanc::PixelFormat_SignedGrayscale16) + { + if (image->GetFormat() == Orthanc::PixelFormat_Grayscale16) + { + image->SetFormat(Orthanc::PixelFormat_SignedGrayscale16); + } + else + { + NotifySliceImageError(operation); + return; + } + } + + NotifySliceImageSuccess(operation, *image); + } + + void OrthancSlicesLoader::ParseSliceImagePam(const OrthancApiClient::BinaryResponseReadyMessage& message) + { + const Operation& operation = dynamic_cast(message.GetPayload()); + std::auto_ptr image; + + try + { + image.reset(new Orthanc::PamReader); + dynamic_cast(*image).ReadFromMemory(message.GetAnswer(), message.GetAnswerSize()); + } + catch (Orthanc::OrthancException&) + { + NotifySliceImageError(operation); + return; + } + + if (image->GetWidth() != operation.GetSlice().GetWidth() || + image->GetHeight() != operation.GetSlice().GetHeight()) + { + NotifySliceImageError(operation); + return; + } + + if (operation.GetSlice().GetConverter().GetExpectedPixelFormat() == + Orthanc::PixelFormat_SignedGrayscale16) + { + if (image->GetFormat() == Orthanc::PixelFormat_Grayscale16) + { + image->SetFormat(Orthanc::PixelFormat_SignedGrayscale16); + } + else + { + NotifySliceImageError(operation); + return; + } + } + + NotifySliceImageSuccess(operation, *image); + } + + + void OrthancSlicesLoader::ParseSliceImageJpeg(const OrthancApiClient::JsonResponseReadyMessage& message) + { + const Operation& operation = dynamic_cast(message.GetPayload()); + + const Json::Value& encoded = message.GetJson(); + if (encoded.type() != Json::objectValue || + !encoded.isMember("Orthanc") || + encoded["Orthanc"].type() != Json::objectValue) + { + NotifySliceImageError(operation); + return; + } + + const Json::Value& info = encoded["Orthanc"]; + if (!info.isMember("PixelData") || + !info.isMember("Stretched") || + !info.isMember("Compression") || + info["Compression"].type() != Json::stringValue || + info["PixelData"].type() != Json::stringValue || + info["Stretched"].type() != Json::booleanValue || + info["Compression"].asString() != "Jpeg") + { + NotifySliceImageError(operation); + return; + } + + bool isSigned = false; + bool isStretched = info["Stretched"].asBool(); + + if (info.isMember("IsSigned")) + { + if (info["IsSigned"].type() != Json::booleanValue) + { + NotifySliceImageError(operation); + return; + } + else + { + isSigned = info["IsSigned"].asBool(); + } + } + + std::auto_ptr reader; + + { + std::string jpeg; + //Orthanc::Toolbox::DecodeBase64(jpeg, info["PixelData"].asString()); + jpeg = base64_decode(info["PixelData"].asString()); + + try + { + reader.reset(new Orthanc::JpegReader); + dynamic_cast(*reader).ReadFromMemory(jpeg); + } + catch (Orthanc::OrthancException&) + { + NotifySliceImageError(operation); + return; + } + } + + Orthanc::PixelFormat expectedFormat = + operation.GetSlice().GetConverter().GetExpectedPixelFormat(); + + if (reader->GetFormat() == Orthanc::PixelFormat_RGB24) // This is a color image + { + if (expectedFormat != Orthanc::PixelFormat_RGB24) + { + NotifySliceImageError(operation); + return; + } + + if (isSigned || isStretched) + { + NotifySliceImageError(operation); + return; + } + else + { + NotifySliceImageSuccess(operation, *reader); + return; + } + } + + if (reader->GetFormat() != Orthanc::PixelFormat_Grayscale8) + { + NotifySliceImageError(operation); + return; + } + + if (!isStretched) + { + if (expectedFormat != reader->GetFormat()) + { + NotifySliceImageError(operation); + return; + } + else + { + NotifySliceImageSuccess(operation, *reader); + return; + } + } + + int32_t stretchLow = 0; + int32_t stretchHigh = 0; + + if (!info.isMember("StretchLow") || + !info.isMember("StretchHigh") || + info["StretchLow"].type() != Json::intValue || + info["StretchHigh"].type() != Json::intValue) + { + NotifySliceImageError(operation); + return; + } + + stretchLow = info["StretchLow"].asInt(); + stretchHigh = info["StretchHigh"].asInt(); + + if (stretchLow < -32768 || + stretchHigh > 65535 || + (stretchLow < 0 && stretchHigh > 32767)) + { + // This range cannot be represented with a uint16_t or an int16_t + NotifySliceImageError(operation); + return; + } + + // Decode a grayscale JPEG 8bpp image coming from the Web viewer + std::auto_ptr image + (new Orthanc::Image(expectedFormat, reader->GetWidth(), reader->GetHeight(), false)); + + Orthanc::ImageProcessing::Convert(*image, *reader); + reader.reset(); + + float scaling = static_cast(stretchHigh - stretchLow) / 255.0f; + + if (!OrthancStone::LinearAlgebra::IsCloseToZero(scaling)) + { + float offset = static_cast(stretchLow) / scaling; + Orthanc::ImageProcessing::ShiftScale(*image, offset, scaling, true); + } + + NotifySliceImageSuccess(operation, *image); + } + + + class StringImage : public Orthanc::ImageAccessor + { + private: + std::string buffer_; + + public: + StringImage(Orthanc::PixelFormat format, + unsigned int width, + unsigned int height, + std::string& buffer) + { + if (buffer.size() != Orthanc::GetBytesPerPixel(format) * width * height) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + + buffer_.swap(buffer); // The source buffer is now empty + + void* data = (buffer_.empty() ? NULL : &buffer_[0]); + + AssignWritable(format, width, height, + Orthanc::GetBytesPerPixel(format) * width, data); + } + }; + + void OrthancSlicesLoader::ParseSliceRawImage(const OrthancApiClient::BinaryResponseReadyMessage& message) + { + const Operation& operation = dynamic_cast(message.GetPayload()); + Orthanc::GzipCompressor compressor; + + std::string raw; + compressor.Uncompress(raw, message.GetAnswer(), message.GetAnswerSize()); + + const Orthanc::DicomImageInformation& info = operation.GetSlice().GetImageInformation(); + + if (info.GetBitsAllocated() == 32 && + info.GetBitsStored() == 32 && + info.GetHighBit() == 31 && + info.GetChannelCount() == 1 && + !info.IsSigned() && + info.GetPhotometricInterpretation() == Orthanc::PhotometricInterpretation_Monochrome2 && + raw.size() == info.GetWidth() * info.GetHeight() * 4) + { + // This is the case of RT-DOSE (uint32_t values) + + std::auto_ptr image + (new StringImage(Orthanc::PixelFormat_Grayscale32, info.GetWidth(), + info.GetHeight(), raw)); + + // TODO - Only for big endian + for (unsigned int y = 0; y < image->GetHeight(); y++) + { + uint32_t *p = reinterpret_cast(image->GetRow(y)); + for (unsigned int x = 0; x < image->GetWidth(); x++, p++) + { + *p = le32toh(*p); + } + } + + NotifySliceImageSuccess(operation, *image); + } + else if (info.GetBitsAllocated() == 16 && + info.GetBitsStored() == 16 && + info.GetHighBit() == 15 && + info.GetChannelCount() == 1 && + !info.IsSigned() && + info.GetPhotometricInterpretation() == Orthanc::PhotometricInterpretation_Monochrome2 && + raw.size() == info.GetWidth() * info.GetHeight() * 2) + { + std::auto_ptr image + (new StringImage(Orthanc::PixelFormat_Grayscale16, info.GetWidth(), + info.GetHeight(), raw)); + + // TODO - Big endian ? + + NotifySliceImageSuccess(operation, *image); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + } + + + OrthancSlicesLoader::OrthancSlicesLoader(OrthancStone::MessageBroker& broker, + OrthancApiClient& orthanc) : + OrthancStone::IObservable(broker), + OrthancStone::IObserver(broker), + orthanc_(orthanc), + state_(State_Initialization) + { + } + + + void OrthancSlicesLoader::ScheduleLoadSeries(const std::string& seriesId) + { + if (state_ != State_Initialization) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + state_ = State_LoadingGeometry; + orthanc_.GetJsonAsync("/series/" + seriesId + "/instances-tags", + new OrthancStone::Callable(*this, &OrthancSlicesLoader::ParseSeriesGeometry), + new OrthancStone::Callable(*this, &OrthancSlicesLoader::OnGeometryError), + NULL); + } + } + + void OrthancSlicesLoader::ScheduleLoadInstance(const std::string& instanceId) + { + if (state_ != State_Initialization) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + state_ = State_LoadingGeometry; + + // Tag "3004-000c" is "Grid Frame Offset Vector", which is + // mandatory to read RT DOSE, but is too long to be returned by default + orthanc_.GetJsonAsync("/instances/" + instanceId + "/tags?ignore-length=3004-000c", + new OrthancStone::Callable(*this, &OrthancSlicesLoader::ParseInstanceGeometry), + new OrthancStone::Callable(*this, &OrthancSlicesLoader::OnGeometryError), + Operation::DownloadInstanceGeometry(instanceId)); + } + } + + + void OrthancSlicesLoader::ScheduleLoadFrame(const std::string& instanceId, + unsigned int frame) + { + if (state_ != State_Initialization) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + state_ = State_LoadingGeometry; + + orthanc_.GetJsonAsync("/instances/" + instanceId + "/tags", + new OrthancStone::Callable(*this, &OrthancSlicesLoader::ParseFrameGeometry), + new OrthancStone::Callable(*this, &OrthancSlicesLoader::OnGeometryError), + Operation::DownloadFrameGeometry(instanceId, frame)); + } + } + + + bool OrthancSlicesLoader::IsGeometryReady() const + { + return state_ == State_GeometryReady; + } + + + size_t OrthancSlicesLoader::GetSlicesCount() const + { + if (state_ != State_GeometryReady) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + return slices_.GetSlicesCount(); + } + + + const Slice& OrthancSlicesLoader::GetSlice(size_t index) const + { + if (state_ != State_GeometryReady) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + return dynamic_cast(slices_.GetSlicePayload(index)); + } + + + bool OrthancSlicesLoader::LookupSlice(size_t& index, + const OrthancStone::CoordinateSystem3D& plane) const + { + if (state_ != State_GeometryReady) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + double distance; + return (slices_.LookupClosestSlice(index, distance, plane) && + distance <= GetSlice(index).GetThickness() / 2.0); + } + + + void OrthancSlicesLoader::ScheduleSliceImagePng(const Slice& slice, + size_t index) + { + std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + + boost::lexical_cast(slice.GetFrame())); + + switch (slice.GetConverter().GetExpectedPixelFormat()) + { + case Orthanc::PixelFormat_RGB24: + uri += "/preview"; + break; + + case Orthanc::PixelFormat_Grayscale16: + uri += "/image-uint16"; + break; + + case Orthanc::PixelFormat_SignedGrayscale16: + uri += "/image-int16"; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + orthanc_.GetBinaryAsync(uri, "image/png", + new OrthancStone::Callable + (*this, &OrthancSlicesLoader::ParseSliceImagePng), + new OrthancStone::Callable + (*this, &OrthancSlicesLoader::OnSliceImageError), + Operation::DownloadSliceImage( + static_cast(index), slice, SliceImageQuality_FullPng)); +} + + void OrthancSlicesLoader::ScheduleSliceImagePam(const Slice& slice, + size_t index) + { + std::string uri = + ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + + boost::lexical_cast(slice.GetFrame())); + + switch (slice.GetConverter().GetExpectedPixelFormat()) + { + case Orthanc::PixelFormat_RGB24: + uri += "/preview"; + break; + + case Orthanc::PixelFormat_Grayscale16: + uri += "/image-uint16"; + break; + + case Orthanc::PixelFormat_SignedGrayscale16: + uri += "/image-int16"; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + orthanc_.GetBinaryAsync(uri, "image/x-portable-arbitrarymap", + new OrthancStone::Callable + (*this, &OrthancSlicesLoader::ParseSliceImagePam), + new OrthancStone::Callable + (*this, &OrthancSlicesLoader::OnSliceImageError), + Operation::DownloadSliceImage(static_cast(index), + slice, SliceImageQuality_FullPam)); + } + + + + void OrthancSlicesLoader::ScheduleSliceImageJpeg(const Slice& slice, + size_t index, + SliceImageQuality quality) + { + unsigned int value; + + switch (quality) + { + case SliceImageQuality_Jpeg50: + value = 50; + break; + + case SliceImageQuality_Jpeg90: + value = 90; + break; + + case SliceImageQuality_Jpeg95: + value = 95; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + // This requires the official Web viewer plugin to be installed! + std::string uri = ("/web-viewer/instances/jpeg" + + boost::lexical_cast(value) + + "-" + slice.GetOrthancInstanceId() + "_" + + boost::lexical_cast(slice.GetFrame())); + + orthanc_.GetJsonAsync(uri, + new OrthancStone::Callable + (*this, &OrthancSlicesLoader::ParseSliceImageJpeg), + new OrthancStone::Callable + (*this, &OrthancSlicesLoader::OnSliceImageError), + Operation::DownloadSliceImage( + static_cast(index), slice, quality)); + } + + + + void OrthancSlicesLoader::ScheduleLoadSliceImage(size_t index, + SliceImageQuality quality) + { + if (state_ != State_GeometryReady) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + const Slice& slice = GetSlice(index); + + if (slice.HasOrthancDecoding()) + { + switch (quality) + { + case SliceImageQuality_FullPng: + ScheduleSliceImagePng(slice, index); + break; + case SliceImageQuality_FullPam: + ScheduleSliceImagePam(slice, index); + break; + default: + ScheduleSliceImageJpeg(slice, index, quality); + } + } + else + { + std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + + boost::lexical_cast(slice.GetFrame()) + "/raw.gz"); + orthanc_.GetBinaryAsync(uri, IWebService::HttpHeaders(), + new OrthancStone::Callable + (*this, &OrthancSlicesLoader::ParseSliceRawImage), + new OrthancStone::Callable + (*this, &OrthancSlicesLoader::OnSliceImageError), + Operation::DownloadSliceRawImage( + static_cast(index), slice)); + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Toolbox/OrthancSlicesLoader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Toolbox/OrthancSlicesLoader.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,209 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../../Messages/IObservable.h" +#include "../../StoneEnumerations.h" +#include "../../Toolbox/SlicesSorter.h" +#include "IWebService.h" +#include "OrthancApiClient.h" +#include "Slice.h" + +#include + + +namespace Deprecated +{ + class OrthancSlicesLoader : public OrthancStone::IObservable, public OrthancStone::IObserver + { + public: + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, SliceGeometryReadyMessage, OrthancSlicesLoader); + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, SliceGeometryErrorMessage, OrthancSlicesLoader); + + + class SliceImageReadyMessage : public OrthancStone::OriginMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + unsigned int sliceIndex_; + const Slice& slice_; + const Orthanc::ImageAccessor& image_; + SliceImageQuality effectiveQuality_; + + public: + SliceImageReadyMessage(const OrthancSlicesLoader& origin, + unsigned int sliceIndex, + const Slice& slice, + const Orthanc::ImageAccessor& image, + SliceImageQuality effectiveQuality) : + OriginMessage(origin), + sliceIndex_(sliceIndex), + slice_(slice), + image_(image), + effectiveQuality_(effectiveQuality) + { + } + + unsigned int GetSliceIndex() const + { + return sliceIndex_; + } + + const Slice& GetSlice() const + { + return slice_; + } + + const Orthanc::ImageAccessor& GetImage() const + { + return image_; + } + + SliceImageQuality GetEffectiveQuality() const + { + return effectiveQuality_; + } + }; + + + class SliceImageErrorMessage : public OrthancStone::OriginMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + const Slice& slice_; + unsigned int sliceIndex_; + SliceImageQuality effectiveQuality_; + + public: + SliceImageErrorMessage(const OrthancSlicesLoader& origin, + unsigned int sliceIndex, + const Slice& slice, + SliceImageQuality effectiveQuality) : + OriginMessage(origin), + slice_(slice), + sliceIndex_(sliceIndex), + effectiveQuality_(effectiveQuality) + { + } + unsigned int GetSliceIndex() const + { + return sliceIndex_; + } + + const Slice& GetSlice() const + { + return slice_; + } + + SliceImageQuality GetEffectiveQuality() const + { + return effectiveQuality_; + } + }; + + private: + enum State + { + State_Error, + State_Initialization, + State_LoadingGeometry, + State_GeometryReady + }; + + enum Mode + { + Mode_SeriesGeometry, + Mode_InstanceGeometry, + Mode_FrameGeometry, + Mode_LoadImage, + Mode_LoadRawImage, + Mode_LoadDicomFile + }; + + class Operation; + + OrthancApiClient& orthanc_; + State state_; + OrthancStone::SlicesSorter slices_; + + void NotifySliceImageSuccess(const Operation& operation, + const Orthanc::ImageAccessor& image); + + void NotifySliceImageError(const Operation& operation); + + void OnGeometryError(const IWebService::HttpRequestErrorMessage& message); + + void OnSliceImageError(const IWebService::HttpRequestErrorMessage& message); + + void ParseSeriesGeometry(const OrthancApiClient::JsonResponseReadyMessage& message); + + void ParseInstanceGeometry(const OrthancApiClient::JsonResponseReadyMessage& message); + + void ParseFrameGeometry(const OrthancApiClient::JsonResponseReadyMessage& message); + + void ParseSliceImagePng(const OrthancApiClient::BinaryResponseReadyMessage& message); + + void ParseSliceImagePam(const OrthancApiClient::BinaryResponseReadyMessage& message); + + void ParseSliceImageJpeg(const OrthancApiClient::JsonResponseReadyMessage& message); + + void ParseSliceRawImage(const OrthancApiClient::BinaryResponseReadyMessage& message); + + void ScheduleSliceImagePng(const Slice& slice, + size_t index); + + void ScheduleSliceImagePam(const Slice& slice, + size_t index); + + void ScheduleSliceImageJpeg(const Slice& slice, + size_t index, + SliceImageQuality quality); + + void SortAndFinalizeSlices(); + + public: + OrthancSlicesLoader(OrthancStone::MessageBroker& broker, + //ISliceLoaderObserver& callback, + OrthancApiClient& orthancApi); + + void ScheduleLoadSeries(const std::string& seriesId); + + void ScheduleLoadInstance(const std::string& instanceId); + + void ScheduleLoadFrame(const std::string& instanceId, + unsigned int frame); + + bool IsGeometryReady() const; + + size_t GetSlicesCount() const; + + const Slice& GetSlice(size_t index) const; + + bool LookupSlice(size_t& index, + const OrthancStone::CoordinateSystem3D& plane) const; + + void ScheduleLoadSliceImage(size_t index, + SliceImageQuality requestedQuality); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Toolbox/Slice.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Toolbox/Slice.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,367 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "Slice.h" + +#include "../../StoneEnumerations.h" +#include "../../Toolbox/GeometryToolbox.h" + +#include +#include +#include + +#include + +namespace Deprecated +{ + static bool ParseDouble(double& target, + const std::string& source) + { + try + { + target = boost::lexical_cast(source); + return true; + } + catch (boost::bad_lexical_cast&) + { + return false; + } + } + + Slice* Slice::Clone() const + { + std::auto_ptr target(new Slice()); + + target->type_ = type_; + target->orthancInstanceId_ = orthancInstanceId_; + target->sopClassUid_ = sopClassUid_; + target->frame_ = frame_; + target->frameCount_ = frameCount_; + target->geometry_ = geometry_; + target->pixelSpacingX_ = pixelSpacingX_; + target->pixelSpacingY_ = pixelSpacingY_; + target->thickness_ = thickness_; + target->width_ = width_; + target->height_ = height_; + target->converter_ = converter_; + if (imageInformation_.get() != NULL) + target->imageInformation_.reset(imageInformation_->Clone()); + + return target.release(); + } + + bool Slice::ComputeRTDoseGeometry(const Orthanc::DicomMap& dataset, + unsigned int frame) + { + // http://dicom.nema.org/medical/Dicom/2016a/output/chtml/part03/sect_C.8.8.3.2.html + + { + std::string increment; + + if (dataset.CopyToString(increment, Orthanc::DICOM_TAG_FRAME_INCREMENT_POINTER, false)) + { + Orthanc::Toolbox::ToUpperCase(increment); + if (increment != "3004,000C") // This is the "Grid Frame Offset Vector" tag + { + LOG(ERROR) << "Bad value for the \"FrameIncrementPointer\" tag"; + return false; + } + } + } + + std::string offsetTag; + + if (!dataset.CopyToString(offsetTag, Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR, false) || + offsetTag.empty()) + { + LOG(ERROR) << "Cannot read the \"GridFrameOffsetVector\" tag, check you are using Orthanc >= 1.3.1"; + return false; + } + + std::vector offsets; + Orthanc::Toolbox::TokenizeString(offsets, offsetTag, '\\'); + + if (frameCount_ <= 1 || + offsets.size() < frameCount_ || + offsets.size() < 2 || + frame >= frameCount_) + { + LOG(ERROR) << "No information about the 3D location of some slice(s) in a RT DOSE"; + return false; + } + + double offset0, offset1, z; + + if (!ParseDouble(offset0, offsets[0]) || + !ParseDouble(offset1, offsets[1]) || + !ParseDouble(z, offsets[frame])) + { + LOG(ERROR) << "Invalid syntax"; + return false; + } + + if (!OrthancStone::LinearAlgebra::IsCloseToZero(offset0)) + { + LOG(ERROR) << "Invalid syntax"; + return false; + } + + geometry_ = OrthancStone::CoordinateSystem3D(geometry_.GetOrigin() + z * geometry_.GetNormal(), + //+ 650 * geometry_.GetAxisX(), + geometry_.GetAxisX(), + geometry_.GetAxisY()); + + thickness_ = offset1 - offset0; + if (thickness_ < 0) + { + thickness_ = -thickness_; + } + + return true; + } + + + bool Slice::ParseOrthancFrame(const Orthanc::DicomMap& dataset, + const std::string& instanceId, + unsigned int frame) + { + orthancInstanceId_ = instanceId; + frame_ = frame; + type_ = Type_OrthancDecodableFrame; + imageInformation_.reset(new Orthanc::DicomImageInformation(dataset)); + + if (!dataset.CopyToString(sopClassUid_, Orthanc::DICOM_TAG_SOP_CLASS_UID, false) || + sopClassUid_.empty()) + { + LOG(ERROR) << "Instance without a SOP class UID"; + return false; + } + + if (!dataset.ParseUnsignedInteger32(frameCount_, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES)) + { + frameCount_ = 1; // Assume instance with one frame + } + + if (frame >= frameCount_) + { + return false; + } + + if (!dataset.ParseUnsignedInteger32(width_, Orthanc::DICOM_TAG_COLUMNS) || + !dataset.ParseUnsignedInteger32(height_, Orthanc::DICOM_TAG_ROWS)) + { + return false; + } + + thickness_ = 100.0 * std::numeric_limits::epsilon(); + + std::string tmp; + if (dataset.CopyToString(tmp, Orthanc::DICOM_TAG_SLICE_THICKNESS, false)) + { + if (!tmp.empty() && + !ParseDouble(thickness_, tmp)) + { + return false; // Syntax error + } + } + + converter_.ReadParameters(dataset); + + OrthancStone::GeometryToolbox::GetPixelSpacing(pixelSpacingX_, pixelSpacingY_, dataset); + + std::string position, orientation; + if (dataset.CopyToString(position, Orthanc::DICOM_TAG_IMAGE_POSITION_PATIENT, false) && + dataset.CopyToString(orientation, Orthanc::DICOM_TAG_IMAGE_ORIENTATION_PATIENT, false)) + { + geometry_ = OrthancStone::CoordinateSystem3D(position, orientation); + + bool ok = true; + + switch (OrthancStone::StringToSopClassUid(sopClassUid_)) + { + case OrthancStone::SopClassUid_RTDose: + type_ = Type_OrthancRawFrame; + ok = ComputeRTDoseGeometry(dataset, frame); + break; + + default: + break; + } + + if (!ok) + { + LOG(ERROR) << "Cannot deduce the 3D location of frame " << frame + << " in instance " << instanceId << ", whose SOP class UID is: " << sopClassUid_; + return false; + } + } + + return true; + } + + + const std::string Slice::GetOrthancInstanceId() const + { + if (type_ == Type_OrthancDecodableFrame || + type_ == Type_OrthancRawFrame) + { + return orthancInstanceId_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + + unsigned int Slice::GetFrame() const + { + if (type_ == Type_Invalid) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + return frame_; + } + + + const OrthancStone::CoordinateSystem3D& Slice::GetGeometry() const + { + if (type_ == Type_Invalid) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + return geometry_; + } + + + double Slice::GetThickness() const + { + if (type_ == Type_Invalid) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + return thickness_; + } + + + double Slice::GetPixelSpacingX() const + { + if (type_ == Type_Invalid) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + return pixelSpacingX_; + } + + + double Slice::GetPixelSpacingY() const + { + if (type_ == Type_Invalid) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + return pixelSpacingY_; + } + + + unsigned int Slice::GetWidth() const + { + if (type_ == Type_Invalid) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + return width_; + } + + + unsigned int Slice::GetHeight() const + { + if (type_ == Type_Invalid) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + return height_; + } + + + const DicomFrameConverter& Slice::GetConverter() const + { + if (type_ == Type_Invalid) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + return converter_; + } + + + bool Slice::ContainsPlane(const OrthancStone::CoordinateSystem3D& plane) const + { + if (type_ == Type_Invalid) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + bool opposite; + return (OrthancStone::GeometryToolbox::IsParallelOrOpposite(opposite, + GetGeometry().GetNormal(), + plane.GetNormal()) && + OrthancStone::LinearAlgebra::IsNear(GetGeometry().ProjectAlongNormal(GetGeometry().GetOrigin()), + GetGeometry().ProjectAlongNormal(plane.GetOrigin()), + thickness_ / 2.0)); + } + + + void Slice::GetExtent(std::vector& points) const + { + double sx = GetPixelSpacingX(); + double sy = GetPixelSpacingY(); + double w = static_cast(GetWidth()); + double h = static_cast(GetHeight()); + + points.clear(); + points.push_back(GetGeometry().MapSliceToWorldCoordinates(-0.5 * sx, -0.5 * sy)); + points.push_back(GetGeometry().MapSliceToWorldCoordinates((w - 0.5) * sx, -0.5 * sy)); + points.push_back(GetGeometry().MapSliceToWorldCoordinates(-0.5 * sx, (h - 0.5) * sy)); + points.push_back(GetGeometry().MapSliceToWorldCoordinates((w - 0.5) * sx, (h - 0.5) * sy)); + } + + + const Orthanc::DicomImageInformation& Slice::GetImageInformation() const + { + if (imageInformation_.get() == NULL) + { + // Only available if constructing the "Slice" object with a DICOM map + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + return *imageInformation_; + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Toolbox/Slice.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Toolbox/Slice.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,155 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../../Toolbox/CoordinateSystem3D.h" +#include "DicomFrameConverter.h" + +#include +#include + +namespace Deprecated +{ + // TODO - Remove this class + class Slice : + public Orthanc::IDynamicObject /* to be used as a payload of SlicesSorter */ + { + private: + enum Type + { + Type_Invalid, + Type_Standalone, + Type_OrthancDecodableFrame, + Type_OrthancRawFrame + // TODO A slice could come from some DICOM file (URL) + }; + + bool ComputeRTDoseGeometry(const Orthanc::DicomMap& dataset, + unsigned int frame); + + Type type_; + std::string orthancInstanceId_; + std::string sopClassUid_; + unsigned int frame_; + unsigned int frameCount_; // TODO : Redundant with "imageInformation_" + OrthancStone::CoordinateSystem3D geometry_; + double pixelSpacingX_; + double pixelSpacingY_; + double thickness_; + unsigned int width_; // TODO : Redundant with "imageInformation_" + unsigned int height_; // TODO : Redundant with "imageInformation_" + DicomFrameConverter converter_; // TODO : Partially redundant with "imageInformation_" + + std::auto_ptr imageInformation_; + + public: + Slice() : + type_(Type_Invalid) + { + } + + + // this constructor is used to reference, i.e, a slice that is being loaded + Slice(const std::string& orthancInstanceId, + unsigned int frame) : + type_(Type_Invalid), + orthancInstanceId_(orthancInstanceId), + frame_(frame) + { + } + + // TODO Is this constructor the best way to go to tackle missing + // layers within SliceViewerWidget? + Slice(const OrthancStone::CoordinateSystem3D& plane, + double thickness) : + type_(Type_Standalone), + frame_(0), + frameCount_(0), + geometry_(plane), + pixelSpacingX_(1), + pixelSpacingY_(1), + thickness_(thickness), + width_(0), + height_(0) + { + } + + Slice(const OrthancStone::CoordinateSystem3D& plane, + double pixelSpacingX, + double pixelSpacingY, + double thickness, + unsigned int width, + unsigned int height, + const DicomFrameConverter& converter) : + type_(Type_Standalone), + frameCount_(1), + geometry_(plane), + pixelSpacingX_(pixelSpacingX), + pixelSpacingY_(pixelSpacingY), + thickness_(thickness), + width_(width), + height_(height), + converter_(converter) + { + } + + bool IsValid() const + { + return type_ != Type_Invalid; + } + + bool ParseOrthancFrame(const Orthanc::DicomMap& dataset, + const std::string& instanceId, + unsigned int frame); + + bool HasOrthancDecoding() const + { + return type_ == Type_OrthancDecodableFrame; + } + + const std::string GetOrthancInstanceId() const; + + unsigned int GetFrame() const; + + const OrthancStone::CoordinateSystem3D& GetGeometry() const; + + double GetThickness() const; + + double GetPixelSpacingX() const; + + double GetPixelSpacingY() const; + + unsigned int GetWidth() const; + + unsigned int GetHeight() const; + + const DicomFrameConverter& GetConverter() const; + + bool ContainsPlane(const OrthancStone::CoordinateSystem3D& plane) const; + + void GetExtent(std::vector& points) const; + + const Orthanc::DicomImageInformation& GetImageInformation() const; + + Slice* Clone() const; + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Toolbox/ViewportGeometry.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Toolbox/ViewportGeometry.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,217 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "ViewportGeometry.h" + +#include +#include + +#include + +namespace Deprecated +{ + void ViewportGeometry::ComputeTransform() + { + // The following lines must be read in reverse order! + cairo_matrix_t tmp; + + // Bring the center of the scene to the center of the view + cairo_matrix_init_translate(&transform_, + panX_ + static_cast(width_) / 2.0, + panY_ + static_cast(height_) / 2.0); + + // Apply the zoom around (0,0) + cairo_matrix_init_scale(&tmp, zoom_, zoom_); + cairo_matrix_multiply(&transform_, &tmp, &transform_); + + // Bring the center of the scene to (0,0) + cairo_matrix_init_translate(&tmp, + -(sceneExtent_.GetX1() + sceneExtent_.GetX2()) / 2.0, + -(sceneExtent_.GetY1() + sceneExtent_.GetY2()) / 2.0); + cairo_matrix_multiply(&transform_, &tmp, &transform_); + } + + + ViewportGeometry::ViewportGeometry() + { + width_ = 0; + height_ = 0; + + zoom_ = 1; + panX_ = 0; + panY_ = 0; + + ComputeTransform(); + } + + + void ViewportGeometry::SetDisplaySize(unsigned int width, + unsigned int height) + { + if (width_ != width || + height_ != height) + { + LOG(INFO) << "New display size: " << width << "x" << height; + + width_ = width; + height_ = height; + + ComputeTransform(); + } + } + + + void ViewportGeometry::SetSceneExtent(const OrthancStone::Extent2D& extent) + { + LOG(INFO) << "New scene extent: (" + << extent.GetX1() << "," << extent.GetY1() << ") => (" + << extent.GetX2() << "," << extent.GetY2() << ")"; + + sceneExtent_ = extent; + ComputeTransform(); + } + + + void ViewportGeometry::MapDisplayToScene(double& sceneX /* out */, + double& sceneY /* out */, + double x, + double y) const + { + cairo_matrix_t transform = transform_; + + if (cairo_matrix_invert(&transform) != CAIRO_STATUS_SUCCESS) + { + LOG(ERROR) << "Cannot invert singular matrix"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + sceneX = x; + sceneY = y; + cairo_matrix_transform_point(&transform, &sceneX, &sceneY); + } + + + void ViewportGeometry::MapSceneToDisplay(int& displayX /* out */, + int& displayY /* out */, + double x, + double y) const + { + cairo_matrix_transform_point(&transform_, &x, &y); + + displayX = static_cast(boost::math::iround(x)); + displayY = static_cast(boost::math::iround(y)); + } + + + void ViewportGeometry::MapPixelCenterToScene(std::vector& sceneTouches /* out */, + const std::vector& displayTouches) const + { + double sceneX, sceneY; + sceneTouches.clear(); + for (size_t t = 0; t < displayTouches.size(); t++) + { + MapPixelCenterToScene( + sceneX, + sceneY, + static_cast(displayTouches[t].x), + static_cast(displayTouches[t].y)); + + sceneTouches.push_back(Touch((float)sceneX, (float)sceneY)); + } + } + + void ViewportGeometry::MapPixelCenterToScene(double& sceneX, + double& sceneY, + int x, + int y) const + { + // Take the center of the pixel + MapDisplayToScene(sceneX, sceneY, + static_cast(x) + 0.5, + static_cast(y) + 0.5); + } + + + void ViewportGeometry::FitContent() + { + if (width_ > 0 && + height_ > 0 && + !sceneExtent_.IsEmpty()) + { + double zoomX = static_cast(width_) / (sceneExtent_.GetX2() - sceneExtent_.GetX1()); + double zoomY = static_cast(height_) / (sceneExtent_.GetY2() - sceneExtent_.GetY1()); + zoom_ = zoomX < zoomY ? zoomX : zoomY; + + panX_ = 0; + panY_ = 0; + + ComputeTransform(); + } + } + + + void ViewportGeometry::ApplyTransform(OrthancStone::CairoContext& context) const + { + cairo_set_matrix(context.GetObject(), &transform_); + } + + + void ViewportGeometry::GetPan(double& x, + double& y) const + { + x = panX_; + y = panY_; + } + + + void ViewportGeometry::SetPan(double x, + double y) + { + panX_ = x; + panY_ = y; + ComputeTransform(); + } + + + void ViewportGeometry::SetZoom(double zoom) + { + zoom_ = zoom; + ComputeTransform(); + } + + + OrthancStone::Matrix ViewportGeometry::GetMatrix() const + { + OrthancStone::Matrix m(3, 3); + + m(0, 0) = transform_.xx; + m(0, 1) = transform_.xy; + m(0, 2) = transform_.x0; + m(1, 0) = transform_.yx; + m(1, 1) = transform_.yy; + m(1, 2) = transform_.y0; + m(2, 0) = 0; + m(2, 1) = 0; + m(2, 2) = 1; + + return m; + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Toolbox/ViewportGeometry.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Toolbox/ViewportGeometry.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,110 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../../Viewport/CairoContext.h" +#include "../../Toolbox/Extent2D.h" +#include "../../Toolbox/LinearAlgebra.h" +#include "../Viewport/IMouseTracker.h" // to include "Touch" definition + +namespace Deprecated +{ + class ViewportGeometry + { + private: + // Extent of the scene (in world units) + OrthancStone::Extent2D sceneExtent_; + + // Size of the display (in pixels) + unsigned int width_; + unsigned int height_; + + // Zoom/pan + double zoom_; + double panX_; // In pixels (display units) + double panY_; + + cairo_matrix_t transform_; // Scene-to-display transformation + + void ComputeTransform(); + + public: + ViewportGeometry(); + + void SetDisplaySize(unsigned int width, + unsigned int height); + + void SetSceneExtent(const OrthancStone::Extent2D& extent); + + const OrthancStone::Extent2D& GetSceneExtent() const + { + return sceneExtent_; + } + + void MapDisplayToScene(double& sceneX /* out */, + double& sceneY /* out */, + double x, + double y) const; + + void MapPixelCenterToScene(double& sceneX /* out */, + double& sceneY /* out */, + int x, + int y) const; + + void MapPixelCenterToScene(std::vector& sceneTouches /* out */, + const std::vector& displayTouches) const; + + void MapSceneToDisplay(int& displayX /* out */, + int& displayY /* out */, + double x, + double y) const; + + unsigned int GetDisplayWidth() const + { + return width_; + } + + unsigned int GetDisplayHeight() const + { + return height_; + } + + double GetZoom() const + { + return zoom_; + } + + void FitContent(); + + void ApplyTransform(OrthancStone::CairoContext& context) const; + + void GetPan(double& x, + double& y) const; + + void SetPan(double x, + double y); + + void SetZoom(double zoom); + + OrthancStone::Matrix GetMatrix() const; + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Viewport/IMouseTracker.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Viewport/IMouseTracker.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,66 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../../Viewport/CairoSurface.h" +#include + +namespace Deprecated +{ + struct Touch + { + float x; + float y; + + Touch(float x, float y) + : x(x), + y(y) + { + } + Touch() + : x(0.0f), + y(0.0f) + { + } + }; + + + // this is tracking a mouse in screen coordinates/pixels unlike + // the IWorldSceneMouseTracker that is tracking a mouse + // in scene coordinates/mm. + class IMouseTracker : public boost::noncopyable + { + public: + virtual ~IMouseTracker() + { + } + + virtual void Render(Orthanc::ImageAccessor& surface) = 0; + + virtual void MouseUp() = 0; + + // Returns "true" iff. the background scene must be repainted + virtual void MouseMove(int x, + int y, + const std::vector& displayTouches) = 0; + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Viewport/IStatusBar.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Viewport/IStatusBar.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,40 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include +#include + +namespace Deprecated +{ + class IStatusBar : public boost::noncopyable + { + public: + virtual ~IStatusBar() + { + } + + virtual void ClearMessage() = 0; + + virtual void SetMessage(const std::string& message) = 0; + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Viewport/IViewport.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Viewport/IViewport.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,95 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IStatusBar.h" +#include "../../StoneEnumerations.h" +#include "../../Messages/IObservable.h" + +#include +#include "../Viewport/IMouseTracker.h" // only to get the "Touch" definition + +namespace Deprecated +{ + class IWidget; // Forward declaration + + class IViewport : public OrthancStone::IObservable + { + public: + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ViewportChangedMessage, IViewport); + + IViewport(OrthancStone::MessageBroker& broker) : + IObservable(broker) + { + } + + virtual ~IViewport() + { + } + + virtual void FitContent() = 0; + + virtual void SetStatusBar(IStatusBar& statusBar) = 0; + + virtual void SetSize(unsigned int width, + unsigned int height) = 0; + + // The function returns "true" iff. a new frame was rendered + virtual bool Render(Orthanc::ImageAccessor& surface) = 0; + + virtual void MouseDown(OrthancStone::MouseButton button, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers, + const std::vector& touches) = 0; + + virtual void MouseUp() = 0; + + virtual void MouseMove(int x, + int y, + const std::vector& displayTouches) = 0; + + virtual void MouseEnter() = 0; + + virtual void MouseLeave() = 0; + + virtual void MouseWheel(OrthancStone::MouseWheelDirection direction, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers) = 0; + + virtual void KeyPressed(OrthancStone::KeyboardKeys key, + char keyChar, + OrthancStone::KeyboardModifiers modifiers) = 0; + + virtual bool HasAnimation() = 0; + + virtual void DoAnimation() = 0; + + // Should only be called from IWidget + // TODO Why should this be virtual? + virtual void NotifyContentChanged() + { + BroadcastMessage(ViewportChangedMessage(*this)); + } + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Viewport/WidgetViewport.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Viewport/WidgetViewport.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,289 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "WidgetViewport.h" + +#include +#include + +namespace Deprecated +{ + WidgetViewport::WidgetViewport(OrthancStone::MessageBroker& broker) : + IViewport(broker), + statusBar_(NULL), + isMouseOver_(false), + lastMouseX_(0), + lastMouseY_(0), + backgroundChanged_(false) + { + } + + + void WidgetViewport::FitContent() + { + if (centralWidget_.get() != NULL) + { + centralWidget_->FitContent(); + } + } + + + void WidgetViewport::SetStatusBar(IStatusBar& statusBar) + { + statusBar_ = &statusBar; + + if (centralWidget_.get() != NULL) + { + centralWidget_->SetStatusBar(statusBar); + } + } + + + IWidget& WidgetViewport::SetCentralWidget(IWidget* widget) + { + if (widget == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + mouseTracker_.reset(NULL); + + centralWidget_.reset(widget); + centralWidget_->SetViewport(*this); + + if (statusBar_ != NULL) + { + centralWidget_->SetStatusBar(*statusBar_); + } + + NotifyBackgroundChanged(); + + return *widget; + } + + + void WidgetViewport::NotifyBackgroundChanged() + { + backgroundChanged_ = true; + NotifyContentChanged(); + } + + + void WidgetViewport::SetSize(unsigned int width, + unsigned int height) + { + background_.SetSize(width, height, false /* no alpha */); + + if (centralWidget_.get() != NULL) + { + centralWidget_->SetSize(width, height); + } + + NotifyBackgroundChanged(); + } + + + bool WidgetViewport::Render(Orthanc::ImageAccessor& surface) + { + if (centralWidget_.get() == NULL) + { + return false; + } + + Orthanc::ImageAccessor background; + background_.GetWriteableAccessor(background); + + if (backgroundChanged_ && + !centralWidget_->Render(background)) + { + return false; + } + + if (background.GetWidth() != surface.GetWidth() || + background.GetHeight() != surface.GetHeight()) + { + return false; + } + + Orthanc::ImageProcessing::Convert(surface, background); + + if (mouseTracker_.get() != NULL) + { + mouseTracker_->Render(surface); + } + else if (isMouseOver_) + { + centralWidget_->RenderMouseOver(surface, lastMouseX_, lastMouseY_); + } + + return true; + } + + void WidgetViewport::TouchStart(const std::vector& displayTouches) + { + MouseDown(OrthancStone::MouseButton_Left, (int)displayTouches[0].x, (int)displayTouches[0].y, OrthancStone::KeyboardModifiers_None, displayTouches); // one touch is equivalent to a mouse tracker without left button -> set the mouse coordinates to the first touch coordinates + } + + void WidgetViewport::TouchMove(const std::vector& displayTouches) + { + MouseMove((int)displayTouches[0].x, (int)displayTouches[0].y, displayTouches); // one touch is equivalent to a mouse tracker without left button -> set the mouse coordinates to the first touch coordinates + } + + void WidgetViewport::TouchEnd(const std::vector& displayTouches) + { + // note: TouchEnd is not triggered when a single touch gesture ends (it is only triggered when + // going from 2 touches to 1 touch, ...) + MouseUp(); + } + + void WidgetViewport::MouseDown(OrthancStone::MouseButton button, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers, + const std::vector& displayTouches + ) + { + lastMouseX_ = x; + lastMouseY_ = y; + + if (centralWidget_.get() != NULL) + { + mouseTracker_.reset(centralWidget_->CreateMouseTracker(button, x, y, modifiers, displayTouches)); + } + else + { + mouseTracker_.reset(NULL); + } + + NotifyContentChanged(); + } + + + void WidgetViewport::MouseUp() + { + if (mouseTracker_.get() != NULL) + { + mouseTracker_->MouseUp(); + mouseTracker_.reset(NULL); + NotifyContentChanged(); + } + } + + + void WidgetViewport::MouseMove(int x, + int y, + const std::vector& displayTouches) + { + if (centralWidget_.get() == NULL) + { + return; + } + + lastMouseX_ = x; + lastMouseY_ = y; + + bool repaint = false; + + if (mouseTracker_.get() != NULL) + { + mouseTracker_->MouseMove(x, y, displayTouches); + repaint = true; + } + else + { + repaint = centralWidget_->HasRenderMouseOver(); + } + + if (repaint) + { + // The scene must be repainted, notify the observers + NotifyContentChanged(); + } + } + + + void WidgetViewport::MouseEnter() + { + isMouseOver_ = true; + NotifyContentChanged(); + } + + + void WidgetViewport::MouseLeave() + { + isMouseOver_ = false; + + if (mouseTracker_.get() != NULL) + { + mouseTracker_->MouseUp(); + mouseTracker_.reset(NULL); + } + + NotifyContentChanged(); + } + + + void WidgetViewport::MouseWheel(OrthancStone::MouseWheelDirection direction, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers) + { + if (centralWidget_.get() != NULL && + mouseTracker_.get() == NULL) + { + centralWidget_->MouseWheel(direction, x, y, modifiers); + } + } + + + void WidgetViewport::KeyPressed(OrthancStone::KeyboardKeys key, + char keyChar, + OrthancStone::KeyboardModifiers modifiers) + { + if (centralWidget_.get() != NULL && + mouseTracker_.get() == NULL) + { + centralWidget_->KeyPressed(key, keyChar, modifiers); + } + } + + + bool WidgetViewport::HasAnimation() + { + if (centralWidget_.get() != NULL) + { + return centralWidget_->HasAnimation(); + } + else + { + return false; + } + } + + + void WidgetViewport::DoAnimation() + { + if (centralWidget_.get() != NULL) + { + centralWidget_->DoAnimation(); + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Viewport/WidgetViewport.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Viewport/WidgetViewport.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,94 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IViewport.h" +#include "../Widgets/IWidget.h" + +#include + +namespace Deprecated +{ + class WidgetViewport : public IViewport + { + private: + std::auto_ptr centralWidget_; + IStatusBar* statusBar_; + std::auto_ptr mouseTracker_; + bool isMouseOver_; + int lastMouseX_; + int lastMouseY_; + OrthancStone::CairoSurface background_; + bool backgroundChanged_; + + public: + WidgetViewport(OrthancStone::MessageBroker& broker); + + virtual void FitContent(); + + virtual void SetStatusBar(IStatusBar& statusBar); + + IWidget& SetCentralWidget(IWidget* widget); // Takes ownership + + virtual void NotifyBackgroundChanged(); + + virtual void SetSize(unsigned int width, + unsigned int height); + + virtual bool Render(Orthanc::ImageAccessor& surface); + + virtual void MouseDown(OrthancStone::MouseButton button, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers, + const std::vector& displayTouches); + + virtual void MouseUp(); + + virtual void MouseMove(int x, + int y, + const std::vector& displayTouches); + + virtual void MouseEnter(); + + virtual void MouseLeave(); + + virtual void TouchStart(const std::vector& touches); + + virtual void TouchMove(const std::vector& touches); + + virtual void TouchEnd(const std::vector& touches); + + virtual void MouseWheel(OrthancStone::MouseWheelDirection direction, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers); + + virtual void KeyPressed(OrthancStone::KeyboardKeys key, + char keyChar, + OrthancStone::KeyboardModifiers modifiers); + + virtual bool HasAnimation(); + + virtual void DoAnimation(); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Volumes/ISlicedVolume.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Volumes/ISlicedVolume.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,77 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../../Messages/IObservable.h" +#include "../Toolbox/Slice.h" + +namespace Deprecated +{ + class ISlicedVolume : public OrthancStone::IObservable + { + public: + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentChangedMessage, ISlicedVolume); + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryErrorMessage, ISlicedVolume); + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryReadyMessage, ISlicedVolume); + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, VolumeReadyMessage, ISlicedVolume); + + + class SliceContentChangedMessage : public OrthancStone::OriginMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + size_t sliceIndex_; + const Slice& slice_; + + public: + SliceContentChangedMessage(ISlicedVolume& origin, + size_t sliceIndex, + const Slice& slice) : + OriginMessage(origin), + sliceIndex_(sliceIndex), + slice_(slice) + { + } + + size_t GetSliceIndex() const + { + return sliceIndex_; + } + + const Slice& GetSlice() const + { + return slice_; + } + }; + + + ISlicedVolume(OrthancStone::MessageBroker& broker) : + IObservable(broker) + { + } + + virtual size_t GetSliceCount() const = 0; + + virtual const Slice& GetSlice(size_t slice) const = 0; + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Volumes/IVolumeLoader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Volumes/IVolumeLoader.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,40 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../../Messages/IObservable.h" + +namespace Deprecated +{ + class IVolumeLoader : public OrthancStone::IObservable + { + public: + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryReadyMessage, IVolumeLoader); + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryErrorMessage, IVolumeLoader); + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentChangedMessage, IVolumeLoader); + + IVolumeLoader(OrthancStone::MessageBroker& broker) : + IObservable(broker) + { + } + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Volumes/StructureSetLoader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Volumes/StructureSetLoader.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,116 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "StructureSetLoader.h" + +#include "../../Toolbox/MessagingToolbox.h" + +#include + +namespace Deprecated +{ + StructureSetLoader::StructureSetLoader(OrthancStone::MessageBroker& broker, + OrthancApiClient& orthanc) : + IVolumeLoader(broker), + IObserver(broker), + orthanc_(orthanc) + { + } + + + void StructureSetLoader::OnReferencedSliceLoaded(const OrthancApiClient::JsonResponseReadyMessage& message) + { + OrthancPlugins::FullOrthancDataset dataset(message.GetJson()); + + Orthanc::DicomMap slice; + OrthancStone::MessagingToolbox::ConvertDataset(slice, dataset); + structureSet_->AddReferencedSlice(slice); + + BroadcastMessage(ContentChangedMessage(*this)); + } + + + void StructureSetLoader::OnStructureSetLoaded(const OrthancApiClient::JsonResponseReadyMessage& message) + { + OrthancPlugins::FullOrthancDataset dataset(message.GetJson()); + structureSet_.reset(new OrthancStone::DicomStructureSet(dataset)); + + std::set instances; + structureSet_->GetReferencedInstances(instances); + + for (std::set::const_iterator it = instances.begin(); + it != instances.end(); ++it) + { + orthanc_.PostBinaryAsyncExpectJson("/tools/lookup", *it, + new OrthancStone::Callable(*this, &StructureSetLoader::OnLookupCompleted)); + } + + BroadcastMessage(GeometryReadyMessage(*this)); + } + + + void StructureSetLoader::OnLookupCompleted(const OrthancApiClient::JsonResponseReadyMessage& message) + { + const Json::Value& lookup = message.GetJson(); + + if (lookup.type() != Json::arrayValue || + lookup.size() != 1 || + !lookup[0].isMember("Type") || + !lookup[0].isMember("Path") || + lookup[0]["Type"].type() != Json::stringValue || + lookup[0]["ID"].type() != Json::stringValue || + lookup[0]["Type"].asString() != "Instance") + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + + const std::string& instance = lookup[0]["ID"].asString(); + orthanc_.GetJsonAsync("/instances/" + instance + "/tags", + new OrthancStone::Callable(*this, &StructureSetLoader::OnReferencedSliceLoaded)); + } + + + void StructureSetLoader::ScheduleLoadInstance(const std::string& instance) + { + if (structureSet_.get() != NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + orthanc_.GetJsonAsync("/instances/" + instance + "/tags?ignore-length=3006-0050", + new OrthancStone::Callable(*this, &StructureSetLoader::OnStructureSetLoaded)); + } + } + + + OrthancStone::DicomStructureSet& StructureSetLoader::GetStructureSet() + { + if (structureSet_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + return *structureSet_; + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Volumes/StructureSetLoader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Volumes/StructureSetLoader.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,57 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../../Toolbox/DicomStructureSet.h" +#include "../Toolbox/OrthancApiClient.h" +#include "IVolumeLoader.h" + +namespace Deprecated +{ + class StructureSetLoader : + public IVolumeLoader, + public OrthancStone::IObserver + { + private: + OrthancApiClient& orthanc_; + std::auto_ptr structureSet_; + + void OnReferencedSliceLoaded(const OrthancApiClient::JsonResponseReadyMessage& message); + + void OnStructureSetLoaded(const OrthancApiClient::JsonResponseReadyMessage& message); + + void OnLookupCompleted(const OrthancApiClient::JsonResponseReadyMessage& message); + + public: + StructureSetLoader(OrthancStone::MessageBroker& broker, + OrthancApiClient& orthanc); + + void ScheduleLoadInstance(const std::string& instance); + + bool HasStructureSet() const + { + return structureSet_.get() != NULL; + } + + OrthancStone::DicomStructureSet& GetStructureSet(); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/CairoWidget.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/CairoWidget.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,101 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "CairoWidget.h" + +#include +#include + +namespace Deprecated +{ + static bool IsAligned(const Orthanc::ImageAccessor& target) + { + // TODO + return true; + } + + CairoWidget::CairoWidget(const std::string& name) : + WidgetBase(name) + { + } + + void CairoWidget::SetSize(unsigned int width, + unsigned int height) + { + surface_.SetSize(width, height, false /* no alpha */); + } + + + bool CairoWidget::Render(Orthanc::ImageAccessor& target) + { + // Don't call the base class here, as + // "ClearBackgroundCairo()" is a faster alternative + + if (IsAligned(target)) + { + OrthancStone::CairoSurface surface(target, false /* no alpha */); + OrthancStone::CairoContext context(surface); + ClearBackgroundCairo(context); + return RenderCairo(context); + } + else + { + OrthancStone::CairoContext context(surface_); + ClearBackgroundCairo(context); + + if (RenderCairo(context)) + { + Orthanc::ImageAccessor surface; + surface_.GetReadOnlyAccessor(surface); + Orthanc::ImageProcessing::Copy(target, surface); + return true; + } + else + { + return false; + } + } + } + + + void CairoWidget::RenderMouseOver(Orthanc::ImageAccessor& target, + int x, + int y) + { + if (IsAligned(target)) + { + OrthancStone::CairoSurface surface(target, false /* no alpha */); + OrthancStone::CairoContext context(surface); + RenderMouseOverCairo(context, x, y); + } + else + { + Orthanc::ImageAccessor accessor; + surface_.GetWriteableAccessor(accessor); + Orthanc::ImageProcessing::Copy(accessor, target); + + OrthancStone::CairoContext context(surface_); + RenderMouseOverCairo(context, x, y); + + Orthanc::ImageProcessing::Copy(target, accessor); + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/CairoWidget.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/CairoWidget.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,52 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "WidgetBase.h" + +namespace Deprecated +{ + class CairoWidget : public WidgetBase + { + private: + OrthancStone::CairoSurface surface_; + + protected: + virtual bool RenderCairo(OrthancStone::CairoContext& context) = 0; + + virtual void RenderMouseOverCairo(OrthancStone::CairoContext& context, + int x, + int y) = 0; + + public: + CairoWidget(const std::string& name); + + virtual void SetSize(unsigned int width, + unsigned int height); + + virtual bool Render(Orthanc::ImageAccessor& target); + + virtual void RenderMouseOver(Orthanc::ImageAccessor& target, + int x, + int y); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/EmptyWidget.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/EmptyWidget.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,41 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "EmptyWidget.h" + +#include +#include + +namespace Deprecated +{ + bool EmptyWidget::Render(Orthanc::ImageAccessor& surface) + { + // Note: This call is slow + Orthanc::ImageProcessing::Set(surface, red_, green_, blue_, 255); + return true; + } + + + void EmptyWidget::DoAnimation() + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/EmptyWidget.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/EmptyWidget.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,116 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IWidget.h" + +namespace Deprecated +{ + /** + * This is a test widget that simply fills its surface with an + * uniform color. + **/ + class EmptyWidget : public IWidget + { + private: + uint8_t red_; + uint8_t green_; + uint8_t blue_; + + public: + EmptyWidget(uint8_t red, + uint8_t green, + uint8_t blue) : + red_(red), + green_(green), + blue_(blue) + { + } + + virtual void FitContent() + { + } + + virtual void SetParent(IWidget& widget) + { + } + + virtual void SetViewport(WidgetViewport& viewport) + { + } + + virtual void NotifyContentChanged() + { + } + + virtual void SetStatusBar(IStatusBar& statusBar) + { + } + + virtual void SetSize(unsigned int width, + unsigned int height) + { + } + + virtual bool Render(Orthanc::ImageAccessor& surface); + + virtual IMouseTracker* CreateMouseTracker(OrthancStone::MouseButton button, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers, + const std::vector& touches) + { + return NULL; + } + + virtual void RenderMouseOver(Orthanc::ImageAccessor& target, + int x, + int y) + { + } + + virtual void MouseWheel(OrthancStone::MouseWheelDirection direction, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers) + { + } + + virtual void KeyPressed(OrthancStone::KeyboardKeys key, + char keyChar, + OrthancStone::KeyboardModifiers modifiers) + { + } + + virtual bool HasAnimation() const + { + return false; + } + + virtual void DoAnimation(); + + virtual bool HasRenderMouseOver() + { + return false; + } + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/IWidget.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/IWidget.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,81 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../../StoneEnumerations.h" +#include "../Viewport/IMouseTracker.h" +#include "../Viewport/IStatusBar.h" + +namespace Deprecated +{ + class WidgetViewport; // Forward declaration + + class IWidget : public boost::noncopyable + { + public: + virtual ~IWidget() + { + } + + virtual void FitContent() = 0; + + virtual void SetParent(IWidget& parent) = 0; + + virtual void SetViewport(WidgetViewport& viewport) = 0; + + virtual void SetStatusBar(IStatusBar& statusBar) = 0; + + virtual void SetSize(unsigned int width, + unsigned int height) = 0; + + virtual bool Render(Orthanc::ImageAccessor& surface) = 0; + + virtual IMouseTracker* CreateMouseTracker(OrthancStone::MouseButton button, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers, + const std::vector& touches) = 0; + + virtual void RenderMouseOver(Orthanc::ImageAccessor& target, + int x, + int y) = 0; + + virtual bool HasRenderMouseOver() = 0; + + virtual void MouseWheel(OrthancStone::MouseWheelDirection direction, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers) = 0; + + virtual void KeyPressed(OrthancStone::KeyboardKeys key, + char keyChar, + OrthancStone::KeyboardModifiers modifiers) = 0; + + virtual bool HasAnimation() const = 0; + + virtual void DoAnimation() = 0; + + // Subclasses can call this method to signal the display of the + // widget must be refreshed + virtual void NotifyContentChanged() = 0; + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/IWorldSceneInteractor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/IWorldSceneInteractor.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,70 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IWorldSceneMouseTracker.h" + +#include "../Toolbox/ViewportGeometry.h" +#include "../../StoneEnumerations.h" +#include "../Viewport/IStatusBar.h" + +namespace Deprecated +{ + class WorldSceneWidget; + + class IWorldSceneInteractor : public boost::noncopyable + { + public: + virtual ~IWorldSceneInteractor() + { + } + + virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, + const ViewportGeometry& view, + OrthancStone::MouseButton button, + OrthancStone::KeyboardModifiers modifiers, + int viewportX, + int viewportY, + double x, + double y, + IStatusBar* statusBar, + const std::vector& touches) = 0; + + virtual void MouseOver(OrthancStone::CairoContext& context, + WorldSceneWidget& widget, + const ViewportGeometry& view, + double x, + double y, + IStatusBar* statusBar) = 0; + + virtual void MouseWheel(WorldSceneWidget& widget, + OrthancStone::MouseWheelDirection direction, + OrthancStone::KeyboardModifiers modifiers, + IStatusBar* statusBar) = 0; + + virtual void KeyPressed(WorldSceneWidget& widget, + OrthancStone::KeyboardKeys key, + char keyChar, + OrthancStone::KeyboardModifiers modifiers, + IStatusBar* statusBar) = 0; + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/IWorldSceneMouseTracker.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/IWorldSceneMouseTracker.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,54 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../../Viewport/CairoContext.h" +#include "../Viewport/IMouseTracker.h" // only to get the "Touch" definition + +namespace Deprecated +{ + + // this is tracking a mouse in scene coordinates/mm unlike + // the IMouseTracker that is tracking a mouse + // in screen coordinates/pixels. + class IWorldSceneMouseTracker : public boost::noncopyable + { + public: + virtual ~IWorldSceneMouseTracker() + { + } + + virtual bool HasRender() const = 0; + + virtual void Render(OrthancStone::CairoContext& context, + double zoom) = 0; + + virtual void MouseUp() = 0; + + virtual void MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector& displayTouches, + const std::vector& sceneTouches) = 0; + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/LayoutWidget.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/LayoutWidget.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,503 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "LayoutWidget.h" + +#include +#include + +#include + +namespace Deprecated +{ + class LayoutWidget::LayoutMouseTracker : public IMouseTracker + { + private: + std::auto_ptr tracker_; + int left_; + int top_; + unsigned int width_; + unsigned int height_; + + public: + LayoutMouseTracker(IMouseTracker* tracker, + int left, + int top, + unsigned int width, + unsigned int height) : + tracker_(tracker), + left_(left), + top_(top), + width_(width), + height_(height) + { + if (tracker == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + virtual void Render(Orthanc::ImageAccessor& surface) + { + Orthanc::ImageAccessor accessor; + surface.GetRegion(accessor, left_, top_, width_, height_); + tracker_->Render(accessor); + } + + virtual void MouseUp() + { + tracker_->MouseUp(); + } + + virtual void MouseMove(int x, + int y, + const std::vector& displayTouches) + { + std::vector relativeTouches; + for (size_t t = 0; t < displayTouches.size(); t++) + { + relativeTouches.push_back(Touch(displayTouches[t].x - left_, displayTouches[t].y - top_)); + } + + tracker_->MouseMove(x - left_, y - top_, relativeTouches); + } + }; + + + class LayoutWidget::ChildWidget : public boost::noncopyable + { + private: + std::auto_ptr widget_; + int left_; + int top_; + unsigned int width_; + unsigned int height_; + + public: + ChildWidget(IWidget* widget) : + widget_(widget) + { + assert(widget != NULL); + SetEmpty(); + } + + void DoAnimation() + { + if (widget_->HasAnimation()) + { + widget_->DoAnimation(); + } + } + + IWidget& GetWidget() const + { + return *widget_; + } + + void SetRectangle(unsigned int left, + unsigned int top, + unsigned int width, + unsigned int height) + { + left_ = left; + top_ = top; + width_ = width; + height_ = height; + + widget_->SetSize(width, height); + } + + void SetEmpty() + { + SetRectangle(0, 0, 0, 0); + } + + bool Contains(int x, + int y) const + { + return (x >= left_ && + y >= top_ && + x < left_ + static_cast(width_) && + y < top_ + static_cast(height_)); + } + + bool Render(Orthanc::ImageAccessor& target) + { + if (width_ == 0 || + height_ == 0) + { + return true; + } + else + { + Orthanc::ImageAccessor accessor; + target.GetRegion(accessor, left_, top_, width_, height_); + return widget_->Render(accessor); + } + } + + IMouseTracker* CreateMouseTracker(OrthancStone::MouseButton button, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers, + const std::vector& touches) + { + if (Contains(x, y)) + { + IMouseTracker* tracker = widget_->CreateMouseTracker(button, + x - left_, + y - top_, + modifiers, + touches); + if (tracker) + { + return new LayoutMouseTracker(tracker, left_, top_, width_, height_); + } + } + + return NULL; + } + + void RenderMouseOver(Orthanc::ImageAccessor& target, + int x, + int y) + { + if (Contains(x, y)) + { + Orthanc::ImageAccessor accessor; + target.GetRegion(accessor, left_, top_, width_, height_); + + widget_->RenderMouseOver(accessor, x - left_, y - top_); + } + } + + void MouseWheel(OrthancStone::MouseWheelDirection direction, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers) + { + if (Contains(x, y)) + { + widget_->MouseWheel(direction, x - left_, y - top_, modifiers); + } + } + + bool HasRenderMouseOver() + { + return widget_->HasRenderMouseOver(); + } + }; + + + void LayoutWidget::ComputeChildrenExtents() + { + if (children_.size() == 0) + { + return; + } + + float internal = static_cast(paddingInternal_); + + if (width_ <= paddingLeft_ + paddingRight_ || + height_ <= paddingTop_ + paddingBottom_) + { + for (size_t i = 0; i < children_.size(); i++) + { + children_[i]->SetEmpty(); + } + } + else if (isHorizontal_) + { + unsigned int padding = paddingLeft_ + paddingRight_ + (static_cast(children_.size()) - 1) * paddingInternal_; + float childWidth = ((static_cast(width_) - static_cast(padding)) / + static_cast(children_.size())); + + for (size_t i = 0; i < children_.size(); i++) + { + float left = static_cast(paddingLeft_) + static_cast(i) * (childWidth + internal); + float right = left + childWidth; + + if (left >= right) + { + children_[i]->SetEmpty(); + } + else + { + children_[i]->SetRectangle(static_cast(left), + paddingTop_, + boost::math::iround(right - left), + height_ - paddingTop_ - paddingBottom_); + } + } + } + else + { + unsigned int padding = paddingTop_ + paddingBottom_ + (static_cast(children_.size()) - 1) * paddingInternal_; + float childHeight = ((static_cast(height_) - static_cast(padding)) / + static_cast(children_.size())); + + for (size_t i = 0; i < children_.size(); i++) + { + float top = static_cast(paddingTop_) + static_cast(i) * (childHeight + internal); + float bottom = top + childHeight; + + if (top >= bottom) + { + children_[i]->SetEmpty(); + } + else + { + children_[i]->SetRectangle(paddingTop_, + static_cast(top), + width_ - paddingLeft_ - paddingRight_, + boost::math::iround(bottom - top)); + } + } + } + + NotifyContentChanged(*this); + } + + + LayoutWidget::LayoutWidget(const std::string& name) : + WidgetBase(name), + isHorizontal_(true), + width_(0), + height_(0), + paddingLeft_(0), + paddingTop_(0), + paddingRight_(0), + paddingBottom_(0), + paddingInternal_(0) + { + } + + + LayoutWidget::~LayoutWidget() + { + for (size_t i = 0; i < children_.size(); i++) + { + delete children_[i]; + } + } + + + void LayoutWidget::FitContent() + { + for (size_t i = 0; i < children_.size(); i++) + { + children_[i]->GetWidget().FitContent(); + } + } + + + void LayoutWidget::NotifyContentChanged(const IWidget& widget) + { + // One of the children has changed + WidgetBase::NotifyContentChanged(); + } + + + void LayoutWidget::SetHorizontal() + { + isHorizontal_ = true; + ComputeChildrenExtents(); + } + + + void LayoutWidget::SetVertical() + { + isHorizontal_ = false; + ComputeChildrenExtents(); + } + + + void LayoutWidget::SetPadding(unsigned int left, + unsigned int top, + unsigned int right, + unsigned int bottom, + unsigned int spacing) + { + paddingLeft_ = left; + paddingTop_ = top; + paddingRight_ = right; + paddingBottom_ = bottom; + paddingInternal_ = spacing; + } + + + void LayoutWidget::SetPadding(unsigned int padding) + { + paddingLeft_ = padding; + paddingTop_ = padding; + paddingRight_ = padding; + paddingBottom_ = padding; + paddingInternal_ = padding; + } + + + IWidget& LayoutWidget::AddWidget(IWidget* widget) // Takes ownership + { + if (widget == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + if (GetStatusBar() != NULL) + { + widget->SetStatusBar(*GetStatusBar()); + } + + children_.push_back(new ChildWidget(widget)); + widget->SetParent(*this); + + ComputeChildrenExtents(); + + if (widget->HasAnimation()) + { + hasAnimation_ = true; + } + + return *widget; + } + + + void LayoutWidget::SetStatusBar(IStatusBar& statusBar) + { + WidgetBase::SetStatusBar(statusBar); + + for (size_t i = 0; i < children_.size(); i++) + { + children_[i]->GetWidget().SetStatusBar(statusBar); + } + } + + + void LayoutWidget::SetSize(unsigned int width, + unsigned int height) + { + width_ = width; + height_ = height; + ComputeChildrenExtents(); + } + + + bool LayoutWidget::Render(Orthanc::ImageAccessor& surface) + { + if (!WidgetBase::Render(surface)) + { + return false; + } + + for (size_t i = 0; i < children_.size(); i++) + { + if (!children_[i]->Render(surface)) + { + return false; + } + } + + return true; + } + + + IMouseTracker* LayoutWidget::CreateMouseTracker(OrthancStone::MouseButton button, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers, + const std::vector& touches) + { + for (size_t i = 0; i < children_.size(); i++) + { + IMouseTracker* tracker = children_[i]->CreateMouseTracker(button, x, y, modifiers, touches); + if (tracker != NULL) + { + return tracker; + } + } + + return NULL; + } + + + void LayoutWidget::RenderMouseOver(Orthanc::ImageAccessor& target, + int x, + int y) + { + for (size_t i = 0; i < children_.size(); i++) + { + children_[i]->RenderMouseOver(target, x, y); + } + } + + + void LayoutWidget::MouseWheel(OrthancStone::MouseWheelDirection direction, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers) + { + for (size_t i = 0; i < children_.size(); i++) + { + children_[i]->MouseWheel(direction, x, y, modifiers); + } + } + + + void LayoutWidget::KeyPressed(OrthancStone::KeyboardKeys key, + char keyChar, + OrthancStone::KeyboardModifiers modifiers) + { + for (size_t i = 0; i < children_.size(); i++) + { + children_[i]->GetWidget().KeyPressed(key, keyChar, modifiers); + } + } + + + void LayoutWidget::DoAnimation() + { + if (hasAnimation_) + { + for (size_t i = 0; i < children_.size(); i++) + { + children_[i]->DoAnimation(); + } + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + + bool LayoutWidget::HasRenderMouseOver() + { + for (size_t i = 0; i < children_.size(); i++) + { + if (children_[i]->HasRenderMouseOver()) + { + return true; + } + } + + return false; + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/LayoutWidget.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/LayoutWidget.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,134 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "WidgetBase.h" + +#include +#include + +namespace Deprecated +{ + class LayoutWidget : public WidgetBase + { + private: + class LayoutMouseTracker; + class ChildWidget; + + std::vector children_; + bool isHorizontal_; + unsigned int width_; + unsigned int height_; + std::auto_ptr mouseTracker_; + unsigned int paddingLeft_; + unsigned int paddingTop_; + unsigned int paddingRight_; + unsigned int paddingBottom_; + unsigned int paddingInternal_; + bool hasAnimation_; + + void ComputeChildrenExtents(); + + public: + LayoutWidget(const std::string& name); + + virtual ~LayoutWidget(); + + virtual void FitContent(); + + virtual void NotifyContentChanged(const IWidget& widget); + + void SetHorizontal(); + + void SetVertical(); + + void SetPadding(unsigned int left, + unsigned int top, + unsigned int right, + unsigned int bottom, + unsigned int spacing); + + void SetPadding(unsigned int padding); + + unsigned int GetPaddingLeft() const + { + return paddingLeft_; + } + + unsigned int GetPaddingTop() const + { + return paddingTop_; + } + + unsigned int GetPaddingRight() const + { + return paddingRight_; + } + + unsigned int GetPaddingBottom() const + { + return paddingBottom_; + } + + unsigned int GetPaddingInternal() const + { + return paddingInternal_; + } + + IWidget& AddWidget(IWidget* widget); // Takes ownership + + virtual void SetStatusBar(IStatusBar& statusBar); + + virtual void SetSize(unsigned int width, + unsigned int height); + + virtual bool Render(Orthanc::ImageAccessor& surface); + + virtual IMouseTracker* CreateMouseTracker(OrthancStone::MouseButton button, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers, + const std::vector& touches); + + virtual void RenderMouseOver(Orthanc::ImageAccessor& target, + int x, + int y); + + virtual void MouseWheel(OrthancStone::MouseWheelDirection direction, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers); + + virtual void KeyPressed(OrthancStone::KeyboardKeys key, + char keyChar, + OrthancStone::KeyboardModifiers modifiers); + + virtual bool HasAnimation() const + { + return hasAnimation_; + } + + virtual void DoAnimation(); + + virtual bool HasRenderMouseOver(); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/PanMouseTracker.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/PanMouseTracker.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,58 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "PanMouseTracker.h" + +#include +#include + +namespace Deprecated +{ + PanMouseTracker::PanMouseTracker(WorldSceneWidget& that, + int x, + int y) : + that_(that) + { + that.GetView().GetPan(originalPanX_, originalPanY_); + that.GetView().MapPixelCenterToScene(downX_, downY_, x, y); + } + + + void PanMouseTracker::Render(OrthancStone::CairoContext& context, + double zoom) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + + void PanMouseTracker::MouseMove(int displayX, + int displayY, + double x, + double y, + const std::vector& displayTouches, + const std::vector& sceneTouches) + { + ViewportGeometry view = that_.GetView(); + view.SetPan(originalPanX_ + (x - downX_) * view.GetZoom(), + originalPanY_ + (y - downY_) * view.GetZoom()); + that_.SetView(view); + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/PanMouseTracker.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/PanMouseTracker.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,61 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "WorldSceneWidget.h" + +namespace Deprecated +{ + class PanMouseTracker : public IWorldSceneMouseTracker + { + private: + WorldSceneWidget& that_; + double originalPanX_; + double originalPanY_; + double downX_; + double downY_; + + public: + PanMouseTracker(WorldSceneWidget& that, + int x, + int y); + + virtual bool HasRender() const + { + return false; + } + + virtual void MouseUp() + { + } + + virtual void Render(OrthancStone::CairoContext& context, + double zoom); + + virtual void MouseMove(int displayX, + int displayY, + double x, + double y, + const std::vector& displayTouches, + const std::vector& sceneTouches); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/PanZoomMouseTracker.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/PanZoomMouseTracker.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,137 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "PanZoomMouseTracker.h" + +#include +#include +#include + +namespace Deprecated +{ + Touch GetCenter(const std::vector& touches) + { + return Touch((touches[0].x + touches[1].x) / 2.0f, (touches[0].y + touches[1].y) / 2.0f); + } + + double GetDistance(const std::vector& touches) + { + float dx = touches[0].x - touches[1].x; + float dy = touches[0].y - touches[1].y; + return sqrt((double)(dx * dx) + (double)(dy * dy)); + } + + + PanZoomMouseTracker::PanZoomMouseTracker(WorldSceneWidget& that, + const std::vector& startTouches) + : that_(that), + originalZoom_(that.GetView().GetZoom()) + { + that.GetView().GetPan(originalPanX_, originalPanY_); + that.GetView().MapPixelCenterToScene(originalSceneTouches_, startTouches); + + originalDisplayCenter_ = GetCenter(startTouches); + originalSceneCenter_ = GetCenter(originalSceneTouches_); + originalDisplayDistanceBetweenTouches_ = GetDistance(startTouches); + +// printf("original Pan %f %f\n", originalPanX_, originalPanY_); +// printf("original Zoom %f \n", originalZoom_); +// printf("original distance %f \n", (float)originalDisplayDistanceBetweenTouches_); +// printf("original display touches 0 %f %f\n", startTouches[0].x, startTouches[0].y); +// printf("original display touches 1 %f %f\n", startTouches[1].x, startTouches[1].y); +// printf("original Scene center %f %f\n", originalSceneCenter_.x, originalSceneCenter_.y); + + unsigned int height = that.GetView().GetDisplayHeight(); + + if (height <= 3) + { + idle_ = true; + LOG(WARNING) << "image is too small to zoom (current height = " << height << ")"; + } + else + { + idle_ = false; + normalization_ = 1.0 / static_cast(height - 1); + } + + } + + + void PanZoomMouseTracker::Render(OrthancStone::CairoContext& context, + double zoom) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + + void PanZoomMouseTracker::MouseMove(int displayX, + int displayY, + double x, + double y, + const std::vector& displayTouches, + const std::vector& sceneTouches) + { + ViewportGeometry view = that_.GetView(); + +// printf("Display touches 0 %f %f\n", displayTouches[0].x, displayTouches[0].y); +// printf("Display touches 1 %f %f\n", displayTouches[1].x, displayTouches[1].y); +// printf("Scene touches 0 %f %f\n", sceneTouches[0].x, sceneTouches[0].y); +// printf("Scene touches 1 %f %f\n", sceneTouches[1].x, sceneTouches[1].y); + +// printf("zoom = %f\n", view.GetZoom()); + Touch currentSceneCenter = GetCenter(sceneTouches); + double panX = originalPanX_ + (currentSceneCenter.x - originalSceneCenter_.x) * view.GetZoom(); + double panY = originalPanY_ + (currentSceneCenter.y - originalSceneCenter_.y) * view.GetZoom(); + + view.SetPan(panX, panY); + + static const double MIN_ZOOM = -4; + static const double MAX_ZOOM = 4; + + if (!idle_) + { + double currentDistanceBetweenTouches = GetDistance(displayTouches); + + double dy = static_cast(currentDistanceBetweenTouches - originalDisplayDistanceBetweenTouches_) * normalization_; // In the range [-1,1] + double z; + + // Linear interpolation from [-1, 1] to [MIN_ZOOM, MAX_ZOOM] + if (dy < -1.0) + { + z = MIN_ZOOM; + } + else if (dy > 1.0) + { + z = MAX_ZOOM; + } + else + { + z = MIN_ZOOM + (MAX_ZOOM - MIN_ZOOM) * (dy + 1.0) / 2.0; + } + + z = pow(2.0, z); + + view.SetZoom(z * originalZoom_); + } + + that_.SetView(view); + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/PanZoomMouseTracker.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/PanZoomMouseTracker.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,65 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "WorldSceneWidget.h" + +namespace Deprecated +{ + class PanZoomMouseTracker : public IWorldSceneMouseTracker + { + private: + WorldSceneWidget& that_; + std::vector originalSceneTouches_; + Touch originalSceneCenter_; + Touch originalDisplayCenter_; + double originalPanX_; + double originalPanY_; + double originalZoom_; + double originalDisplayDistanceBetweenTouches_; + bool idle_; + double normalization_; + + public: + PanZoomMouseTracker(WorldSceneWidget& that, + const std::vector& startTouches); + + virtual bool HasRender() const + { + return false; + } + + virtual void MouseUp() + { + } + + virtual void Render(OrthancStone::CairoContext& context, + double zoom); + + virtual void MouseMove(int displayX, + int displayY, + double x, + double y, + const std::vector& displayTouches, + const std::vector& sceneTouches); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/SliceViewerWidget.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/SliceViewerWidget.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,654 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "SliceViewerWidget.h" + +#include "../Layers/SliceOutlineRenderer.h" +#include "../../Toolbox/GeometryToolbox.h" +#include "../Layers/FrameRenderer.h" + +#include +#include + +#include + + +static const double THIN_SLICE_THICKNESS = 100.0 * std::numeric_limits::epsilon(); + +namespace Deprecated +{ + class SliceViewerWidget::Scene : public boost::noncopyable + { + private: + OrthancStone::CoordinateSystem3D plane_; + double thickness_; + size_t countMissing_; + std::vector renderers_; + + public: + void DeleteLayer(size_t index) + { + if (index >= renderers_.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + assert(countMissing_ <= renderers_.size()); + + if (renderers_[index] != NULL) + { + assert(countMissing_ < renderers_.size()); + delete renderers_[index]; + renderers_[index] = NULL; + countMissing_++; + } + } + + Scene(const OrthancStone::CoordinateSystem3D& plane, + double thickness, + size_t countLayers) : + plane_(plane), + thickness_(thickness), + countMissing_(countLayers), + renderers_(countLayers, NULL) + { + if (thickness <= 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + ~Scene() + { + for (size_t i = 0; i < renderers_.size(); i++) + { + DeleteLayer(i); + } + } + + void SetLayer(size_t index, + ILayerRenderer* renderer) // Takes ownership + { + if (renderer == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + DeleteLayer(index); + + renderers_[index] = renderer; + countMissing_--; + } + + const OrthancStone::CoordinateSystem3D& GetPlane() const + { + return plane_; + } + + bool HasRenderer(size_t index) + { + return renderers_[index] != NULL; + } + + bool IsComplete() const + { + return countMissing_ == 0; + } + + unsigned int GetCountMissing() const + { + return static_cast(countMissing_); + } + + bool RenderScene(OrthancStone::CairoContext& context, + const ViewportGeometry& view, + const OrthancStone::CoordinateSystem3D& viewportPlane) + { + bool fullQuality = true; + cairo_t *cr = context.GetObject(); + + for (size_t i = 0; i < renderers_.size(); i++) + { + if (renderers_[i] != NULL) + { + const OrthancStone::CoordinateSystem3D& framePlane = renderers_[i]->GetLayerPlane(); + + double x0, y0, x1, y1, x2, y2; + viewportPlane.ProjectPoint(x0, y0, framePlane.GetOrigin()); + viewportPlane.ProjectPoint(x1, y1, framePlane.GetOrigin() + framePlane.GetAxisX()); + viewportPlane.ProjectPoint(x2, y2, framePlane.GetOrigin() + framePlane.GetAxisY()); + + /** + * Now we solve the system of linear equations Ax + b = x', given: + * A [0 ; 0] + b = [x0 ; y0] + * A [1 ; 0] + b = [x1 ; y1] + * A [0 ; 1] + b = [x2 ; y2] + * <=> + * b = [x0 ; y0] + * A [1 ; 0] = [x1 ; y1] - b = [x1 - x0 ; y1 - y0] + * A [0 ; 1] = [x2 ; y2] - b = [x2 - x0 ; y2 - y0] + * <=> + * b = [x0 ; y0] + * [a11 ; a21] = [x1 - x0 ; y1 - y0] + * [a12 ; a22] = [x2 - x0 ; y2 - y0] + **/ + + cairo_matrix_t transform; + cairo_matrix_init(&transform, x1 - x0, y1 - y0, x2 - x0, y2 - y0, x0, y0); + + cairo_save(cr); + cairo_transform(cr, &transform); + + if (!renderers_[i]->RenderLayer(context, view)) + { + cairo_restore(cr); + return false; + } + + cairo_restore(cr); + } + + if (renderers_[i] != NULL && + !renderers_[i]->IsFullQuality()) + { + fullQuality = false; + } + } + + if (!fullQuality) + { + double x, y; + view.MapDisplayToScene(x, y, static_cast(view.GetDisplayWidth()) / 2.0, 10); + + cairo_translate(cr, x, y); + +#if 1 + double s = 5.0 / view.GetZoom(); + cairo_rectangle(cr, -s, -s, 2.0 * s, 2.0 * s); +#else + // TODO Drawing filled circles makes WebAssembly crash! + cairo_arc(cr, 0, 0, 5.0 / view.GetZoom(), 0, 2.0 * boost::math::constants::pi()); +#endif + + cairo_set_line_width(cr, 2.0 / view.GetZoom()); + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_stroke_preserve(cr); + cairo_set_source_rgb(cr, 1, 0, 0); + cairo_fill(cr); + } + + return true; + } + + void SetLayerStyle(size_t index, + const RenderStyle& style) + { + if (renderers_[index] != NULL) + { + renderers_[index]->SetLayerStyle(style); + } + } + + bool ContainsPlane(const OrthancStone::CoordinateSystem3D& plane) const + { + bool isOpposite; + if (!OrthancStone::GeometryToolbox::IsParallelOrOpposite(isOpposite, + plane.GetNormal(), + plane_.GetNormal())) + { + return false; + } + else + { + double z = (plane_.ProjectAlongNormal(plane.GetOrigin()) - + plane_.ProjectAlongNormal(plane_.GetOrigin())); + + if (z < 0) + { + z = -z; + } + + return z <= thickness_; + } + } + + double GetThickness() const + { + return thickness_; + } + }; + + + bool SliceViewerWidget::LookupLayer(size_t& index /* out */, + const IVolumeSlicer& layer) const + { + LayersIndex::const_iterator found = layersIndex_.find(&layer); + + if (found == layersIndex_.end()) + { + return false; + } + else + { + index = found->second; + assert(index < layers_.size() && + layers_[index] == &layer); + return true; + } + } + + + void SliceViewerWidget::GetLayerExtent(OrthancStone::Extent2D& extent, + IVolumeSlicer& source) const + { + extent.Reset(); + + std::vector points; + if (source.GetExtent(points, plane_)) + { + for (size_t i = 0; i < points.size(); i++) + { + double x, y; + plane_.ProjectPoint(x, y, points[i]); + extent.AddPoint(x, y); + } + } + } + + + OrthancStone::Extent2D SliceViewerWidget::GetSceneExtent() + { + OrthancStone::Extent2D sceneExtent; + + for (size_t i = 0; i < layers_.size(); i++) + { + assert(layers_[i] != NULL); + OrthancStone::Extent2D layerExtent; + GetLayerExtent(layerExtent, *layers_[i]); + + sceneExtent.Union(layerExtent); + } + + return sceneExtent; + } + + + bool SliceViewerWidget::RenderScene(OrthancStone::CairoContext& context, + const ViewportGeometry& view) + { + if (currentScene_.get() != NULL) + { + return currentScene_->RenderScene(context, view, plane_); + } + else + { + return true; + } + } + + + void SliceViewerWidget::ResetPendingScene() + { + double thickness; + if (pendingScene_.get() == NULL) + { + thickness = 1.0; + } + else + { + thickness = pendingScene_->GetThickness(); + } + + pendingScene_.reset(new Scene(plane_, thickness, layers_.size())); + } + + + void SliceViewerWidget::UpdateLayer(size_t index, + ILayerRenderer* renderer, + const OrthancStone::CoordinateSystem3D& plane) + { + LOG(INFO) << "Updating layer " << index; + + std::auto_ptr tmp(renderer); + + if (renderer == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + if (index >= layers_.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + assert(layers_.size() == styles_.size()); + renderer->SetLayerStyle(styles_[index]); + + if (currentScene_.get() != NULL && + currentScene_->ContainsPlane(plane)) + { + currentScene_->SetLayer(index, tmp.release()); + NotifyContentChanged(); + } + else if (pendingScene_.get() != NULL && + pendingScene_->ContainsPlane(plane)) + { + pendingScene_->SetLayer(index, tmp.release()); + + if (currentScene_.get() == NULL || + !currentScene_->IsComplete() || + pendingScene_->IsComplete()) + { + currentScene_ = pendingScene_; + NotifyContentChanged(); + } + } + } + + + SliceViewerWidget::SliceViewerWidget(OrthancStone::MessageBroker& broker, + const std::string& name) : + WorldSceneWidget(name), + IObserver(broker), + IObservable(broker), + started_(false) + { + SetBackgroundCleared(true); + } + + + SliceViewerWidget::~SliceViewerWidget() + { + for (size_t i = 0; i < layers_.size(); i++) + { + delete layers_[i]; + } + } + + void SliceViewerWidget::ObserveLayer(IVolumeSlicer& layer) + { + layer.RegisterObserverCallback(new OrthancStone::Callable + (*this, &SliceViewerWidget::OnGeometryReady)); + // currently ignore errors layer->RegisterObserverCallback(new Callable(*this, &SliceViewerWidget::...)); + layer.RegisterObserverCallback(new OrthancStone::Callable + (*this, &SliceViewerWidget::OnSliceChanged)); + layer.RegisterObserverCallback(new OrthancStone::Callable + (*this, &SliceViewerWidget::OnContentChanged)); + layer.RegisterObserverCallback(new OrthancStone::Callable + (*this, &SliceViewerWidget::OnLayerReady)); + layer.RegisterObserverCallback(new OrthancStone::Callable + (*this, &SliceViewerWidget::OnLayerError)); + } + + + size_t SliceViewerWidget::AddLayer(IVolumeSlicer* layer) // Takes ownership + { + if (layer == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + size_t index = layers_.size(); + layers_.push_back(layer); + styles_.push_back(RenderStyle()); + layersIndex_[layer] = index; + + ResetPendingScene(); + + ObserveLayer(*layer); + + ResetChangedLayers(); + + return index; + } + + + void SliceViewerWidget::ReplaceLayer(size_t index, IVolumeSlicer* layer) // Takes ownership + { + if (layer == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + if (index >= layers_.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + delete layers_[index]; + layers_[index] = layer; + layersIndex_[layer] = index; + + ResetPendingScene(); + + ObserveLayer(*layer); + + InvalidateLayer(index); + } + + + void SliceViewerWidget::RemoveLayer(size_t index) + { + if (index >= layers_.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + IVolumeSlicer* previousLayer = layers_[index]; + layersIndex_.erase(layersIndex_.find(previousLayer)); + layers_.erase(layers_.begin() + index); + changedLayers_.erase(changedLayers_.begin() + index); + styles_.erase(styles_.begin() + index); + + delete layers_[index]; + + currentScene_->DeleteLayer(index); + ResetPendingScene(); + + NotifyContentChanged(); + } + + + const RenderStyle& SliceViewerWidget::GetLayerStyle(size_t layer) const + { + if (layer >= layers_.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + assert(layers_.size() == styles_.size()); + return styles_[layer]; + } + + + void SliceViewerWidget::SetLayerStyle(size_t layer, + const RenderStyle& style) + { + if (layer >= layers_.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + assert(layers_.size() == styles_.size()); + styles_[layer] = style; + + if (currentScene_.get() != NULL) + { + currentScene_->SetLayerStyle(layer, style); + } + + if (pendingScene_.get() != NULL) + { + pendingScene_->SetLayerStyle(layer, style); + } + + NotifyContentChanged(); + } + + + void SliceViewerWidget::SetSlice(const OrthancStone::CoordinateSystem3D& plane) + { + LOG(INFO) << "Setting slice origin: (" << plane.GetOrigin()[0] + << "," << plane.GetOrigin()[1] + << "," << plane.GetOrigin()[2] << ")"; + + Deprecated::Slice displayedSlice(plane_, THIN_SLICE_THICKNESS); + + //if (!displayedSlice.ContainsPlane(slice)) + { + if (currentScene_.get() == NULL || + (pendingScene_.get() != NULL && + pendingScene_->IsComplete())) + { + currentScene_ = pendingScene_; + } + + plane_ = plane; + ResetPendingScene(); + + InvalidateAllLayers(); // TODO Removing this line avoid loading twice the image in WASM + } + + BroadcastMessage(DisplayedSliceMessage(*this, displayedSlice)); + } + + + void SliceViewerWidget::OnGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message) + { + size_t i; + if (LookupLayer(i, message.GetOrigin())) + { + LOG(INFO) << ": Geometry ready for layer " << i << " in " << GetName(); + + changedLayers_[i] = true; + //layers_[i]->ScheduleLayerCreation(plane_); + } + BroadcastMessage(GeometryChangedMessage(*this)); + } + + + void SliceViewerWidget::InvalidateAllLayers() + { + for (size_t i = 0; i < layers_.size(); i++) + { + assert(layers_[i] != NULL); + changedLayers_[i] = true; + + //layers_[i]->ScheduleLayerCreation(plane_); + } + } + + + void SliceViewerWidget::InvalidateLayer(size_t layer) + { + if (layer >= layers_.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + assert(layers_[layer] != NULL); + changedLayers_[layer] = true; + + //layers_[layer]->ScheduleLayerCreation(plane_); + } + + + void SliceViewerWidget::OnContentChanged(const IVolumeSlicer::ContentChangedMessage& message) + { + size_t index; + if (LookupLayer(index, message.GetOrigin())) + { + InvalidateLayer(index); + } + + BroadcastMessage(SliceViewerWidget::ContentChangedMessage(*this)); + } + + + void SliceViewerWidget::OnSliceChanged(const IVolumeSlicer::SliceContentChangedMessage& message) + { + if (message.GetSlice().ContainsPlane(plane_)) + { + size_t index; + if (LookupLayer(index, message.GetOrigin())) + { + InvalidateLayer(index); + } + } + + BroadcastMessage(SliceViewerWidget::ContentChangedMessage(*this)); + } + + + void SliceViewerWidget::OnLayerReady(const IVolumeSlicer::LayerReadyMessage& message) + { + size_t index; + if (LookupLayer(index, message.GetOrigin())) + { + LOG(INFO) << "Renderer ready for layer " << index; + UpdateLayer(index, message.CreateRenderer(), message.GetSlice()); + } + + BroadcastMessage(SliceViewerWidget::ContentChangedMessage(*this)); + } + + + void SliceViewerWidget::OnLayerError(const IVolumeSlicer::LayerErrorMessage& message) + { + size_t index; + if (LookupLayer(index, message.GetOrigin())) + { + LOG(ERROR) << "Using error renderer on layer " << index; + + // TODO + //UpdateLayer(index, new SliceOutlineRenderer(slice), slice); + + BroadcastMessage(SliceViewerWidget::ContentChangedMessage(*this)); + } + } + + + void SliceViewerWidget::ResetChangedLayers() + { + changedLayers_.resize(layers_.size()); + + for (size_t i = 0; i < changedLayers_.size(); i++) + { + changedLayers_[i] = false; + } + } + + + void SliceViewerWidget::DoAnimation() + { + assert(changedLayers_.size() <= layers_.size()); + + for (size_t i = 0; i < changedLayers_.size(); i++) + { + if (changedLayers_[i]) + { + layers_[i]->ScheduleLayerCreation(plane_); + } + } + + ResetChangedLayers(); + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/SliceViewerWidget.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/SliceViewerWidget.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,155 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "WorldSceneWidget.h" +#include "../Layers/IVolumeSlicer.h" +#include "../../Toolbox/Extent2D.h" +#include "../../Messages/IObserver.h" + +#include + +namespace Deprecated +{ + class SliceViewerWidget : + public WorldSceneWidget, + public OrthancStone::IObserver, + public OrthancStone::IObservable + { + public: + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryChangedMessage, SliceViewerWidget); + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentChangedMessage, SliceViewerWidget); + + + // TODO - Use this message in ReferenceLineSource + class DisplayedSliceMessage : public OrthancStone::OriginMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + const Deprecated::Slice& slice_; + + public: + DisplayedSliceMessage(SliceViewerWidget& origin, + const Deprecated::Slice& slice) : + OriginMessage(origin), + slice_(slice) + { + } + + const Deprecated::Slice& GetSlice() const + { + return slice_; + } + }; + + private: + SliceViewerWidget(const SliceViewerWidget&); + SliceViewerWidget& operator=(const SliceViewerWidget&); + + class Scene; + + typedef std::map LayersIndex; + + bool started_; + LayersIndex layersIndex_; + std::vector layers_; + std::vector styles_; + OrthancStone::CoordinateSystem3D plane_; + std::auto_ptr currentScene_; + std::auto_ptr pendingScene_; + std::vector changedLayers_; + + bool LookupLayer(size_t& index /* out */, + const IVolumeSlicer& layer) const; + + void GetLayerExtent(OrthancStone::Extent2D& extent, + IVolumeSlicer& source) const; + + void OnGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message); + + virtual void OnContentChanged(const IVolumeSlicer::ContentChangedMessage& message); + + virtual void OnSliceChanged(const IVolumeSlicer::SliceContentChangedMessage& message); + + virtual void OnLayerReady(const IVolumeSlicer::LayerReadyMessage& message); + + virtual void OnLayerError(const IVolumeSlicer::LayerErrorMessage& message); + + void ObserveLayer(IVolumeSlicer& source); + + void ResetChangedLayers(); + + public: + SliceViewerWidget(OrthancStone::MessageBroker& broker, + const std::string& name); + + virtual OrthancStone::Extent2D GetSceneExtent(); + + protected: + virtual bool RenderScene(OrthancStone::CairoContext& context, + const ViewportGeometry& view); + + void ResetPendingScene(); + + void UpdateLayer(size_t index, + ILayerRenderer* renderer, + const OrthancStone::CoordinateSystem3D& plane); + + void InvalidateAllLayers(); + + void InvalidateLayer(size_t layer); + + public: + virtual ~SliceViewerWidget(); + + size_t AddLayer(IVolumeSlicer* layer); // Takes ownership + + void ReplaceLayer(size_t layerIndex, IVolumeSlicer* layer); // Takes ownership + + void RemoveLayer(size_t layerIndex); + + size_t GetLayerCount() const + { + return layers_.size(); + } + + const RenderStyle& GetLayerStyle(size_t layer) const; + + void SetLayerStyle(size_t layer, + const RenderStyle& style); + + void SetSlice(const OrthancStone::CoordinateSystem3D& plane); + + const OrthancStone::CoordinateSystem3D& GetSlice() const + { + return plane_; + } + + virtual bool HasAnimation() const + { + return true; + } + + virtual void DoAnimation(); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/TestCairoWidget.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/TestCairoWidget.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,126 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "TestCairoWidget.h" + +#include + + +namespace Deprecated +{ + namespace Samples + { + void TestCairoWidget::DoAnimation() + { + value_ -= 0.01f; + if (value_ < 0) + { + value_ = 1; + } + + NotifyContentChanged(); + } + + + bool TestCairoWidget::RenderCairo(OrthancStone::CairoContext& context) + { + cairo_t* cr = context.GetObject(); + + cairo_set_source_rgb (cr, .3, 0, 0); + cairo_paint(cr); + + cairo_set_source_rgb(cr, 0, 1, 0); + cairo_rectangle(cr, width_ / 4, height_ / 4, width_ / 2, height_ / 2); + cairo_set_line_width(cr, 1.0); + cairo_fill(cr); + + cairo_set_source_rgb(cr, 0, 1, value_); + cairo_rectangle(cr, width_ / 2 - 50, height_ / 2 - 50, 100, 100); + cairo_fill(cr); + + return true; + } + + + void TestCairoWidget::RenderMouseOverCairo(OrthancStone::CairoContext& context, + int x, + int y) + { + cairo_t* cr = context.GetObject(); + + cairo_set_source_rgb (cr, 1, 0, 0); + cairo_rectangle(cr, x - 5, y - 5, 10, 10); + cairo_set_line_width(cr, 1.0); + cairo_stroke(cr); + + char buf[64]; + sprintf(buf, "(%d,%d)", x, y); + UpdateStatusBar(buf); + } + + + TestCairoWidget::TestCairoWidget(const std::string& name, bool animate) : + CairoWidget(name), + width_(0), + height_(0), + value_(1), + animate_(animate) + { + } + + + void TestCairoWidget::SetSize(unsigned int width, + unsigned int height) + { + CairoWidget::SetSize(width, height); + width_ = width; + height_ = height; + } + + + IMouseTracker* TestCairoWidget::CreateMouseTracker(OrthancStone::MouseButton button, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers, + const std::vector& touches) + { + UpdateStatusBar("Click"); + return NULL; + } + + + void TestCairoWidget::MouseWheel(OrthancStone::MouseWheelDirection direction, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers) + { + UpdateStatusBar(direction == OrthancStone::MouseWheelDirection_Down ? "Wheel down" : "Wheel up"); + } + + + void TestCairoWidget::KeyPressed(OrthancStone::KeyboardKeys key, + char keyChar, + OrthancStone::KeyboardModifiers modifiers) + { + UpdateStatusBar("Key pressed: \"" + std::string(1, keyChar) + "\""); + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/TestCairoWidget.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/TestCairoWidget.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,79 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "CairoWidget.h" + +namespace Deprecated +{ + namespace Samples + { + class TestCairoWidget : public CairoWidget + { + private: + unsigned int width_; + unsigned int height_; + float value_; + bool animate_; + + protected: + virtual bool RenderCairo(OrthancStone::CairoContext& context); + + virtual void RenderMouseOverCairo(OrthancStone::CairoContext& context, + int x, + int y); + + public: + TestCairoWidget(const std::string& name, bool animate); + + virtual void SetSize(unsigned int width, + unsigned int height); + + virtual IMouseTracker* CreateMouseTracker(OrthancStone::MouseButton button, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers, + const std::vector& touches); + + virtual void MouseWheel(OrthancStone::MouseWheelDirection direction, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers); + + virtual void KeyPressed(OrthancStone::KeyboardKeys key, + char keyChar, + OrthancStone::KeyboardModifiers modifiers); + + virtual bool HasAnimation() const + { + return animate_; + } + + virtual void DoAnimation(); + + virtual bool HasRenderMouseOver() + { + return true; + } + }; + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/TestWorldSceneWidget.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/TestWorldSceneWidget.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,149 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "TestWorldSceneWidget.h" + +#include + +#include +#include + +namespace Deprecated +{ + namespace Samples + { + class TestWorldSceneWidget::Interactor : public IWorldSceneInteractor + { + public: + virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, + const ViewportGeometry& view, + OrthancStone::MouseButton button, + OrthancStone::KeyboardModifiers modifiers, + int viewportX, + int viewportY, + double x, + double y, + IStatusBar* statusBar, + const std::vector& touches) + { + if (statusBar) + { + char buf[64]; + sprintf(buf, "X = %0.2f, Y = %0.2f", x, y); + statusBar->SetMessage(buf); + } + + return NULL; + } + + virtual void MouseOver(OrthancStone::CairoContext& context, + WorldSceneWidget& widget, + const ViewportGeometry& view, + double x, + double y, + IStatusBar* statusBar) + { + double S = 0.5; + + if (fabs(x) <= S && + fabs(y) <= S) + { + cairo_t* cr = context.GetObject(); + cairo_set_source_rgb(cr, 1, 0, 0); + cairo_rectangle(cr, -S, -S , 2.0 * S, 2.0 * S); + cairo_set_line_width(cr, 1.0 / view.GetZoom()); + cairo_stroke(cr); + } + } + + virtual void MouseWheel(WorldSceneWidget& widget, + OrthancStone::MouseWheelDirection direction, + OrthancStone::KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + if (statusBar) + { + statusBar->SetMessage(direction == OrthancStone::MouseWheelDirection_Down ? "Wheel down" : "Wheel up"); + } + } + + virtual void KeyPressed(WorldSceneWidget& widget, + OrthancStone::KeyboardKeys key, + char keyChar, + OrthancStone::KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + if (statusBar) + { + statusBar->SetMessage("Key pressed: \"" + std::string(1, keyChar) + "\""); + } + } + }; + + + bool TestWorldSceneWidget::RenderScene(OrthancStone::CairoContext& context, + const ViewportGeometry& view) + { + cairo_t* cr = context.GetObject(); + + // Clear background + cairo_set_source_rgb(cr, 0, 0, 0); + cairo_paint(cr); + + float color = static_cast(count_ % 16) / 15.0f; + cairo_set_source_rgb(cr, 0, 1.0f - color, color); + cairo_rectangle(cr, -10, -.5, 20, 1); + cairo_fill(cr); + + return true; + } + + + TestWorldSceneWidget::TestWorldSceneWidget(const std::string& name, bool animate) : + WorldSceneWidget(name), + interactor_(new Interactor), + animate_(animate), + count_(0) + { + SetInteractor(*interactor_); + } + + + OrthancStone::Extent2D TestWorldSceneWidget::GetSceneExtent() + { + return OrthancStone::Extent2D(-10, -.5, 10, .5); + } + + + void TestWorldSceneWidget::DoAnimation() + { + if (animate_) + { + count_++; + NotifyContentChanged(); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/TestWorldSceneWidget.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/TestWorldSceneWidget.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,63 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "WorldSceneWidget.h" + +#include + +namespace Deprecated +{ + namespace Samples + { + class TestWorldSceneWidget : public WorldSceneWidget + { + private: + class Interactor; + + std::auto_ptr interactor_; + bool animate_; + unsigned int count_; + + protected: + virtual bool RenderScene(OrthancStone::CairoContext& context, + const ViewportGeometry& view); + + public: + TestWorldSceneWidget(const std::string& name, bool animate); + + virtual OrthancStone::Extent2D GetSceneExtent(); + + virtual bool HasAnimation() const + { + return animate_; + } + + virtual void DoAnimation(); + + virtual bool HasRenderMouseOver() + { + return true; + } + }; + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/WidgetBase.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/WidgetBase.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,166 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "WidgetBase.h" + +#include +#include +#include + +namespace Deprecated +{ + void WidgetBase::NotifyContentChanged() + { + if (parent_ != NULL) + { + parent_->NotifyContentChanged(); + } + + if (viewport_ != NULL) + { + viewport_->NotifyBackgroundChanged(); + } + } + + + void WidgetBase::SetParent(IWidget& parent) + { + if (parent_ != NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + parent_ = &parent; + } + } + + + void WidgetBase::ClearBackgroundOrthanc(Orthanc::ImageAccessor& target) const + { + // Clear the background using Orthanc + + if (backgroundCleared_) + { + Orthanc::ImageProcessing::Set(target, + backgroundColor_[0], + backgroundColor_[1], + backgroundColor_[2], + 255 /* alpha */); + } + } + + + void WidgetBase::ClearBackgroundCairo(OrthancStone::CairoContext& context) const + { + // Clear the background using Cairo + + if (IsBackgroundCleared()) + { + uint8_t red, green, blue; + GetBackgroundColor(red, green, blue); + + context.SetSourceColor(red, green, blue); + cairo_paint(context.GetObject()); + } + } + + + void WidgetBase::ClearBackgroundCairo(Orthanc::ImageAccessor& target) const + { + OrthancStone::CairoSurface surface(target, false /* no alpha */); + OrthancStone::CairoContext context(surface); + ClearBackgroundCairo(context); + } + + + void WidgetBase::UpdateStatusBar(const std::string& message) + { + if (statusBar_ != NULL) + { + statusBar_->SetMessage(message); + } + } + + + WidgetBase::WidgetBase(const std::string& name) : + parent_(NULL), + viewport_(NULL), + statusBar_(NULL), + backgroundCleared_(false), + transmitMouseOver_(false), + name_(name) + { + backgroundColor_[0] = 0; + backgroundColor_[1] = 0; + backgroundColor_[2] = 0; + } + + + void WidgetBase::SetViewport(WidgetViewport& viewport) + { + if (viewport_ != NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + viewport_ = &viewport; + } + } + + + void WidgetBase::SetBackgroundColor(uint8_t red, + uint8_t green, + uint8_t blue) + { + backgroundColor_[0] = red; + backgroundColor_[1] = green; + backgroundColor_[2] = blue; + } + + void WidgetBase::GetBackgroundColor(uint8_t& red, + uint8_t& green, + uint8_t& blue) const + { + red = backgroundColor_[0]; + green = backgroundColor_[1]; + blue = backgroundColor_[2]; + } + + + bool WidgetBase::Render(Orthanc::ImageAccessor& surface) + { +#if 0 + ClearBackgroundOrthanc(surface); +#else + ClearBackgroundCairo(surface); // Faster than Orthanc +#endif + + return true; + } + + + void WidgetBase::DoAnimation() + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/WidgetBase.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/WidgetBase.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,117 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IWidget.h" + +#include "../../Viewport/CairoContext.h" +#include "../Viewport/WidgetViewport.h" + +namespace Deprecated +{ + class WidgetBase : public IWidget + { + private: + IWidget* parent_; + WidgetViewport* viewport_; + IStatusBar* statusBar_; + bool backgroundCleared_; + uint8_t backgroundColor_[3]; + bool transmitMouseOver_; + std::string name_; + + protected: + void ClearBackgroundOrthanc(Orthanc::ImageAccessor& target) const; + + void ClearBackgroundCairo(OrthancStone::CairoContext& context) const; + + void ClearBackgroundCairo(Orthanc::ImageAccessor& target) const; + + void UpdateStatusBar(const std::string& message); + + IStatusBar* GetStatusBar() const + { + return statusBar_; + } + + public: + WidgetBase(const std::string& name); + + virtual void FitContent() + { + } + + virtual void SetParent(IWidget& parent); + + virtual void SetViewport(WidgetViewport& viewport); + + void SetBackgroundCleared(bool clear) + { + backgroundCleared_ = clear; + } + + bool IsBackgroundCleared() const + { + return backgroundCleared_; + } + + void SetTransmitMouseOver(bool transmit) + { + transmitMouseOver_ = transmit; + } + + void SetBackgroundColor(uint8_t red, + uint8_t green, + uint8_t blue); + + void GetBackgroundColor(uint8_t& red, + uint8_t& green, + uint8_t& blue) const; + + virtual void SetStatusBar(IStatusBar& statusBar) + { + statusBar_ = &statusBar; + } + + virtual bool Render(Orthanc::ImageAccessor& surface); + + virtual bool HasAnimation() const + { + return false; + } + + virtual void DoAnimation(); + + virtual bool HasRenderMouseOver() + { + return transmitMouseOver_; + } + + virtual void NotifyContentChanged(); + + const std::string& GetName() const + { + return name_; + } + + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/WorldSceneWidget.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/WorldSceneWidget.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,231 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "WorldSceneWidget.h" + +#include "PanMouseTracker.h" +#include "ZoomMouseTracker.h" +#include "PanZoomMouseTracker.h" + +#include +#include + +#include +#include +#include + +namespace Deprecated +{ + // this is an adapter between a IWorldSceneMouseTracker + // that is tracking a mouse in scene coordinates/mm and + // an IMouseTracker that is tracking a mouse + // in screen coordinates/pixels. + class WorldSceneWidget::SceneMouseTracker : public IMouseTracker + { + private: + ViewportGeometry view_; + std::auto_ptr tracker_; + + public: + SceneMouseTracker(const ViewportGeometry& view, + IWorldSceneMouseTracker* tracker) : + view_(view), + tracker_(tracker) + { + if (tracker == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + virtual void Render(Orthanc::ImageAccessor& target) + { + if (tracker_->HasRender()) + { + OrthancStone::CairoSurface surface(target, false /* no alpha */); + OrthancStone::CairoContext context(surface); + view_.ApplyTransform(context); + tracker_->Render(context, view_.GetZoom()); + } + } + + virtual void MouseUp() + { + tracker_->MouseUp(); + } + + virtual void MouseMove(int x, + int y, + const std::vector& displayTouches) + { + double sceneX, sceneY; + view_.MapPixelCenterToScene(sceneX, sceneY, x, y); + + std::vector sceneTouches; + for (size_t t = 0; t < displayTouches.size(); t++) + { + double sx, sy; + + view_.MapPixelCenterToScene( + sx, sy, (int)displayTouches[t].x, (int)displayTouches[t].y); + + sceneTouches.push_back( + Touch(static_cast(sx), static_cast(sy))); + } + tracker_->MouseMove(x, y, sceneX, sceneY, displayTouches, sceneTouches); + } + }; + + + bool WorldSceneWidget::RenderCairo(OrthancStone::CairoContext& context) + { + view_.ApplyTransform(context); + return RenderScene(context, view_); + } + + + void WorldSceneWidget::RenderMouseOverCairo(OrthancStone::CairoContext& context, + int x, + int y) + { + ViewportGeometry view = GetView(); + view.ApplyTransform(context); + + double sceneX, sceneY; + view.MapPixelCenterToScene(sceneX, sceneY, x, y); + + if (interactor_) + { + interactor_->MouseOver(context, *this, view, sceneX, sceneY, GetStatusBar()); + } + } + + + void WorldSceneWidget::SetSceneExtent(ViewportGeometry& view) + { + view.SetSceneExtent(GetSceneExtent()); + } + + + void WorldSceneWidget::SetSize(unsigned int width, + unsigned int height) + { + CairoWidget::SetSize(width, height); + view_.SetDisplaySize(width, height); + } + + + void WorldSceneWidget::SetInteractor(IWorldSceneInteractor& interactor) + { + interactor_ = &interactor; + } + + + void WorldSceneWidget::FitContent() + { + SetSceneExtent(view_); + view_.FitContent(); + + NotifyContentChanged(); + } + + + void WorldSceneWidget::SetView(const ViewportGeometry& view) + { + view_ = view; + + NotifyContentChanged(); + } + + + IMouseTracker* WorldSceneWidget::CreateMouseTracker(OrthancStone::MouseButton button, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers, + const std::vector& touches) + { + double sceneX, sceneY; + view_.MapPixelCenterToScene(sceneX, sceneY, x, y); + + // asks the Widget Interactor to provide a mouse tracker + std::auto_ptr tracker; + + if (interactor_) + { + tracker.reset(interactor_->CreateMouseTracker(*this, view_, button, modifiers, x, y, sceneX, sceneY, GetStatusBar(), touches)); + } + + if (tracker.get() != NULL) + { + return new SceneMouseTracker(view_, tracker.release()); + } + else if (hasDefaultMouseEvents_) + { + printf("has default mouse events\n"); + if (touches.size() == 2) + { + printf("2 touches !\n"); + return new SceneMouseTracker(view_, new PanZoomMouseTracker(*this, touches)); + } + else + { + switch (button) + { + case OrthancStone::MouseButton_Middle: + return new SceneMouseTracker(view_, new PanMouseTracker(*this, x, y)); + + case OrthancStone::MouseButton_Right: + return new SceneMouseTracker(view_, new ZoomMouseTracker(*this, x, y)); + + default: + return NULL; + } + } + } + else + { + return NULL; + } + } + + + void WorldSceneWidget::MouseWheel(OrthancStone::MouseWheelDirection direction, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers) + { + if (interactor_) + { + interactor_->MouseWheel(*this, direction, modifiers, GetStatusBar()); + } + } + + + void WorldSceneWidget::KeyPressed(OrthancStone::KeyboardKeys key, + char keyChar, + OrthancStone::KeyboardModifiers modifiers) + { + if (interactor_) + { + interactor_->KeyPressed(*this, key, keyChar, modifiers, GetStatusBar()); + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/WorldSceneWidget.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/WorldSceneWidget.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,103 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "CairoWidget.h" +#include "IWorldSceneInteractor.h" + +#include "../Toolbox/ViewportGeometry.h" + +namespace Deprecated +{ + class WorldSceneWidget : public CairoWidget + { + private: + class SceneMouseTracker; + + ViewportGeometry view_; + IWorldSceneInteractor* interactor_; + bool hasDefaultMouseEvents_; + + protected: + virtual OrthancStone::Extent2D GetSceneExtent() = 0; + + virtual bool RenderScene(OrthancStone::CairoContext& context, + const ViewportGeometry& view) = 0; + + // From CairoWidget + virtual bool RenderCairo(OrthancStone::CairoContext& context); + + // From CairoWidget + virtual void RenderMouseOverCairo(OrthancStone::CairoContext& context, + int x, + int y); + + void SetSceneExtent(ViewportGeometry& geometry); + + public: + WorldSceneWidget(const std::string& name) : + CairoWidget(name), + interactor_(NULL), + hasDefaultMouseEvents_(true) + { + } + + void SetDefaultMouseEvents(bool value) + { + hasDefaultMouseEvents_ = value; + } + + bool HasDefaultMouseEvents() const + { + return hasDefaultMouseEvents_; + } + + void SetInteractor(IWorldSceneInteractor& interactor); + + void SetView(const ViewportGeometry& view); + + const ViewportGeometry& GetView() const + { + return view_; + } + + virtual void SetSize(unsigned int width, + unsigned int height); + + virtual void FitContent(); + + virtual IMouseTracker* CreateMouseTracker(OrthancStone::MouseButton button, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers, + const std::vector& touches); + + virtual void MouseWheel(OrthancStone::MouseWheelDirection direction, + int x, + int y, + OrthancStone::KeyboardModifiers modifiers); + + virtual void KeyPressed(OrthancStone::KeyboardKeys key, + char keyChar, + OrthancStone::KeyboardModifiers modifiers); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/ZoomMouseTracker.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/ZoomMouseTracker.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,110 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "ZoomMouseTracker.h" + +#include +#include + +namespace Deprecated +{ + ZoomMouseTracker::ZoomMouseTracker(WorldSceneWidget& that, + int x, + int y) : + that_(that), + originalZoom_(that.GetView().GetZoom()), + downX_(x), + downY_(y) + { + that.GetView().MapPixelCenterToScene(centerX_, centerY_, x, y); + + unsigned int height = that.GetView().GetDisplayHeight(); + + if (height <= 3) + { + idle_ = true; + LOG(WARNING) << "image is too small to zoom (current height = " << height << ")"; + } + else + { + idle_ = false; + normalization_ = 1.0 / static_cast(height - 1); + } + } + + + void ZoomMouseTracker::Render(OrthancStone::CairoContext& context, + double zoom) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + + void ZoomMouseTracker::MouseMove(int displayX, + int displayY, + double x, + double y, + const std::vector& displayTouches, + const std::vector& sceneTouches) + { + static const double MIN_ZOOM = -4; + static const double MAX_ZOOM = 4; + + + if (!idle_) + { + double dy = static_cast(displayY - downY_) * normalization_; // In the range [-1,1] + double z; + + // Linear interpolation from [-1, 1] to [MIN_ZOOM, MAX_ZOOM] + if (dy < -1.0) + { + z = MIN_ZOOM; + } + else if (dy > 1.0) + { + z = MAX_ZOOM; + } + else + { + z = MIN_ZOOM + (MAX_ZOOM - MIN_ZOOM) * (dy + 1.0) / 2.0; + } + + z = pow(2.0, z); + + ViewportGeometry view = that_.GetView(); + + view.SetZoom(z * originalZoom_); + + // Correct the pan so that the original click point is kept at + // the same location on the display + double panX, panY; + view.GetPan(panX, panY); + + int tx, ty; + view.MapSceneToDisplay(tx, ty, centerX_, centerY_); + view.SetPan(panX + static_cast(downX_ - tx), + panY + static_cast(downY_ - ty)); + + that_.SetView(view); + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/Widgets/ZoomMouseTracker.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Widgets/ZoomMouseTracker.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,64 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "WorldSceneWidget.h" + +namespace Deprecated +{ + class ZoomMouseTracker : public IWorldSceneMouseTracker + { + private: + WorldSceneWidget& that_; + double originalZoom_; + int downX_; + int downY_; + double centerX_; + double centerY_; + bool idle_; + double normalization_; + + public: + ZoomMouseTracker(WorldSceneWidget& that, + int x, + int y); + + virtual bool HasRender() const + { + return false; + } + + virtual void MouseUp() + { + } + + virtual void Render(OrthancStone::CairoContext& context, + double zoom); + + virtual void MouseMove(int displayX, + int displayY, + double x, + double y, + const std::vector& displayTouches, + const std::vector& sceneTouches); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Deprecated/dev.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/dev.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,958 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "Layers/FrameRenderer.h" +#include "Layers/LineLayerRenderer.h" +#include "Layers/SliceOutlineRenderer.h" +#include "Toolbox/DownloadStack.h" +#include "Toolbox/GeometryToolbox.h" +#include "Toolbox/OrthancSlicesLoader.h" +#include "Volumes/ISlicedVolume.h" +#include "Volumes/ImageBuffer3D.h" +#include "Widgets/SliceViewerWidget.h" + +#include +#include +#include + +#include + + +namespace Deprecated +{ + // TODO: Handle errors while loading + class OrthancVolumeImage : + public ISlicedVolume, + public OrthancStone::IObserver + { + private: + OrthancSlicesLoader loader_; + std::auto_ptr image_; + std::auto_ptr downloadStack_; + bool computeRange_; + size_t pendingSlices_; + + void ScheduleSliceDownload() + { + assert(downloadStack_.get() != NULL); + + unsigned int slice; + if (downloadStack_->Pop(slice)) + { + loader_.ScheduleLoadSliceImage(slice, OrthancStone::SliceImageQuality_Jpeg90); + } + } + + + static bool IsCompatible(const Slice& a, + const Slice& b) + { + if (!OrthancStone::GeometryToolbox::IsParallel(a.GetGeometry().GetNormal(), + b.GetGeometry().GetNormal())) + { + LOG(ERROR) << "A slice in the volume image is not parallel to the others."; + return false; + } + + if (a.GetConverter().GetExpectedPixelFormat() != b.GetConverter().GetExpectedPixelFormat()) + { + LOG(ERROR) << "The pixel format changes across the slices of the volume image."; + return false; + } + + if (a.GetWidth() != b.GetWidth() || + a.GetHeight() != b.GetHeight()) + { + LOG(ERROR) << "The slices dimensions (width/height) are varying throughout the volume image"; + return false; + } + + if (!OrthancStone::LinearAlgebra::IsNear(a.GetPixelSpacingX(), b.GetPixelSpacingX()) || + !OrthancStone::LinearAlgebra::IsNear(a.GetPixelSpacingY(), b.GetPixelSpacingY())) + { + LOG(ERROR) << "The pixel spacing of the slices change across the volume image"; + return false; + } + + return true; + } + + + static double GetDistance(const Slice& a, + const Slice& b) + { + return fabs(a.GetGeometry().ProjectAlongNormal(a.GetGeometry().GetOrigin()) - + a.GetGeometry().ProjectAlongNormal(b.GetGeometry().GetOrigin())); + } + + + void OnSliceGeometryReady(const OrthancSlicesLoader::SliceGeometryReadyMessage& message) + { + assert(&message.GetOrigin() == &loader_); + + if (loader_.GetSlicesCount() == 0) + { + LOG(ERROR) << "Empty volume image"; + BroadcastMessage(ISlicedVolume::GeometryErrorMessage(*this)); + return; + } + + for (size_t i = 1; i < loader_.GetSlicesCount(); i++) + { + if (!IsCompatible(loader_.GetSlice(0), loader_.GetSlice(i))) + { + BroadcastMessage(ISlicedVolume::GeometryErrorMessage(*this)); + return; + } + } + + double spacingZ; + + if (loader_.GetSlicesCount() > 1) + { + spacingZ = GetDistance(loader_.GetSlice(0), loader_.GetSlice(1)); + } + else + { + // This is a volume with one single slice: Choose a dummy + // z-dimension for voxels + spacingZ = 1; + } + + for (size_t i = 1; i < loader_.GetSlicesCount(); i++) + { + if (!OrthancStone::LinearAlgebra::IsNear(spacingZ, GetDistance(loader_.GetSlice(i - 1), loader_.GetSlice(i)), + 0.001 /* this is expressed in mm */)) + { + LOG(ERROR) << "The distance between successive slices is not constant in a volume image"; + BroadcastMessage(ISlicedVolume::GeometryErrorMessage(*this)); + return; + } + } + + unsigned int width = loader_.GetSlice(0).GetWidth(); + unsigned int height = loader_.GetSlice(0).GetHeight(); + Orthanc::PixelFormat format = loader_.GetSlice(0).GetConverter().GetExpectedPixelFormat(); + LOG(INFO) << "Creating a volume image of size " << width << "x" << height + << "x" << loader_.GetSlicesCount() << " in " << Orthanc::EnumerationToString(format); + + image_.reset(new OrthancStone::ImageBuffer3D(format, width, height, static_cast(loader_.GetSlicesCount()), computeRange_)); + image_->GetGeometry().SetAxialGeometry(loader_.GetSlice(0).GetGeometry()); + image_->GetGeometry().SetVoxelDimensions(loader_.GetSlice(0).GetPixelSpacingX(), + loader_.GetSlice(0).GetPixelSpacingY(), spacingZ); + image_->Clear(); + + downloadStack_.reset(new DownloadStack(static_cast(loader_.GetSlicesCount()))); + pendingSlices_ = loader_.GetSlicesCount(); + + for (unsigned int i = 0; i < 4; i++) // Limit to 4 simultaneous downloads + { + ScheduleSliceDownload(); + } + + // TODO Check the DicomFrameConverter are constant + + BroadcastMessage(ISlicedVolume::GeometryReadyMessage(*this)); + } + + + void OnSliceGeometryError(const OrthancSlicesLoader::SliceGeometryErrorMessage& message) + { + assert(&message.GetOrigin() == &loader_); + + LOG(ERROR) << "Unable to download a volume image"; + BroadcastMessage(ISlicedVolume::GeometryErrorMessage(*this)); + } + + + void OnSliceImageReady(const OrthancSlicesLoader::SliceImageReadyMessage& message) + { + assert(&message.GetOrigin() == &loader_); + + { + OrthancStone::ImageBuffer3D::SliceWriter writer(*image_, OrthancStone::VolumeProjection_Axial, message.GetSliceIndex()); + Orthanc::ImageProcessing::Copy(writer.GetAccessor(), message.GetImage()); + } + + BroadcastMessage(ISlicedVolume::SliceContentChangedMessage + (*this, message.GetSliceIndex(), message.GetSlice())); + + if (pendingSlices_ == 1) + { + BroadcastMessage(ISlicedVolume::VolumeReadyMessage(*this)); + pendingSlices_ = 0; + } + else if (pendingSlices_ > 1) + { + pendingSlices_ -= 1; + } + + ScheduleSliceDownload(); + } + + + void OnSliceImageError(const OrthancSlicesLoader::SliceImageErrorMessage& message) + { + assert(&message.GetOrigin() == &loader_); + + LOG(ERROR) << "Cannot download slice " << message.GetSliceIndex() << " in a volume image"; + ScheduleSliceDownload(); + } + + + public: + OrthancVolumeImage(OrthancStone::MessageBroker& broker, + OrthancApiClient& orthanc, + bool computeRange) : + ISlicedVolume(broker), + IObserver(broker), + loader_(broker, orthanc), + computeRange_(computeRange), + pendingSlices_(0) + { + loader_.RegisterObserverCallback( + new OrthancStone::Callable + (*this, &OrthancVolumeImage::OnSliceGeometryReady)); + + loader_.RegisterObserverCallback( + new OrthancStone::Callable + (*this, &OrthancVolumeImage::OnSliceGeometryError)); + + loader_.RegisterObserverCallback( + new OrthancStone::Callable + (*this, &OrthancVolumeImage::OnSliceImageReady)); + + loader_.RegisterObserverCallback( + new OrthancStone::Callable + (*this, &OrthancVolumeImage::OnSliceImageError)); + } + + void ScheduleLoadSeries(const std::string& seriesId) + { + loader_.ScheduleLoadSeries(seriesId); + } + + void ScheduleLoadInstance(const std::string& instanceId) + { + loader_.ScheduleLoadInstance(instanceId); + } + + void ScheduleLoadFrame(const std::string& instanceId, + unsigned int frame) + { + loader_.ScheduleLoadFrame(instanceId, frame); + } + + virtual size_t GetSlicesCount() const + { + return loader_.GetSlicesCount(); + } + + virtual const Slice& GetSlice(size_t index) const + { + return loader_.GetSlice(index); + } + + OrthancStone::ImageBuffer3D& GetImage() const + { + if (image_.get() == NULL) + { + // The geometry is not ready yet + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + return *image_; + } + } + + bool FitWindowingToRange(RenderStyle& style, + const DicomFrameConverter& converter) const + { + if (image_.get() == NULL) + { + return false; + } + else + { + return image_->FitWindowingToRange(style, converter); + } + } + }; + + + class VolumeImageGeometry + { + private: + unsigned int width_; + unsigned int height_; + size_t depth_; + double pixelSpacingX_; + double pixelSpacingY_; + double sliceThickness_; + OrthancStone::CoordinateSystem3D reference_; + DicomFrameConverter converter_; + + double ComputeAxialThickness(const OrthancVolumeImage& volume) const + { + double thickness; + + size_t n = volume.GetSlicesCount(); + if (n > 1) + { + const Slice& a = volume.GetSlice(0); + const Slice& b = volume.GetSlice(n - 1); + thickness = ((reference_.ProjectAlongNormal(b.GetGeometry().GetOrigin()) - + reference_.ProjectAlongNormal(a.GetGeometry().GetOrigin())) / + (static_cast(n) - 1.0)); + } + else + { + thickness = volume.GetSlice(0).GetThickness(); + } + + if (thickness <= 0) + { + // The slices should have been sorted with increasing Z + // (along the normal) by the OrthancSlicesLoader + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + else + { + return thickness; + } + } + + void SetupAxial(const OrthancVolumeImage& volume) + { + const Slice& axial = volume.GetSlice(0); + + width_ = axial.GetWidth(); + height_ = axial.GetHeight(); + depth_ = volume.GetSlicesCount(); + + pixelSpacingX_ = axial.GetPixelSpacingX(); + pixelSpacingY_ = axial.GetPixelSpacingY(); + sliceThickness_ = ComputeAxialThickness(volume); + + reference_ = axial.GetGeometry(); + } + + void SetupCoronal(const OrthancVolumeImage& volume) + { + const Slice& axial = volume.GetSlice(0); + double axialThickness = ComputeAxialThickness(volume); + + width_ = axial.GetWidth(); + height_ = static_cast(volume.GetSlicesCount()); + depth_ = axial.GetHeight(); + + pixelSpacingX_ = axial.GetPixelSpacingX(); + pixelSpacingY_ = axialThickness; + sliceThickness_ = axial.GetPixelSpacingY(); + + OrthancStone::Vector origin = axial.GetGeometry().GetOrigin(); + origin += (static_cast(volume.GetSlicesCount() - 1) * + axialThickness * axial.GetGeometry().GetNormal()); + + reference_ = OrthancStone::CoordinateSystem3D(origin, + axial.GetGeometry().GetAxisX(), + - axial.GetGeometry().GetNormal()); + } + + void SetupSagittal(const OrthancVolumeImage& volume) + { + const Slice& axial = volume.GetSlice(0); + double axialThickness = ComputeAxialThickness(volume); + + width_ = axial.GetHeight(); + height_ = static_cast(volume.GetSlicesCount()); + depth_ = axial.GetWidth(); + + pixelSpacingX_ = axial.GetPixelSpacingY(); + pixelSpacingY_ = axialThickness; + sliceThickness_ = axial.GetPixelSpacingX(); + + OrthancStone::Vector origin = axial.GetGeometry().GetOrigin(); + origin += (static_cast(volume.GetSlicesCount() - 1) * + axialThickness * axial.GetGeometry().GetNormal()); + + reference_ = OrthancStone::CoordinateSystem3D(origin, + axial.GetGeometry().GetAxisY(), + axial.GetGeometry().GetNormal()); + } + + public: + VolumeImageGeometry(const OrthancVolumeImage& volume, + OrthancStone::VolumeProjection projection) + { + if (volume.GetSlicesCount() == 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + converter_ = volume.GetSlice(0).GetConverter(); + + switch (projection) + { + case OrthancStone::VolumeProjection_Axial: + SetupAxial(volume); + break; + + case OrthancStone::VolumeProjection_Coronal: + SetupCoronal(volume); + break; + + case OrthancStone::VolumeProjection_Sagittal: + SetupSagittal(volume); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + size_t GetSlicesCount() const + { + return depth_; + } + + const OrthancStone::Vector& GetNormal() const + { + return reference_.GetNormal(); + } + + bool LookupSlice(size_t& index, + const OrthancStone::CoordinateSystem3D& slice) const + { + bool opposite; + if (!OrthancStone::GeometryToolbox::IsParallelOrOpposite(opposite, + reference_.GetNormal(), + slice.GetNormal())) + { + return false; + } + + double z = (reference_.ProjectAlongNormal(slice.GetOrigin()) - + reference_.ProjectAlongNormal(reference_.GetOrigin())) / sliceThickness_; + + int s = static_cast(boost::math::iround(z)); + + if (s < 0 || + s >= static_cast(depth_)) + { + return false; + } + else + { + index = static_cast(s); + return true; + } + } + + Slice* GetSlice(size_t slice) const + { + if (slice >= depth_) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + OrthancStone::CoordinateSystem3D origin(reference_.GetOrigin() + + static_cast(slice) * sliceThickness_ * reference_.GetNormal(), + reference_.GetAxisX(), + reference_.GetAxisY()); + + return new Slice(origin, pixelSpacingX_, pixelSpacingY_, sliceThickness_, + width_, height_, converter_); + } + } + }; + + + + class VolumeImageMPRSlicer : + public IVolumeSlicer, + public OrthancStone::IObserver + { + private: + class RendererFactory : public LayerReadyMessage::IRendererFactory + { + private: + const Orthanc::ImageAccessor& frame_; + const Slice& slice_; + bool isFullQuality_; + + public: + RendererFactory(const Orthanc::ImageAccessor& frame, + const Slice& slice, + bool isFullQuality) : + frame_(frame), + slice_(slice), + isFullQuality_(isFullQuality) + { + } + + virtual ILayerRenderer* CreateRenderer() const + { + return FrameRenderer::CreateRenderer(frame_, slice_, isFullQuality_); + } + }; + + + OrthancVolumeImage& volume_; + std::auto_ptr axialGeometry_; + std::auto_ptr coronalGeometry_; + std::auto_ptr sagittalGeometry_; + + + bool IsGeometryReady() const + { + return axialGeometry_.get() != NULL; + } + + void OnGeometryReady(const ISlicedVolume::GeometryReadyMessage& message) + { + assert(&message.GetOrigin() == &volume_); + + // These 3 values are only used to speed up the IVolumeSlicer + axialGeometry_.reset(new VolumeImageGeometry(volume_, OrthancStone::VolumeProjection_Axial)); + coronalGeometry_.reset(new VolumeImageGeometry(volume_, OrthancStone::VolumeProjection_Coronal)); + sagittalGeometry_.reset(new VolumeImageGeometry(volume_, OrthancStone::VolumeProjection_Sagittal)); + + BroadcastMessage(IVolumeSlicer::GeometryReadyMessage(*this)); + } + + void OnGeometryError(const ISlicedVolume::GeometryErrorMessage& message) + { + assert(&message.GetOrigin() == &volume_); + + BroadcastMessage(IVolumeSlicer::GeometryErrorMessage(*this)); + } + + void OnContentChanged(const ISlicedVolume::ContentChangedMessage& message) + { + assert(&message.GetOrigin() == &volume_); + + BroadcastMessage(IVolumeSlicer::ContentChangedMessage(*this)); + } + + void OnSliceContentChanged(const ISlicedVolume::SliceContentChangedMessage& message) + { + assert(&message.GetOrigin() == &volume_); + + //IVolumeSlicer::OnSliceContentChange(slice); + + // TODO Improve this? + BroadcastMessage(IVolumeSlicer::ContentChangedMessage(*this)); + } + + const VolumeImageGeometry& GetProjectionGeometry(OrthancStone::VolumeProjection projection) + { + if (!IsGeometryReady()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + switch (projection) + { + case OrthancStone::VolumeProjection_Axial: + return *axialGeometry_; + + case OrthancStone::VolumeProjection_Sagittal: + return *sagittalGeometry_; + + case OrthancStone::VolumeProjection_Coronal: + return *coronalGeometry_; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + + bool DetectProjection(OrthancStone::VolumeProjection& projection, + const OrthancStone::CoordinateSystem3D& viewportSlice) + { + bool isOpposite; // Ignored + + if (OrthancStone::GeometryToolbox::IsParallelOrOpposite(isOpposite, + viewportSlice.GetNormal(), + axialGeometry_->GetNormal())) + { + projection = OrthancStone::VolumeProjection_Axial; + return true; + } + else if (OrthancStone::GeometryToolbox::IsParallelOrOpposite(isOpposite, + viewportSlice.GetNormal(), + sagittalGeometry_->GetNormal())) + { + projection = OrthancStone::VolumeProjection_Sagittal; + return true; + } + else if (OrthancStone::GeometryToolbox::IsParallelOrOpposite(isOpposite, + viewportSlice.GetNormal(), + coronalGeometry_->GetNormal())) + { + projection = OrthancStone::VolumeProjection_Coronal; + return true; + } + else + { + return false; + } + } + + + public: + VolumeImageMPRSlicer(OrthancStone::MessageBroker& broker, + OrthancVolumeImage& volume) : + IVolumeSlicer(broker), + IObserver(broker), + volume_(volume) + { + volume_.RegisterObserverCallback( + new OrthancStone::Callable + (*this, &VolumeImageMPRSlicer::OnGeometryReady)); + + volume_.RegisterObserverCallback( + new OrthancStone::Callable + (*this, &VolumeImageMPRSlicer::OnGeometryError)); + + volume_.RegisterObserverCallback( + new OrthancStone::Callable + (*this, &VolumeImageMPRSlicer::OnContentChanged)); + + volume_.RegisterObserverCallback( + new OrthancStone::Callable + (*this, &VolumeImageMPRSlicer::OnSliceContentChanged)); + } + + virtual bool GetExtent(std::vector& points, + const OrthancStone::CoordinateSystem3D& viewportSlice) ORTHANC_OVERRIDE + { + OrthancStone::VolumeProjection projection; + + if (!IsGeometryReady() || + !DetectProjection(projection, viewportSlice)) + { + return false; + } + else + { + // As the slices of the volumic image are arranged in a box, + // we only consider one single reference slice (the one with index 0). + std::auto_ptr slice(GetProjectionGeometry(projection).GetSlice(0)); + slice->GetExtent(points); + + return true; + } + } + + virtual void ScheduleLayerCreation(const OrthancStone::CoordinateSystem3D& viewportSlice) ORTHANC_OVERRIDE + { + OrthancStone::VolumeProjection projection; + + if (IsGeometryReady() && + DetectProjection(projection, viewportSlice)) + { + const VolumeImageGeometry& geometry = GetProjectionGeometry(projection); + + size_t closest; + + if (geometry.LookupSlice(closest, viewportSlice)) + { + bool isFullQuality = true; // TODO + + std::auto_ptr frame; + + { + OrthancStone::ImageBuffer3D::SliceReader reader(volume_.GetImage(), projection, static_cast(closest)); + + // TODO Transfer ownership if non-axial, to avoid memcpy + frame.reset(Orthanc::Image::Clone(reader.GetAccessor())); + } + + std::auto_ptr slice(geometry.GetSlice(closest)); + + RendererFactory factory(*frame, *slice, isFullQuality); + + BroadcastMessage(IVolumeSlicer::LayerReadyMessage(*this, factory, slice->GetGeometry())); + return; + } + } + + // Error + OrthancStone::CoordinateSystem3D slice; + BroadcastMessage(IVolumeSlicer::LayerErrorMessage(*this, slice)); + } + }; + + + class VolumeImageInteractor : + public IWorldSceneInteractor, + public OrthancStone::IObserver + { + private: + SliceViewerWidget& widget_; + OrthancStone::VolumeProjection projection_; + std::auto_ptr slices_; + size_t slice_; + + protected: + void OnGeometryReady(const ISlicedVolume::GeometryReadyMessage& message) + { + if (slices_.get() == NULL) + { + const OrthancVolumeImage& image = + dynamic_cast(message.GetOrigin()); + + slices_.reset(new VolumeImageGeometry(image, projection_)); + SetSlice(slices_->GetSlicesCount() / 2); + + widget_.FitContent(); + } + } + + virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, + const ViewportGeometry& view, + OrthancStone::MouseButton button, + OrthancStone::KeyboardModifiers modifiers, + int viewportX, + int viewportY, + double x, + double y, + IStatusBar* statusBar, + const std::vector& touches) ORTHANC_OVERRIDE + { + return NULL; + } + + virtual void MouseOver(OrthancStone::CairoContext& context, + WorldSceneWidget& widget, + const ViewportGeometry& view, + double x, + double y, + IStatusBar* statusBar) ORTHANC_OVERRIDE + { + } + + virtual void MouseWheel(WorldSceneWidget& widget, + OrthancStone::MouseWheelDirection direction, + OrthancStone::KeyboardModifiers modifiers, + IStatusBar* statusBar) ORTHANC_OVERRIDE + { + int scale = (modifiers & OrthancStone::KeyboardModifiers_Control ? 10 : 1); + + switch (direction) + { + case OrthancStone::MouseWheelDirection_Up: + OffsetSlice(-scale); + break; + + case OrthancStone::MouseWheelDirection_Down: + OffsetSlice(scale); + break; + + default: + break; + } + } + + virtual void KeyPressed(WorldSceneWidget& widget, + OrthancStone::KeyboardKeys key, + char keyChar, + OrthancStone::KeyboardModifiers modifiers, + IStatusBar* statusBar) ORTHANC_OVERRIDE + { + switch (keyChar) + { + case 's': + widget.FitContent(); + break; + + default: + break; + } + } + + public: + VolumeImageInteractor(OrthancStone::MessageBroker& broker, + OrthancVolumeImage& volume, + SliceViewerWidget& widget, + OrthancStone::VolumeProjection projection) : + IObserver(broker), + widget_(widget), + projection_(projection) + { + widget.SetInteractor(*this); + + volume.RegisterObserverCallback( + new OrthancStone::Callable + (*this, &VolumeImageInteractor::OnGeometryReady)); + } + + bool IsGeometryReady() const + { + return slices_.get() != NULL; + } + + size_t GetSlicesCount() const + { + if (slices_.get() == NULL) + { + return 0; + } + else + { + return slices_->GetSlicesCount(); + } + } + + void OffsetSlice(int offset) + { + if (slices_.get() != NULL) + { + int slice = static_cast(slice_) + offset; + + if (slice < 0) + { + slice = 0; + } + + if (slice >= static_cast(slices_->GetSlicesCount())) + { + slice = static_cast(slices_->GetSlicesCount()) - 1; + } + + if (slice != static_cast(slice_)) + { + SetSlice(slice); + } + } + } + + void SetSlice(size_t slice) + { + if (slices_.get() != NULL) + { + slice_ = slice; + + std::auto_ptr tmp(slices_->GetSlice(slice_)); + widget_.SetSlice(tmp->GetGeometry()); + } + } + }; + + + + class ReferenceLineSource : public IVolumeSlicer + { + private: + class RendererFactory : public LayerReadyMessage::IRendererFactory + { + private: + double x1_; + double y1_; + double x2_; + double y2_; + const OrthancStone::CoordinateSystem3D& slice_; + + public: + RendererFactory(double x1, + double y1, + double x2, + double y2, + const OrthancStone::CoordinateSystem3D& slice) : + x1_(x1), + y1_(y1), + x2_(x2), + y2_(y2), + slice_(slice) + { + } + + virtual ILayerRenderer* CreateRenderer() const + { + return new LineLayerRenderer(x1_, y1_, x2_, y2_, slice_); + } + }; + + SliceViewerWidget& otherPlane_; + + public: + ReferenceLineSource(OrthancStone::MessageBroker& broker, + SliceViewerWidget& otherPlane) : + IVolumeSlicer(broker), + otherPlane_(otherPlane) + { + BroadcastMessage(IVolumeSlicer::GeometryReadyMessage(*this)); + } + + virtual bool GetExtent(std::vector& points, + const OrthancStone::CoordinateSystem3D& viewportSlice) + { + return false; + } + + virtual void ScheduleLayerCreation(const OrthancStone::CoordinateSystem3D& viewportSlice) + { + Slice reference(viewportSlice, 0.001); + + OrthancStone::Vector p, d; + + const OrthancStone::CoordinateSystem3D& slice = otherPlane_.GetSlice(); + + // Compute the line of intersection between the two slices + if (!OrthancStone::GeometryToolbox::IntersectTwoPlanes(p, d, + slice.GetOrigin(), slice.GetNormal(), + viewportSlice.GetOrigin(), viewportSlice.GetNormal())) + { + // The two slice are parallel, don't try and display the intersection + BroadcastMessage(IVolumeSlicer::LayerErrorMessage(*this, reference.GetGeometry())); + } + else + { + double x1, y1, x2, y2; + viewportSlice.ProjectPoint(x1, y1, p); + viewportSlice.ProjectPoint(x2, y2, p + 1000.0 * d); + + const OrthancStone::Extent2D extent = otherPlane_.GetSceneExtent(); + + if (OrthancStone::GeometryToolbox::ClipLineToRectangle(x1, y1, x2, y2, + x1, y1, x2, y2, + extent.GetX1(), extent.GetY1(), + extent.GetX2(), extent.GetY2())) + { + RendererFactory factory(x1, y1, x2, y2, slice); + BroadcastMessage(IVolumeSlicer::LayerReadyMessage(*this, factory, reference.GetGeometry())); + } + else + { + // Error: Parallel slices + BroadcastMessage(IVolumeSlicer::LayerErrorMessage(*this, reference.GetGeometry())); + } + } + } + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/CircleMeasureTracker.cpp --- a/Framework/Layers/CircleMeasureTracker.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,106 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "CircleMeasureTracker.h" - -#include -#include - -namespace OrthancStone -{ - CircleMeasureTracker::CircleMeasureTracker(IStatusBar* statusBar, - const CoordinateSystem3D& slice, - double x, - double y, - uint8_t red, - uint8_t green, - uint8_t blue, - const Orthanc::Font& font) : - statusBar_(statusBar), - slice_(slice), - x1_(x), - y1_(y), - x2_(x), - y2_(y), - font_(font) - { - color_[0] = red; - color_[1] = green; - color_[2] = blue; - } - - - void CircleMeasureTracker::Render(CairoContext& context, - double zoom) - { - double x = (x1_ + x2_) / 2.0; - double y = (y1_ + y2_) / 2.0; - - Vector tmp; - LinearAlgebra::AssignVector(tmp, x2_ - x1_, y2_ - y1_); - double r = boost::numeric::ublas::norm_2(tmp) / 2.0; - - context.SetSourceColor(color_[0], color_[1], color_[2]); - - cairo_t* cr = context.GetObject(); - cairo_save(cr); - cairo_set_line_width(cr, 2.0 / zoom); - cairo_translate(cr, x, y); - cairo_arc(cr, 0, 0, r, 0, 2.0 * boost::math::constants::pi()); - cairo_stroke_preserve(cr); - cairo_stroke(cr); - cairo_restore(cr); - - context.DrawText(font_, FormatRadius(), x, y, BitmapAnchor_Center); - } - - - double CircleMeasureTracker::GetRadius() const // In millimeters - { - Vector a = slice_.MapSliceToWorldCoordinates(x1_, y1_); - Vector b = slice_.MapSliceToWorldCoordinates(x2_, y2_); - return boost::numeric::ublas::norm_2(b - a) / 2.0; - } - - - std::string CircleMeasureTracker::FormatRadius() const - { - char buf[64]; - sprintf(buf, "%0.01f cm", GetRadius() / 10.0); - return buf; - } - - void CircleMeasureTracker::MouseMove(int displayX, - int displayY, - double x, - double y, - const std::vector& displayTouches, - const std::vector& sceneTouches) - { - x2_ = x; - y2_ = y; - - if (statusBar_ != NULL) - { - statusBar_->SetMessage("Circle radius: " + FormatRadius()); - } - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/CircleMeasureTracker.h --- a/Framework/Layers/CircleMeasureTracker.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../Widgets/IWorldSceneMouseTracker.h" - -#include "../Viewport/IStatusBar.h" -#include "../Toolbox/CoordinateSystem3D.h" - -#include - -namespace OrthancStone -{ - class CircleMeasureTracker : public IWorldSceneMouseTracker - { - private: - IStatusBar* statusBar_; - CoordinateSystem3D slice_; - double x1_; - double y1_; - double x2_; - double y2_; - uint8_t color_[3]; - const Orthanc::Font& font_; - - public: - CircleMeasureTracker(IStatusBar* statusBar, - const CoordinateSystem3D& slice, - double x, - double y, - uint8_t red, - uint8_t green, - uint8_t blue, - const Orthanc::Font& font); - - virtual bool HasRender() const - { - return true; - } - - virtual void Render(CairoContext& context, - double zoom); - - double GetRadius() const; // In millimeters - - std::string FormatRadius() const; - - virtual void MouseUp() - { - // Possibly create a new landmark "volume" with the circle in subclasses - } - - virtual void MouseMove(int displayX, - int displayY, - double x, - double y, - const std::vector& displayTouches, - const std::vector& sceneTouches); - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/ColorFrameRenderer.cpp --- a/Framework/Layers/ColorFrameRenderer.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "ColorFrameRenderer.h" - -#include -#include -#include - -namespace OrthancStone -{ - CairoSurface* ColorFrameRenderer::GenerateDisplay(const RenderStyle& style) - { - std::auto_ptr display(new CairoSurface(frame_->GetWidth(), frame_->GetHeight(), false /* no alpha */)); - - Orthanc::ImageAccessor target; - display->GetWriteableAccessor(target); - - Orthanc::ImageProcessing::Convert(target, *frame_); - - return display.release(); - } - - - ColorFrameRenderer::ColorFrameRenderer(const Orthanc::ImageAccessor& frame, - const CoordinateSystem3D& framePlane, - double pixelSpacingX, - double pixelSpacingY, - bool isFullQuality) : - FrameRenderer(framePlane, pixelSpacingX, pixelSpacingY, isFullQuality), - frame_(Orthanc::Image::Clone(frame)) - { - if (frame_.get() == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - if (frame_->GetFormat() != Orthanc::PixelFormat_RGB24) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); - } - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/ColorFrameRenderer.h --- a/Framework/Layers/ColorFrameRenderer.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "FrameRenderer.h" - -namespace OrthancStone -{ - class ColorFrameRenderer : public FrameRenderer - { - private: - std::auto_ptr frame_; // In RGB24 - - protected: - virtual CairoSurface* GenerateDisplay(const RenderStyle& style); - - public: - ColorFrameRenderer(const Orthanc::ImageAccessor& frame, - const CoordinateSystem3D& framePlane, - double pixelSpacingX, - double pixelSpacingY, - bool isFullQuality); - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/DicomSeriesVolumeSlicer.cpp --- a/Framework/Layers/DicomSeriesVolumeSlicer.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,161 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "DicomSeriesVolumeSlicer.h" - -#include "FrameRenderer.h" -#include "../Toolbox/DicomFrameConverter.h" - -#include -#include - -#include - -namespace OrthancStone -{ - - void DicomSeriesVolumeSlicer::OnSliceGeometryReady(const OrthancSlicesLoader::SliceGeometryReadyMessage& message) - { - if (message.GetOrigin().GetSlicesCount() > 0) - { - BroadcastMessage(IVolumeSlicer::GeometryReadyMessage(*this)); - } - else - { - BroadcastMessage(IVolumeSlicer::GeometryErrorMessage(*this)); - } - } - - void DicomSeriesVolumeSlicer::OnSliceGeometryError(const OrthancSlicesLoader::SliceGeometryErrorMessage& message) - { - BroadcastMessage(IVolumeSlicer::GeometryErrorMessage(*this)); - } - - - class DicomSeriesVolumeSlicer::RendererFactory : public LayerReadyMessage::IRendererFactory - { - private: - const OrthancSlicesLoader::SliceImageReadyMessage& message_; - - public: - RendererFactory(const OrthancSlicesLoader::SliceImageReadyMessage& message) : - message_(message) - { - } - - virtual ILayerRenderer* CreateRenderer() const - { - bool isFull = (message_.GetEffectiveQuality() == SliceImageQuality_FullPng || - message_.GetEffectiveQuality() == SliceImageQuality_FullPam); - - return FrameRenderer::CreateRenderer(message_.GetImage(), message_.GetSlice(), isFull); - } - }; - - void DicomSeriesVolumeSlicer::OnSliceImageReady(const OrthancSlicesLoader::SliceImageReadyMessage& message) - { - // first notify that the pixel data of the frame is ready (targeted to, i.e: an image cache) - BroadcastMessage(FrameReadyMessage(*this, message.GetImage(), - message.GetEffectiveQuality(), message.GetSlice())); - - // then notify that the layer is ready for rendering - RendererFactory factory(message); - BroadcastMessage(IVolumeSlicer::LayerReadyMessage(*this, factory, message.GetSlice().GetGeometry())); - } - - void DicomSeriesVolumeSlicer::OnSliceImageError(const OrthancSlicesLoader::SliceImageErrorMessage& message) - { - BroadcastMessage(IVolumeSlicer::LayerErrorMessage(*this, message.GetSlice().GetGeometry())); - } - - - DicomSeriesVolumeSlicer::DicomSeriesVolumeSlicer(MessageBroker& broker, OrthancApiClient& orthanc) : - IVolumeSlicer(broker), - IObserver(broker), - loader_(broker, orthanc), - quality_(SliceImageQuality_FullPng) - { - loader_.RegisterObserverCallback( - new Callable - (*this, &DicomSeriesVolumeSlicer::OnSliceGeometryReady)); - - loader_.RegisterObserverCallback( - new Callable - (*this, &DicomSeriesVolumeSlicer::OnSliceGeometryError)); - - loader_.RegisterObserverCallback( - new Callable - (*this, &DicomSeriesVolumeSlicer::OnSliceImageReady)); - - loader_.RegisterObserverCallback( - new Callable - (*this, &DicomSeriesVolumeSlicer::OnSliceImageError)); - } - - - void DicomSeriesVolumeSlicer::LoadSeries(const std::string& seriesId) - { - loader_.ScheduleLoadSeries(seriesId); - } - - - void DicomSeriesVolumeSlicer::LoadInstance(const std::string& instanceId) - { - loader_.ScheduleLoadInstance(instanceId); - } - - - void DicomSeriesVolumeSlicer::LoadFrame(const std::string& instanceId, - unsigned int frame) - { - loader_.ScheduleLoadFrame(instanceId, frame); - } - - - bool DicomSeriesVolumeSlicer::GetExtent(std::vector& points, - const CoordinateSystem3D& viewportSlice) - { - size_t index; - - if (loader_.IsGeometryReady() && - loader_.LookupSlice(index, viewportSlice)) - { - loader_.GetSlice(index).GetExtent(points); - return true; - } - else - { - return false; - } - } - - - void DicomSeriesVolumeSlicer::ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice) - { - size_t index; - - if (loader_.IsGeometryReady() && - loader_.LookupSlice(index, viewportSlice)) - { - loader_.ScheduleLoadSliceImage(index, quality_); - } - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/DicomSeriesVolumeSlicer.h --- a/Framework/Layers/DicomSeriesVolumeSlicer.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,126 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "IVolumeSlicer.h" -#include "../Toolbox/IWebService.h" -#include "../Toolbox/OrthancSlicesLoader.h" -#include "../Toolbox/OrthancApiClient.h" - -namespace OrthancStone -{ - // this class is in charge of loading a Frame. - // once it's been loaded (first the geometry and then the image), - // messages are sent to observers so they can use it - class DicomSeriesVolumeSlicer : - public IVolumeSlicer, - public IObserver - //private OrthancSlicesLoader::ISliceLoaderObserver - { - public: - // TODO: Add "frame" and "instanceId" - class FrameReadyMessage : public OriginMessage - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - const Orthanc::ImageAccessor& frame_; - SliceImageQuality imageQuality_; - const Deprecated::Slice& slice_; - - public: - FrameReadyMessage(DicomSeriesVolumeSlicer& origin, - const Orthanc::ImageAccessor& frame, - SliceImageQuality imageQuality, - const Deprecated::Slice& slice) : - OriginMessage(origin), - frame_(frame), - imageQuality_(imageQuality), - slice_(slice) - { - } - - const Orthanc::ImageAccessor& GetFrame() const - { - return frame_; - } - - SliceImageQuality GetImageQuality() const - { - return imageQuality_; - } - - const Deprecated::Slice& GetSlice() const - { - return slice_; - } - }; - - - private: - class RendererFactory; - - OrthancSlicesLoader loader_; - SliceImageQuality quality_; - - public: - DicomSeriesVolumeSlicer(MessageBroker& broker, OrthancApiClient& orthanc); - - void LoadSeries(const std::string& seriesId); - - void LoadInstance(const std::string& instanceId); - - void LoadFrame(const std::string& instanceId, - unsigned int frame); - - void SetImageQuality(SliceImageQuality quality) - { - quality_ = quality; - } - - SliceImageQuality GetImageQuality() const - { - return quality_; - } - - size_t GetSlicesCount() const - { - return loader_.GetSlicesCount(); - } - - const Deprecated::Slice& GetSlice(size_t slice) const - { - return loader_.GetSlice(slice); - } - - virtual bool GetExtent(std::vector& points, - const CoordinateSystem3D& viewportSlice); - - virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice); - -protected: - void OnSliceGeometryReady(const OrthancSlicesLoader::SliceGeometryReadyMessage& message); - void OnSliceGeometryError(const OrthancSlicesLoader::SliceGeometryErrorMessage& message); - void OnSliceImageReady(const OrthancSlicesLoader::SliceImageReadyMessage& message); - void OnSliceImageError(const OrthancSlicesLoader::SliceImageErrorMessage& message); - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/DicomStructureSetSlicer.cpp --- a/Framework/Layers/DicomStructureSetSlicer.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,170 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "DicomStructureSetSlicer.h" - -namespace OrthancStone -{ - class DicomStructureSetSlicer::Renderer : public ILayerRenderer - { - private: - class Structure - { - private: - bool visible_; - uint8_t red_; - uint8_t green_; - uint8_t blue_; - std::string name_; - std::vector< std::vector > polygons_; - - public: - Structure(DicomStructureSet& structureSet, - const CoordinateSystem3D& plane, - size_t index) : - name_(structureSet.GetStructureName(index)) - { - structureSet.GetStructureColor(red_, green_, blue_, index); - visible_ = structureSet.ProjectStructure(polygons_, index, plane); - } - - void Render(CairoContext& context) - { - if (visible_) - { - cairo_t* cr = context.GetObject(); - - context.SetSourceColor(red_, green_, blue_); - - for (size_t i = 0; i < polygons_.size(); i++) - { - cairo_move_to(cr, polygons_[i][0].first, polygons_[i][0].second); - - for (size_t j = 1; j < polygons_[i].size(); j++) - { - cairo_line_to(cr, polygons_[i][j].first, polygons_[i][j].second); - } - - cairo_line_to(cr, polygons_[i][0].first, polygons_[i][0].second); - cairo_stroke(cr); - } - } - } - }; - - typedef std::list Structures; - - CoordinateSystem3D plane_; - Structures structures_; - - public: - Renderer(DicomStructureSet& structureSet, - const CoordinateSystem3D& plane) : - plane_(plane) - { - for (size_t k = 0; k < structureSet.GetStructureCount(); k++) - { - structures_.push_back(new Structure(structureSet, plane, k)); - } - } - - virtual ~Renderer() - { - for (Structures::iterator it = structures_.begin(); - it != structures_.end(); ++it) - { - delete *it; - } - } - - virtual bool RenderLayer(CairoContext& context, - const ViewportGeometry& view) - { - cairo_set_line_width(context.GetObject(), 2.0f / view.GetZoom()); - - for (Structures::const_iterator it = structures_.begin(); - it != structures_.end(); ++it) - { - assert(*it != NULL); - (*it)->Render(context); - } - - return true; - } - - virtual const CoordinateSystem3D& GetLayerPlane() - { - return plane_; - } - - virtual void SetLayerStyle(const RenderStyle& style) - { - } - - virtual bool IsFullQuality() - { - return true; - } - }; - - - class DicomStructureSetSlicer::RendererFactory : public LayerReadyMessage::IRendererFactory - { - private: - DicomStructureSet& structureSet_; - const CoordinateSystem3D& plane_; - - public: - RendererFactory(DicomStructureSet& structureSet, - const CoordinateSystem3D& plane) : - structureSet_(structureSet), - plane_(plane) - { - } - - virtual ILayerRenderer* CreateRenderer() const - { - return new Renderer(structureSet_, plane_); - } - }; - - - DicomStructureSetSlicer::DicomStructureSetSlicer(MessageBroker& broker, - StructureSetLoader& loader) : - IVolumeSlicer(broker), - IObserver(broker), - loader_(loader) - { - loader_.RegisterObserverCallback( - new Callable - (*this, &DicomStructureSetSlicer::OnStructureSetLoaded)); - } - - - void DicomStructureSetSlicer::ScheduleLayerCreation(const CoordinateSystem3D& viewportPlane) - { - if (loader_.HasStructureSet()) - { - RendererFactory factory(loader_.GetStructureSet(), viewportPlane); - BroadcastMessage(IVolumeSlicer::LayerReadyMessage(*this, factory, viewportPlane)); - } - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/DicomStructureSetSlicer.h --- a/Framework/Layers/DicomStructureSetSlicer.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "IVolumeSlicer.h" -#include "../Volumes/StructureSetLoader.h" - -namespace OrthancStone -{ - class DicomStructureSetSlicer : - public IVolumeSlicer, - public IObserver - { - private: - class Renderer; - class RendererFactory; - - StructureSetLoader& loader_; - - void OnStructureSetLoaded(const IVolumeLoader::ContentChangedMessage& message) - { - BroadcastMessage(IVolumeSlicer::ContentChangedMessage(*this)); - } - - public: - DicomStructureSetSlicer(MessageBroker& broker, - StructureSetLoader& loader); - - virtual bool GetExtent(std::vector& points, - const CoordinateSystem3D& viewportPlane) - { - return false; - } - - virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportPlane); - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/FrameRenderer.cpp --- a/Framework/Layers/FrameRenderer.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,140 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "FrameRenderer.h" - -#include "GrayscaleFrameRenderer.h" -#include "ColorFrameRenderer.h" - -#include - -namespace OrthancStone -{ - FrameRenderer::FrameRenderer(const CoordinateSystem3D& framePlane, - double pixelSpacingX, - double pixelSpacingY, - bool isFullQuality) : - framePlane_(framePlane), - pixelSpacingX_(pixelSpacingX), - pixelSpacingY_(pixelSpacingY), - isFullQuality_(isFullQuality) - { - } - - - bool FrameRenderer::RenderLayer(CairoContext& context, - const ViewportGeometry& view) - { - if (!style_.visible_) - { - return true; - } - - if (display_.get() == NULL) - { - display_.reset(GenerateDisplay(style_)); - } - - assert(display_.get() != NULL); - - cairo_t *cr = context.GetObject(); - - cairo_save(cr); - - cairo_matrix_t transform; - cairo_matrix_init_identity(&transform); - cairo_matrix_scale(&transform, pixelSpacingX_, pixelSpacingY_); - cairo_matrix_translate(&transform, -0.5, -0.5); - cairo_transform(cr, &transform); - - //cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - cairo_set_source_surface(cr, display_->GetObject(), 0, 0); - - switch (style_.interpolation_) - { - case ImageInterpolation_Nearest: - cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST); - break; - - case ImageInterpolation_Bilinear: - cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_BILINEAR); - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - cairo_paint_with_alpha(cr, style_.alpha_); - - if (style_.drawGrid_) - { - context.SetSourceColor(style_.drawColor_); - cairo_set_line_width(cr, 0.5 / view.GetZoom()); - - for (unsigned int x = 0; x <= display_->GetWidth(); x++) - { - cairo_move_to(cr, x, 0); - cairo_line_to(cr, x, display_->GetHeight()); - } - - for (unsigned int y = 0; y <= display_->GetHeight(); y++) - { - cairo_move_to(cr, 0, y); - cairo_line_to(cr, display_->GetWidth(), y); - } - - cairo_stroke(cr); - } - - cairo_restore(cr); - - return true; - } - - - void FrameRenderer::SetLayerStyle(const RenderStyle& style) - { - style_ = style; - display_.reset(NULL); - } - - - ILayerRenderer* FrameRenderer::CreateRenderer(const Orthanc::ImageAccessor& frame, - const Deprecated::Slice& framePlane, - bool isFullQuality) - { - if (frame.GetFormat() == Orthanc::PixelFormat_RGB24) - { - return new ColorFrameRenderer(frame, - framePlane.GetGeometry(), - framePlane.GetPixelSpacingX(), - framePlane.GetPixelSpacingY(), isFullQuality); - } - else - { - return new GrayscaleFrameRenderer(frame, - framePlane.GetConverter(), - framePlane.GetGeometry(), - framePlane.GetPixelSpacingX(), - framePlane.GetPixelSpacingY(), isFullQuality); - } - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/FrameRenderer.h --- a/Framework/Layers/FrameRenderer.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "ILayerRenderer.h" - -#include "../Toolbox/Slice.h" - -namespace OrthancStone -{ - class FrameRenderer : public ILayerRenderer - { - private: - CoordinateSystem3D framePlane_; - double pixelSpacingX_; - double pixelSpacingY_; - RenderStyle style_; - bool isFullQuality_; - std::auto_ptr display_; - - protected: - virtual CairoSurface* GenerateDisplay(const RenderStyle& style) = 0; - - public: - FrameRenderer(const CoordinateSystem3D& framePlane, - double pixelSpacingX, - double pixelSpacingY, - bool isFullQuality); - - virtual bool RenderLayer(CairoContext& context, - const ViewportGeometry& view); - - virtual const CoordinateSystem3D& GetLayerPlane() - { - return framePlane_; - } - - virtual void SetLayerStyle(const RenderStyle& style); - - virtual bool IsFullQuality() - { - return isFullQuality_; - } - - // TODO: Avoid cloning the "frame" - static ILayerRenderer* CreateRenderer(const Orthanc::ImageAccessor& frame, - const Deprecated::Slice& framePlane, - bool isFullQuality); - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/GrayscaleFrameRenderer.cpp --- a/Framework/Layers/GrayscaleFrameRenderer.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,141 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "GrayscaleFrameRenderer.h" - -#include -#include - -namespace OrthancStone -{ - CairoSurface* GrayscaleFrameRenderer::GenerateDisplay(const RenderStyle& style) - { - assert(frame_->GetFormat() == Orthanc::PixelFormat_Float32); - - std::auto_ptr result; - - float windowCenter, windowWidth; - style.ComputeWindowing(windowCenter, windowWidth, - defaultWindowCenter_, defaultWindowWidth_); - - float x0 = windowCenter - windowWidth / 2.0f; - float x1 = windowCenter + windowWidth / 2.0f; - - //LOG(INFO) << "Window: " << x0 << " => " << x1; - - result.reset(new CairoSurface(frame_->GetWidth(), frame_->GetHeight(), false /* no alpha */)); - - const uint8_t* lut = NULL; - if (style.applyLut_) - { - if (Orthanc::EmbeddedResources::GetFileResourceSize(style.lut_) != 3 * 256) - { - // Invalid colormap - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - lut = reinterpret_cast(Orthanc::EmbeddedResources::GetFileResourceBuffer(style.lut_)); - } - - Orthanc::ImageAccessor target; - result->GetWriteableAccessor(target); - - const unsigned int width = target.GetWidth(); - const unsigned int height = target.GetHeight(); - - for (unsigned int y = 0; y < height; y++) - { - const float* p = reinterpret_cast(frame_->GetConstRow(y)); - uint8_t* q = reinterpret_cast(target.GetRow(y)); - - for (unsigned int x = 0; x < width; x++, p++, q += 4) - { - uint8_t v = 0; - if (windowWidth >= 0.001f) // Avoid division by zero - { - if (*p >= x1) - { - v = 255; - } - else if (*p <= x0) - { - v = 0; - } - else - { - // https://en.wikipedia.org/wiki/Linear_interpolation - v = static_cast(255.0f * (*p - x0) / (x1 - x0)); - } - - if (style.reverse_ ^ (photometric_ == Orthanc::PhotometricInterpretation_Monochrome1)) - { - v = 255 - v; - } - } - - if (style.applyLut_) - { - assert(lut != NULL); - q[3] = 255; - q[2] = lut[3 * v]; - q[1] = lut[3 * v + 1]; - q[0] = lut[3 * v + 2]; - } - else - { - q[3] = 255; - q[2] = v; - q[1] = v; - q[0] = v; - } - } - } - - return result.release(); - } - - - GrayscaleFrameRenderer::GrayscaleFrameRenderer(const Orthanc::ImageAccessor& frame, - const Deprecated::DicomFrameConverter& converter, - const CoordinateSystem3D& framePlane, - double pixelSpacingX, - double pixelSpacingY, - bool isFullQuality) : - FrameRenderer(framePlane, pixelSpacingX, pixelSpacingY, isFullQuality), - frame_(Orthanc::Image::Clone(frame)), - defaultWindowCenter_(static_cast(converter.GetDefaultWindowCenter())), - defaultWindowWidth_(static_cast(converter.GetDefaultWindowWidth())), - photometric_(converter.GetPhotometricInterpretation()) - { - if (frame_.get() == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - converter.ConvertFrameInplace(frame_); - assert(frame_.get() != NULL); - - if (frame_->GetFormat() != Orthanc::PixelFormat_Float32) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); - } - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/GrayscaleFrameRenderer.h --- a/Framework/Layers/GrayscaleFrameRenderer.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "FrameRenderer.h" -#include "../Toolbox/DicomFrameConverter.h" - -namespace OrthancStone -{ - class GrayscaleFrameRenderer : public FrameRenderer - { - private: - std::auto_ptr frame_; // In Float32 - float defaultWindowCenter_; - float defaultWindowWidth_; - Orthanc::PhotometricInterpretation photometric_; - - protected: - virtual CairoSurface* GenerateDisplay(const RenderStyle& style); - - public: - GrayscaleFrameRenderer(const Orthanc::ImageAccessor& frame, - const Deprecated::DicomFrameConverter& converter, - const CoordinateSystem3D& framePlane, - double pixelSpacingX, - double pixelSpacingY, - bool isFullQuality); - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/ILayerRenderer.h --- a/Framework/Layers/ILayerRenderer.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../Viewport/CairoContext.h" -#include "../Toolbox/CoordinateSystem3D.h" -#include "../Toolbox/ViewportGeometry.h" -#include "RenderStyle.h" - -namespace OrthancStone -{ - class ILayerRenderer : public boost::noncopyable - { - public: - virtual ~ILayerRenderer() - { - } - - virtual bool RenderLayer(CairoContext& context, - const ViewportGeometry& view) = 0; - - virtual void SetLayerStyle(const RenderStyle& style) = 0; - - virtual const CoordinateSystem3D& GetLayerPlane() = 0; - - virtual bool IsFullQuality() = 0; - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/IVolumeSlicer.h --- a/Framework/Layers/IVolumeSlicer.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,139 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "ILayerRenderer.h" -#include "../Toolbox/Slice.h" -#include "../../Framework/Messages/IObservable.h" -#include "../../Framework/Messages/IMessage.h" -#include "Core/Images/Image.h" -#include - -namespace OrthancStone -{ - class IVolumeSlicer : public IObservable - { - public: - ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryReadyMessage, IVolumeSlicer); - ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryErrorMessage, IVolumeSlicer); - ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentChangedMessage, IVolumeSlicer); - - class SliceContentChangedMessage : public OriginMessage - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - const Deprecated::Slice& slice_; - - public: - SliceContentChangedMessage(IVolumeSlicer& origin, - const Deprecated::Slice& slice) : - OriginMessage(origin), - slice_(slice) - { - } - - const Deprecated::Slice& GetSlice() const - { - return slice_; - } - }; - - - class LayerReadyMessage : public OriginMessage - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - public: - class IRendererFactory : public boost::noncopyable - { - public: - virtual ~IRendererFactory() - { - } - - virtual ILayerRenderer* CreateRenderer() const = 0; - }; - - private: - const IRendererFactory& factory_; - const CoordinateSystem3D& slice_; - - public: - LayerReadyMessage(IVolumeSlicer& origin, - const IRendererFactory& rendererFactory, - const CoordinateSystem3D& slice) : - OriginMessage(origin), - factory_(rendererFactory), - slice_(slice) - { - } - - ILayerRenderer* CreateRenderer() const - { - return factory_.CreateRenderer(); - } - - const CoordinateSystem3D& GetSlice() const - { - return slice_; - } - }; - - - class LayerErrorMessage : public OriginMessage - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - const CoordinateSystem3D& slice_; - - public: - LayerErrorMessage(IVolumeSlicer& origin, - const CoordinateSystem3D& slice) : - OriginMessage(origin), - slice_(slice) - { - } - - const CoordinateSystem3D& GetSlice() const - { - return slice_; - } - }; - - - IVolumeSlicer(MessageBroker& broker) : - IObservable(broker) - { - } - - virtual ~IVolumeSlicer() - { - } - - virtual bool GetExtent(std::vector& points, - const CoordinateSystem3D& viewportSlice) = 0; - - virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice) = 0; - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/LineLayerRenderer.cpp --- a/Framework/Layers/LineLayerRenderer.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "LineLayerRenderer.h" - -namespace OrthancStone -{ - LineLayerRenderer::LineLayerRenderer(double x1, - double y1, - double x2, - double y2, - const CoordinateSystem3D& plane) : - x1_(x1), - y1_(y1), - x2_(x2), - y2_(y2), - plane_(plane) - { - RenderStyle style; - SetLayerStyle(style); - } - - - bool LineLayerRenderer::RenderLayer(CairoContext& context, - const ViewportGeometry& view) - { - if (visible_) - { - context.SetSourceColor(color_); - - cairo_t *cr = context.GetObject(); - cairo_set_line_width(cr, 1.0 / view.GetZoom()); - cairo_move_to(cr, x1_, y1_); - cairo_line_to(cr, x2_, y2_); - cairo_stroke(cr); - } - - return true; - } - - - void LineLayerRenderer::SetLayerStyle(const RenderStyle& style) - { - visible_ = style.visible_; - color_[0] = style.drawColor_[0]; - color_[1] = style.drawColor_[1]; - color_[2] = style.drawColor_[2]; - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/LineLayerRenderer.h --- a/Framework/Layers/LineLayerRenderer.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "ILayerRenderer.h" - -namespace OrthancStone -{ - class LineLayerRenderer : public ILayerRenderer - { - private: - double x1_; - double y1_; - double x2_; - double y2_; - CoordinateSystem3D plane_; - bool visible_; - uint8_t color_[3]; - - public: - LineLayerRenderer(double x1, - double y1, - double x2, - double y2, - const CoordinateSystem3D& plane); - - virtual bool RenderLayer(CairoContext& context, - const ViewportGeometry& view); - - virtual void SetLayerStyle(const RenderStyle& style); - - virtual const CoordinateSystem3D& GetLayerPlane() - { - return plane_; - } - - virtual bool IsFullQuality() - { - return true; - } - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/LineMeasureTracker.cpp --- a/Framework/Layers/LineMeasureTracker.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,102 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "LineMeasureTracker.h" - -#include - -namespace OrthancStone -{ - LineMeasureTracker::LineMeasureTracker(IStatusBar* statusBar, - const CoordinateSystem3D& slice, - double x, - double y, - uint8_t red, - uint8_t green, - uint8_t blue, - const Orthanc::Font& font) : - statusBar_(statusBar), - slice_(slice), - x1_(x), - y1_(y), - x2_(x), - y2_(y), - font_(font) - { - color_[0] = red; - color_[1] = green; - color_[2] = blue; - } - - - void LineMeasureTracker::Render(CairoContext& context, - double zoom) - { - context.SetSourceColor(color_[0], color_[1], color_[2]); - - cairo_t* cr = context.GetObject(); - cairo_set_line_width(cr, 2.0 / zoom); - cairo_move_to(cr, x1_, y1_); - cairo_line_to(cr, x2_, y2_); - cairo_stroke(cr); - - if (y2_ - y1_ < 0) - { - context.DrawText(font_, FormatLength(), x2_, y2_ - 5, BitmapAnchor_BottomCenter); - } - else - { - context.DrawText(font_, FormatLength(), x2_, y2_ + 5, BitmapAnchor_TopCenter); - } - } - - - double LineMeasureTracker::GetLength() const // In millimeters - { - Vector a = slice_.MapSliceToWorldCoordinates(x1_, y1_); - Vector b = slice_.MapSliceToWorldCoordinates(x2_, y2_); - return boost::numeric::ublas::norm_2(b - a); - } - - - std::string LineMeasureTracker::FormatLength() const - { - char buf[64]; - sprintf(buf, "%0.01f cm", GetLength() / 10.0); - return buf; - } - - void LineMeasureTracker::MouseMove(int displayX, - int displayY, - double x, - double y, - const std::vector& displayTouches, - const std::vector& sceneTouches) - { - x2_ = x; - y2_ = y; - - if (statusBar_ != NULL) - { - statusBar_->SetMessage("Line length: " + FormatLength()); - } - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/LineMeasureTracker.h --- a/Framework/Layers/LineMeasureTracker.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../Widgets/IWorldSceneMouseTracker.h" - -#include "../Viewport/IStatusBar.h" -#include "../Toolbox/CoordinateSystem3D.h" - -namespace OrthancStone -{ - class LineMeasureTracker : public IWorldSceneMouseTracker - { - private: - IStatusBar* statusBar_; - CoordinateSystem3D slice_; - double x1_; - double y1_; - double x2_; - double y2_; - uint8_t color_[3]; - unsigned int fontSize_; - const Orthanc::Font& font_; - - public: - LineMeasureTracker(IStatusBar* statusBar, - const CoordinateSystem3D& slice, - double x, - double y, - uint8_t red, - uint8_t green, - uint8_t blue, - const Orthanc::Font& font); - - virtual bool HasRender() const - { - return true; - } - - virtual void Render(CairoContext& context, - double zoom); - - double GetLength() const; // In millimeters - - std::string FormatLength() const; - - virtual void MouseUp() - { - // Possibly create a new landmark "volume" with the line in subclasses - } - - virtual void MouseMove(int displayX, - int displayY, - double x, - double y, - const std::vector& displayTouches, - const std::vector& sceneTouches); - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/RenderStyle.cpp --- a/Framework/Layers/RenderStyle.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "RenderStyle.h" - -#include - -namespace OrthancStone -{ - RenderStyle::RenderStyle() - { - visible_ = true; - reverse_ = false; - windowing_ = ImageWindowing_Custom; - alpha_ = 1; - applyLut_ = false; - lut_ = Orthanc::EmbeddedResources::COLORMAP_HOT; - drawGrid_ = false; - drawColor_[0] = 255; - drawColor_[1] = 255; - drawColor_[2] = 255; - customWindowCenter_ = 128; - customWindowWidth_ = 256; - interpolation_ = ImageInterpolation_Nearest; - fontSize_ = 14; - } - - - void RenderStyle::ComputeWindowing(float& targetCenter, - float& targetWidth, - float defaultCenter, - float defaultWidth) const - { - if (windowing_ == ImageWindowing_Custom) - { - targetCenter = customWindowCenter_; - targetWidth = customWindowWidth_; - } - else - { - return ::OrthancStone::ComputeWindowing - (targetCenter, targetWidth, windowing_, defaultCenter, defaultWidth); - } - } - - - void RenderStyle::SetColor(uint8_t red, - uint8_t green, - uint8_t blue) - { - drawColor_[0] = red; - drawColor_[1] = green; - drawColor_[2] = blue; - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/RenderStyle.h --- a/Framework/Layers/RenderStyle.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../StoneEnumerations.h" - -#include - -#include - -namespace OrthancStone -{ - struct RenderStyle - { - bool visible_; - bool reverse_; - ImageWindowing windowing_; - float alpha_; // In [0,1] - bool applyLut_; - Orthanc::EmbeddedResources::FileResourceId lut_; - bool drawGrid_; - uint8_t drawColor_[3]; - float customWindowCenter_; - float customWindowWidth_; - ImageInterpolation interpolation_; - unsigned int fontSize_; - - RenderStyle(); - - void ComputeWindowing(float& targetCenter, - float& targetWidth, - float defaultCenter, - float defaultWidth) const; - - void SetColor(uint8_t red, - uint8_t green, - uint8_t blue); - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/SeriesFrameRendererFactory.cpp --- a/Framework/Layers/SeriesFrameRendererFactory.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,177 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "SeriesFrameRendererFactory.h" - -#include "FrameRenderer.h" - -#include -#include -#include -#include -#include - - -namespace OrthancStone -{ - void SeriesFrameRendererFactory::ReadCurrentFrameDataset(size_t frame) - { - if (currentDataset_.get() != NULL && - (fast_ || currentFrame_ == frame)) - { - // The frame has not changed since the previous call, no need to - // update the DICOM dataset - return; - } - - currentDataset_.reset(loader_->DownloadDicom(frame)); - currentFrame_ = frame; - - if (currentDataset_.get() == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - - - void SeriesFrameRendererFactory::GetCurrentPixelSpacing(double& spacingX, - double& spacingY) const - { - if (currentDataset_.get() == NULL) - { - // There was no previous call "ReadCurrentFrameDataset()" - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - GeometryToolbox::GetPixelSpacing(spacingX, spacingY, *currentDataset_); - } - - - double SeriesFrameRendererFactory::GetCurrentSliceThickness() const - { - if (currentDataset_.get() == NULL) - { - // There was no previous call "ReadCurrentFrameDataset()" - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - try - { - OrthancPlugins::DicomDatasetReader reader(*currentDataset_); - - double thickness; - if (reader.GetDoubleValue(thickness, OrthancPlugins::DICOM_TAG_SLICE_THICKNESS)) - { - return thickness; - } - } - catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) - { - } - - // Some arbitrary large slice thickness - return std::numeric_limits::infinity(); - } - - - SeriesFrameRendererFactory::SeriesFrameRendererFactory(ISeriesLoader* loader, // Takes ownership - bool fast) : - loader_(loader), - currentFrame_(0), - fast_(fast) - { - if (loader == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - - - bool SeriesFrameRendererFactory::GetExtent(double& x1, - double& y1, - double& x2, - double& y2, - const SliceGeometry& viewportSlice) - { - if (currentDataset_.get() == NULL) - { - // There has been no previous call to - // "CreateLayerRenderer". Read some arbitrary DICOM frame, the - // one at the middle of the series. - unsigned int depth = loader_->GetGeometry().GetSliceCount(); - ReadCurrentFrameDataset(depth / 2); - } - - double spacingX, spacingY; - GetCurrentPixelSpacing(spacingX, spacingY); - - return FrameRenderer::ComputeFrameExtent(x1, y1, x2, y2, - viewportSlice, - loader_->GetGeometry().GetSlice(0), - loader_->GetWidth(), - loader_->GetHeight(), - spacingX, spacingY); - } - - - ILayerRenderer* SeriesFrameRendererFactory::CreateLayerRenderer(const SliceGeometry& viewportSlice) - { - size_t closest; - double distance; - - bool isOpposite; - if (!GeometryToolbox::IsParallelOrOpposite(isOpposite, loader_->GetGeometry().GetNormal(), viewportSlice.GetNormal()) || - !loader_->GetGeometry().ComputeClosestSlice(closest, distance, viewportSlice.GetOrigin())) - { - // Unable to compute the slice in the series that is the - // closest to the slice displayed by the viewport - return NULL; - } - - ReadCurrentFrameDataset(closest); - assert(currentDataset_.get() != NULL); - - double spacingX, spacingY; - GetCurrentPixelSpacing(spacingX, spacingY); - - if (distance <= GetCurrentSliceThickness() / 2.0) - { - SliceGeometry frameSlice(*currentDataset_); - return FrameRenderer::CreateRenderer(loader_->DownloadFrame(closest), - frameSlice, - *currentDataset_, - spacingX, spacingY, - true); - } - else - { - // The closest slice of the series is too far away from the - // slice displayed by the viewport - return NULL; - } - } - - - ISliceableVolume& SeriesFrameRendererFactory::GetSourceVolume() const - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/SeriesFrameRendererFactory.h --- a/Framework/Layers/SeriesFrameRendererFactory.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "ILayerRendererFactory.h" - -#include "../Toolbox/ISeriesLoader.h" - -namespace OrthancStone -{ - class SeriesFrameRendererFactory : public ILayerRendererFactory - { - private: - std::auto_ptr loader_; - size_t currentFrame_; - bool fast_; - - std::auto_ptr currentDataset_; - - void ReadCurrentFrameDataset(size_t frame); - - void GetCurrentPixelSpacing(double& spacingX, - double& spacingY) const; - - double GetCurrentSliceThickness() const; - - public: - SeriesFrameRendererFactory(ISeriesLoader* loader, // Takes ownership - bool fast); - - virtual bool GetExtent(double& x1, - double& y1, - double& x2, - double& y2, - const SliceGeometry& viewportSlice); - - virtual ILayerRenderer* CreateLayerRenderer(const SliceGeometry& viewportSlice); - - virtual bool HasSourceVolume() const - { - return false; - } - - virtual ISliceableVolume& GetSourceVolume() const; - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/SingleFrameRendererFactory.cpp --- a/Framework/Layers/SingleFrameRendererFactory.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,88 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "SingleFrameRendererFactory.h" - -#include "FrameRenderer.h" -#include "../Toolbox/MessagingToolbox.h" -#include "../Toolbox/DicomFrameConverter.h" - -#include -#include -#include - -namespace OrthancStone -{ - SingleFrameRendererFactory::SingleFrameRendererFactory(OrthancPlugins::IOrthancConnection& orthanc, - const std::string& instanceId, - unsigned int frame) : - orthanc_(orthanc), - instance_(instanceId), - frame_(frame) - { - dicom_.reset(new OrthancPlugins::FullOrthancDataset(orthanc, "/instances/" + instanceId + "/tags")); - - DicomFrameConverter converter; - converter.ReadParameters(*dicom_); - format_ = converter.GetExpectedPixelFormat(); - } - - - bool SingleFrameRendererFactory::GetExtent(double& x1, - double& y1, - double& x2, - double& y2, - const SliceGeometry& viewportSlice) - { - // Assume that PixelSpacingX == PixelSpacingY == 1 - - OrthancPlugins::DicomDatasetReader reader(*dicom_); - - unsigned int width, height; - - if (!reader.GetUnsignedIntegerValue(width, OrthancPlugins::DICOM_TAG_COLUMNS) || - !reader.GetUnsignedIntegerValue(height, OrthancPlugins::DICOM_TAG_ROWS)) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - x1 = 0; - y1 = 0; - x2 = static_cast(width); - y2 = static_cast(height); - - return true; - } - - - ILayerRenderer* SingleFrameRendererFactory::CreateLayerRenderer(const SliceGeometry& viewportSlice) - { - SliceGeometry frameSlice(*dicom_); - return FrameRenderer::CreateRenderer(MessagingToolbox::DecodeFrame(orthanc_, instance_, frame_, format_), - frameSlice, *dicom_, 1, 1, true); - } - - - ISliceableVolume& SingleFrameRendererFactory::GetSourceVolume() const - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/SingleFrameRendererFactory.h --- a/Framework/Layers/SingleFrameRendererFactory.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "ILayerRendererFactory.h" -#include - -namespace OrthancStone -{ - class SingleFrameRendererFactory : public ILayerRendererFactory - { - private: - OrthancPlugins::IOrthancConnection& orthanc_; - std::auto_ptr dicom_; - - std::string instance_; - unsigned int frame_; - Orthanc::PixelFormat format_; - - public: - SingleFrameRendererFactory(OrthancPlugins::IOrthancConnection& orthanc, - const std::string& instanceId, - unsigned int frame); - - const OrthancPlugins::IDicomDataset& GetDataset() const - { - return *dicom_; - } - - SliceGeometry GetSliceGeometry() - { - return SliceGeometry(*dicom_); - } - - virtual bool GetExtent(double& x1, - double& y1, - double& x2, - double& y2, - const SliceGeometry& viewportSlice); - - virtual ILayerRenderer* CreateLayerRenderer(const SliceGeometry& viewportSlice); - - virtual bool HasSourceVolume() const - { - return false; - } - - virtual ISliceableVolume& GetSourceVolume() const; - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/SliceOutlineRenderer.cpp --- a/Framework/Layers/SliceOutlineRenderer.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "SliceOutlineRenderer.h" - -namespace OrthancStone -{ - bool SliceOutlineRenderer::RenderLayer(CairoContext& context, - const ViewportGeometry& view) - { - if (style_.visible_) - { - cairo_t *cr = context.GetObject(); - cairo_save(cr); - - context.SetSourceColor(style_.drawColor_); - - double x1 = -0.5 * pixelSpacingX_; - double y1 = -0.5 * pixelSpacingY_; - - cairo_set_line_width(cr, 1.0 / view.GetZoom()); - cairo_rectangle(cr, x1, y1, - static_cast(width_) * pixelSpacingX_, - static_cast(height_) * pixelSpacingY_); - - double handleSize = 10.0f / view.GetZoom(); - cairo_move_to(cr, x1 + handleSize, y1); - cairo_line_to(cr, x1, y1 + handleSize); - - cairo_stroke(cr); - cairo_restore(cr); - } - - return true; - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Layers/SliceOutlineRenderer.h --- a/Framework/Layers/SliceOutlineRenderer.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "ILayerRenderer.h" -#include "../Toolbox/Slice.h" - -namespace OrthancStone -{ - class SliceOutlineRenderer : public ILayerRenderer - { - private: - CoordinateSystem3D geometry_; - double pixelSpacingX_; - double pixelSpacingY_; - unsigned int width_; - unsigned int height_; - RenderStyle style_; - - public: - SliceOutlineRenderer(const Deprecated::Slice& slice) : - geometry_(slice.GetGeometry()), - pixelSpacingX_(slice.GetPixelSpacingX()), - pixelSpacingY_(slice.GetPixelSpacingY()), - width_(slice.GetWidth()), - height_(slice.GetHeight()) - { - } - - virtual bool RenderLayer(CairoContext& context, - const ViewportGeometry& view); - - virtual void SetLayerStyle(const RenderStyle& style) - { - style_ = style; - } - - virtual const CoordinateSystem3D& GetLayerSlice() - { - return geometry_; - } - - virtual bool IsFullQuality() - { - return true; - } - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Messages/IMessageEmitter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Messages/IMessageEmitter.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,39 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IObserver.h" +#include "IMessage.h" + +namespace OrthancStone +{ + class IMessageEmitter : public boost::noncopyable + { + public: + virtual ~IMessageEmitter() + { + } + + virtual void EmitMessage(const IObserver& observer, + const IMessage& message) = 0; + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Oracle/GetOrthancImageCommand.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Oracle/GetOrthancImageCommand.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,153 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "GetOrthancImageCommand.h" + +#include +#include +#include +#include +#include + +namespace OrthancStone +{ + GetOrthancImageCommand::SuccessMessage::SuccessMessage(const GetOrthancImageCommand& command, + Orthanc::ImageAccessor* image, // Takes ownership + Orthanc::MimeType mime) : + OriginMessage(command), + image_(image), + mime_(mime) + { + if (image == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + + GetOrthancImageCommand::GetOrthancImageCommand() : + uri_("/"), + timeout_(10), + hasExpectedFormat_(false) + { + } + + + void GetOrthancImageCommand::SetExpectedPixelFormat(Orthanc::PixelFormat format) + { + hasExpectedFormat_ = true; + expectedFormat_ = format; + } + + + void GetOrthancImageCommand::SetInstanceUri(const std::string& instance, + Orthanc::PixelFormat pixelFormat) + { + uri_ = "/instances/" + instance; + + switch (pixelFormat) + { + case Orthanc::PixelFormat_RGB24: + uri_ += "/preview"; + break; + + case Orthanc::PixelFormat_Grayscale16: + uri_ += "/image-uint16"; + break; + + case Orthanc::PixelFormat_SignedGrayscale16: + uri_ += "/image-int16"; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + void GetOrthancImageCommand::ProcessHttpAnswer(IMessageEmitter& emitter, + const IObserver& receiver, + const std::string& answer, + const HttpHeaders& answerHeaders) const + { + Orthanc::MimeType contentType = Orthanc::MimeType_Binary; + + for (HttpHeaders::const_iterator it = answerHeaders.begin(); + it != answerHeaders.end(); ++it) + { + std::string s; + Orthanc::Toolbox::ToLowerCase(s, it->first); + + if (s == "content-type") + { + contentType = Orthanc::StringToMimeType(it->second); + break; + } + } + + std::auto_ptr image; + + switch (contentType) + { + case Orthanc::MimeType_Png: + { + image.reset(new Orthanc::PngReader); + dynamic_cast(*image).ReadFromMemory(answer); + break; + } + + case Orthanc::MimeType_Pam: + { + image.reset(new Orthanc::PamReader); + dynamic_cast(*image).ReadFromMemory(answer); + break; + } + + case Orthanc::MimeType_Jpeg: + { + image.reset(new Orthanc::JpegReader); + dynamic_cast(*image).ReadFromMemory(answer); + break; + } + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol, + "Unsupported HTTP Content-Type for an image: " + + std::string(Orthanc::EnumerationToString(contentType))); + } + + if (hasExpectedFormat_) + { + if (expectedFormat_ == Orthanc::PixelFormat_SignedGrayscale16 && + image->GetFormat() == Orthanc::PixelFormat_Grayscale16) + { + image->SetFormat(Orthanc::PixelFormat_SignedGrayscale16); + } + + if (expectedFormat_ != image->GetFormat()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + } + + SuccessMessage message(*this, image.release(), contentType); + emitter.EmitMessage(receiver, message); + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Oracle/GetOrthancImageCommand.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Oracle/GetOrthancImageCommand.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,119 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../Messages/IMessageEmitter.h" +#include "OracleCommandWithPayload.h" + +#include + +#include + +namespace OrthancStone +{ + class GetOrthancImageCommand : public OracleCommandWithPayload + { + public: + typedef std::map HttpHeaders; + + class SuccessMessage : public OriginMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + std::auto_ptr image_; + Orthanc::MimeType mime_; + + public: + SuccessMessage(const GetOrthancImageCommand& command, + Orthanc::ImageAccessor* image, // Takes ownership + Orthanc::MimeType mime); + + const Orthanc::ImageAccessor& GetImage() const + { + return *image_; + } + + Orthanc::MimeType GetMimeType() const + { + return mime_; + } + }; + + + private: + std::string uri_; + HttpHeaders headers_; + unsigned int timeout_; + bool hasExpectedFormat_; + Orthanc::PixelFormat expectedFormat_; + + public: + GetOrthancImageCommand(); + + virtual Type GetType() const + { + return Type_GetOrthancImage; + } + + void SetExpectedPixelFormat(Orthanc::PixelFormat format); + + void SetUri(const std::string& uri) + { + uri_ = uri; + } + + void SetInstanceUri(const std::string& instance, + Orthanc::PixelFormat pixelFormat); + + void SetHttpHeader(const std::string& key, + const std::string& value) + { + headers_[key] = value; + } + + const std::string& GetUri() const + { + return uri_; + } + + const HttpHeaders& GetHttpHeaders() const + { + return headers_; + } + + void SetTimeout(unsigned int seconds) + { + timeout_ = seconds; + } + + unsigned int GetTimeout() const + { + return timeout_; + } + + void ProcessHttpAnswer(IMessageEmitter& emitter, + const IObserver& receiver, + const std::string& answer, + const HttpHeaders& answerHeaders) const; + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Oracle/GetOrthancWebViewerJpegCommand.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Oracle/GetOrthancWebViewerJpegCommand.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,217 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "GetOrthancWebViewerJpegCommand.h" + +#include "../Toolbox/LinearAlgebra.h" + +#include +#include +#include +#include +#include + +#include +#include + +namespace OrthancStone +{ + GetOrthancWebViewerJpegCommand::SuccessMessage::SuccessMessage(const GetOrthancWebViewerJpegCommand& command, + Orthanc::ImageAccessor* image) : // Takes ownership + OriginMessage(command), + image_(image) + { + if (image == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + + GetOrthancWebViewerJpegCommand::GetOrthancWebViewerJpegCommand() : + frame_(0), + quality_(95), + timeout_(10), + expectedFormat_(Orthanc::PixelFormat_Grayscale8) + { + } + + + void GetOrthancWebViewerJpegCommand::SetQuality(unsigned int quality) + { + if (quality <= 0 || + quality > 100) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + quality_ = quality; + } + } + + + std::string GetOrthancWebViewerJpegCommand::GetUri() const + { + return ("/web-viewer/instances/jpeg" + boost::lexical_cast(quality_) + + "-" + instanceId_ + "_" + boost::lexical_cast(frame_)); + } + + + void GetOrthancWebViewerJpegCommand::ProcessHttpAnswer(IMessageEmitter& emitter, + const IObserver& receiver, + const std::string& answer) const + { + // This code comes from older "OrthancSlicesLoader::ParseSliceImageJpeg()" + + Json::Value encoded; + + { + Json::Reader reader; + if (!reader.parse(answer, encoded)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + } + + if (encoded.type() != Json::objectValue || + !encoded.isMember("Orthanc") || + encoded["Orthanc"].type() != Json::objectValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + const Json::Value& info = encoded["Orthanc"]; + if (!info.isMember("PixelData") || + !info.isMember("Stretched") || + !info.isMember("Compression") || + info["Compression"].type() != Json::stringValue || + info["PixelData"].type() != Json::stringValue || + info["Stretched"].type() != Json::booleanValue || + info["Compression"].asString() != "Jpeg") + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + bool isSigned = false; + bool isStretched = info["Stretched"].asBool(); + + if (info.isMember("IsSigned")) + { + if (info["IsSigned"].type() != Json::booleanValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + else + { + isSigned = info["IsSigned"].asBool(); + } + } + + std::auto_ptr reader; + + { + std::string jpeg; + Orthanc::Toolbox::DecodeBase64(jpeg, info["PixelData"].asString()); + + reader.reset(new Orthanc::JpegReader); + dynamic_cast(*reader).ReadFromMemory(jpeg); + } + + if (reader->GetFormat() == Orthanc::PixelFormat_RGB24) // This is a color image + { + if (expectedFormat_ != Orthanc::PixelFormat_RGB24) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + if (isSigned || isStretched) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + else + { + SuccessMessage message(*this, reader.release()); + emitter.EmitMessage(receiver, message); + return; + } + } + + if (reader->GetFormat() != Orthanc::PixelFormat_Grayscale8) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + if (!isStretched) + { + if (expectedFormat_ != reader->GetFormat()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + else + { + SuccessMessage message(*this, reader.release()); + emitter.EmitMessage(receiver, message); + return; + } + } + + int32_t stretchLow = 0; + int32_t stretchHigh = 0; + + if (!info.isMember("StretchLow") || + !info.isMember("StretchHigh") || + info["StretchLow"].type() != Json::intValue || + info["StretchHigh"].type() != Json::intValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + stretchLow = info["StretchLow"].asInt(); + stretchHigh = info["StretchHigh"].asInt(); + + if (stretchLow < -32768 || + stretchHigh > 65535 || + (stretchLow < 0 && stretchHigh > 32767)) + { + // This range cannot be represented with a uint16_t or an int16_t + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + // Decode a grayscale JPEG 8bpp image coming from the Web viewer + std::auto_ptr image + (new Orthanc::Image(expectedFormat_, reader->GetWidth(), reader->GetHeight(), false)); + + Orthanc::ImageProcessing::Convert(*image, *reader); + reader.reset(); + + float scaling = static_cast(stretchHigh - stretchLow) / 255.0f; + + if (!LinearAlgebra::IsCloseToZero(scaling)) + { + float offset = static_cast(stretchLow) / scaling; + Orthanc::ImageProcessing::ShiftScale(*image, offset, scaling, true); + } + + SuccessMessage message(*this, image.release()); + emitter.EmitMessage(receiver, message); + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Oracle/GetOrthancWebViewerJpegCommand.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Oracle/GetOrthancWebViewerJpegCommand.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,135 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../Messages/IMessageEmitter.h" +#include "OracleCommandWithPayload.h" + +#include + +#include + +namespace OrthancStone +{ + class GetOrthancWebViewerJpegCommand : public OracleCommandWithPayload + { + public: + typedef std::map HttpHeaders; + + class SuccessMessage : public OriginMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + std::auto_ptr image_; + + public: + SuccessMessage(const GetOrthancWebViewerJpegCommand& command, + Orthanc::ImageAccessor* image); // Takes ownership + + const Orthanc::ImageAccessor& GetImage() const + { + return *image_; + } + }; + + private: + std::string instanceId_; + unsigned int frame_; + unsigned int quality_; + HttpHeaders headers_; + unsigned int timeout_; + Orthanc::PixelFormat expectedFormat_; + + public: + GetOrthancWebViewerJpegCommand(); + + virtual Type GetType() const + { + return Type_GetOrthancWebViewerJpeg; + } + + void SetExpectedPixelFormat(Orthanc::PixelFormat format) + { + expectedFormat_ = format; + } + + void SetInstance(const std::string& instanceId) + { + instanceId_ = instanceId; + } + + void SetFrame(unsigned int frame) + { + frame_ = frame; + } + + void SetQuality(unsigned int quality); + + void SetHttpHeader(const std::string& key, + const std::string& value) + { + headers_[key] = value; + } + + Orthanc::PixelFormat GetExpectedPixelFormat() const + { + return expectedFormat_; + } + + const std::string& GetInstanceId() const + { + return instanceId_; + } + + unsigned int GetFrame() const + { + return frame_; + } + + unsigned int GetQuality() const + { + return quality_; + } + + const HttpHeaders& GetHttpHeaders() const + { + return headers_; + } + + void SetTimeout(unsigned int seconds) + { + timeout_ = seconds; + } + + unsigned int GetTimeout() const + { + return timeout_; + } + + std::string GetUri() const; + + void ProcessHttpAnswer(IMessageEmitter& emitter, + const IObserver& receiver, + const std::string& answer) const; + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Oracle/IOracle.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Oracle/IOracle.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,38 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IOracleCommand.h" + +namespace OrthancStone +{ + class IOracle : public boost::noncopyable + { + public: + virtual ~IOracle() + { + } + + virtual void Schedule(const IObserver& receiver, + IOracleCommand* command) = 0; // Takes ownership + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Oracle/IOracleCommand.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Oracle/IOracleCommand.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,45 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include + +namespace OrthancStone +{ + class IOracleCommand : public boost::noncopyable + { + public: + enum Type + { + Type_Sleep, + Type_OrthancRestApi, + Type_GetOrthancImage, + Type_GetOrthancWebViewerJpeg + }; + + virtual ~IOracleCommand() + { + } + + virtual Type GetType() const = 0; + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Oracle/OracleCommandExceptionMessage.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Oracle/OracleCommandExceptionMessage.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,64 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../Messages/IMessage.h" +#include "IOracleCommand.h" + +#include + +namespace OrthancStone +{ + class OracleCommandExceptionMessage : public IMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + const IOracleCommand& command_; + Orthanc::OrthancException exception_; + + public: + OracleCommandExceptionMessage(const IOracleCommand& command, + const Orthanc::OrthancException& exception) : + command_(command), + exception_(exception) + { + } + + OracleCommandExceptionMessage(const IOracleCommand& command, + const Orthanc::ErrorCode& error) : + command_(command), + exception_(error) + { + } + + const IOracleCommand& GetCommand() const + { + return command_; + } + + const Orthanc::OrthancException& GetException() const + { + return exception_; + } + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Oracle/OracleCommandWithPayload.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Oracle/OracleCommandWithPayload.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,65 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "OracleCommandWithPayload.h" + +#include + +namespace OrthancStone +{ + void OracleCommandWithPayload::SetPayload(Orthanc::IDynamicObject* payload) + { + if (payload == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + else + { + payload_.reset(payload); + } + } + + + const Orthanc::IDynamicObject& OracleCommandWithPayload::GetPayload() const + { + if (HasPayload()) + { + return *payload_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + + Orthanc::IDynamicObject* OracleCommandWithPayload::ReleasePayload() + { + if (HasPayload()) + { + return payload_.release(); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Oracle/OracleCommandWithPayload.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Oracle/OracleCommandWithPayload.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,49 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IOracleCommand.h" + +#include + +#include + +namespace OrthancStone +{ + class OracleCommandWithPayload : public IOracleCommand + { + private: + std::auto_ptr payload_; + + public: + void SetPayload(Orthanc::IDynamicObject* payload); + + bool HasPayload() const + { + return (payload_.get() != NULL); + } + + const Orthanc::IDynamicObject& GetPayload() const; + + Orthanc::IDynamicObject* ReleasePayload(); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Oracle/OrthancRestApiCommand.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Oracle/OrthancRestApiCommand.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,78 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "OrthancRestApiCommand.h" + +#include + +#include +#include + +namespace OrthancStone +{ + OrthancRestApiCommand::SuccessMessage::SuccessMessage(const OrthancRestApiCommand& command, + const HttpHeaders& answerHeaders, + std::string& answer) : + OriginMessage(command), + headers_(answerHeaders), + answer_(answer) + { + } + + + void OrthancRestApiCommand::SuccessMessage::ParseJsonBody(Json::Value& target) const + { + Json::Reader reader; + if (!reader.parse(answer_, target)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + } + + + OrthancRestApiCommand::OrthancRestApiCommand() : + method_(Orthanc::HttpMethod_Get), + uri_("/"), + timeout_(10) + { + } + + + void OrthancRestApiCommand::SetBody(const Json::Value& json) + { + Json::FastWriter writer; + body_ = writer.write(json); + } + + + const std::string& OrthancRestApiCommand::GetBody() const + { + if (method_ == Orthanc::HttpMethod_Post || + method_ == Orthanc::HttpMethod_Put) + { + return body_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Oracle/OrthancRestApiCommand.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Oracle/OrthancRestApiCommand.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,131 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../Messages/IMessage.h" +#include "OracleCommandWithPayload.h" + +#include + +#include +#include + +namespace OrthancStone +{ + class OrthancRestApiCommand : public OracleCommandWithPayload + { + public: + typedef std::map HttpHeaders; + + class SuccessMessage : public OriginMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + HttpHeaders headers_; + std::string answer_; + + public: + SuccessMessage(const OrthancRestApiCommand& command, + const HttpHeaders& answerHeaders, + std::string& answer /* will be swapped to avoid a memcpy() */); + + const std::string& GetAnswer() const + { + return answer_; + } + + void ParseJsonBody(Json::Value& target) const; + + const HttpHeaders& GetAnswerHeaders() const + { + return headers_; + } + }; + + + private: + Orthanc::HttpMethod method_; + std::string uri_; + std::string body_; + HttpHeaders headers_; + unsigned int timeout_; + + public: + OrthancRestApiCommand(); + + virtual Type GetType() const + { + return Type_OrthancRestApi; + } + + void SetMethod(Orthanc::HttpMethod method) + { + method_ = method; + } + + void SetUri(const std::string& uri) + { + uri_ = uri; + } + + void SetBody(const std::string& body) + { + body_ = body; + } + + void SetBody(const Json::Value& json); + + void SetHttpHeader(const std::string& key, + const std::string& value) + { + headers_[key] = value; + } + + Orthanc::HttpMethod GetMethod() const + { + return method_; + } + + const std::string& GetUri() const + { + return uri_; + } + + const std::string& GetBody() const; + + const HttpHeaders& GetHttpHeaders() const + { + return headers_; + } + + void SetTimeout(unsigned int seconds) + { + timeout_ = seconds; + } + + unsigned int GetTimeout() const + { + return timeout_; + } + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Oracle/SleepOracleCommand.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Oracle/SleepOracleCommand.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,52 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../Messages/IMessage.h" +#include "OracleCommandWithPayload.h" + +namespace OrthancStone +{ + class SleepOracleCommand : public OracleCommandWithPayload + { + private: + unsigned int milliseconds_; + + public: + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, TimeoutMessage, SleepOracleCommand); + + SleepOracleCommand(unsigned int milliseconds) : + milliseconds_(milliseconds) + { + } + + virtual Type GetType() const + { + return Type_Sleep; + } + + unsigned int GetDelay() const + { + return milliseconds_; + } + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Oracle/ThreadedOracle.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Oracle/ThreadedOracle.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,507 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "ThreadedOracle.h" + +#include "GetOrthancImageCommand.h" +#include "GetOrthancWebViewerJpegCommand.h" +#include "OrthancRestApiCommand.h" +#include "SleepOracleCommand.h" +#include "OracleCommandExceptionMessage.h" + +#include +#include +#include +#include + + +namespace OrthancStone +{ + class ThreadedOracle::Item : public Orthanc::IDynamicObject + { + private: + const IObserver& receiver_; + std::auto_ptr command_; + + public: + Item(const IObserver& receiver, + IOracleCommand* command) : + receiver_(receiver), + command_(command) + { + if (command == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + const IObserver& GetReceiver() const + { + return receiver_; + } + + IOracleCommand& GetCommand() + { + assert(command_.get() != NULL); + return *command_; + } + }; + + + class ThreadedOracle::SleepingCommands : public boost::noncopyable + { + private: + class Item + { + private: + const IObserver& receiver_; + std::auto_ptr command_; + boost::posix_time::ptime expiration_; + + public: + Item(const IObserver& receiver, + SleepOracleCommand* command) : + receiver_(receiver), + command_(command) + { + if (command == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + expiration_ = (boost::posix_time::second_clock::local_time() + + boost::posix_time::milliseconds(command_->GetDelay())); + } + + const boost::posix_time::ptime& GetExpirationTime() const + { + return expiration_; + } + + void Awake(IMessageEmitter& emitter) + { + assert(command_.get() != NULL); + + SleepOracleCommand::TimeoutMessage message(*command_); + emitter.EmitMessage(receiver_, message); + } + }; + + typedef std::list Content; + + boost::mutex mutex_; + Content content_; + + public: + ~SleepingCommands() + { + for (Content::iterator it = content_.begin(); it != content_.end(); ++it) + { + if (*it != NULL) + { + delete *it; + } + } + } + + void Add(const IObserver& receiver, + SleepOracleCommand* command) // Takes ownership + { + boost::mutex::scoped_lock lock(mutex_); + + content_.push_back(new Item(receiver, command)); + } + + void AwakeExpired(IMessageEmitter& emitter) + { + boost::mutex::scoped_lock lock(mutex_); + + const boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); + + Content stillSleeping; + + for (Content::iterator it = content_.begin(); it != content_.end(); ++it) + { + if (*it != NULL && + (*it)->GetExpirationTime() <= now) + { + (*it)->Awake(emitter); + delete *it; + *it = NULL; + } + else + { + stillSleeping.push_back(*it); + } + } + + // Compact the still-sleeping commands + content_ = stillSleeping; + } + }; + + + static void CopyHttpHeaders(Orthanc::HttpClient& client, + const Orthanc::HttpClient::HttpHeaders& headers) + { + for (Orthanc::HttpClient::HttpHeaders::const_iterator + it = headers.begin(); it != headers.end(); it++ ) + { + client.AddHeader(it->first, it->second); + } + } + + + static void DecodeAnswer(std::string& answer, + const Orthanc::HttpClient::HttpHeaders& headers) + { + Orthanc::HttpCompression contentEncoding = Orthanc::HttpCompression_None; + + for (Orthanc::HttpClient::HttpHeaders::const_iterator it = headers.begin(); + it != headers.end(); ++it) + { + std::string s; + Orthanc::Toolbox::ToLowerCase(s, it->first); + + if (s == "content-encoding") + { + if (it->second == "gzip") + { + contentEncoding = Orthanc::HttpCompression_Gzip; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol, + "Unsupported HTTP Content-Encoding: " + it->second); + } + + break; + } + } + + if (contentEncoding == Orthanc::HttpCompression_Gzip) + { + std::string compressed; + answer.swap(compressed); + + Orthanc::GzipCompressor compressor; + compressor.Uncompress(answer, compressed.c_str(), compressed.size()); + } + } + + + static void Execute(IMessageEmitter& emitter, + const Orthanc::WebServiceParameters& orthanc, + const IObserver& receiver, + const OrthancRestApiCommand& command) + { + Orthanc::HttpClient client(orthanc, command.GetUri()); + client.SetMethod(command.GetMethod()); + client.SetTimeout(command.GetTimeout()); + + CopyHttpHeaders(client, command.GetHttpHeaders()); + + if (command.GetMethod() == Orthanc::HttpMethod_Post || + command.GetMethod() == Orthanc::HttpMethod_Put) + { + client.SetBody(command.GetBody()); + } + + std::string answer; + Orthanc::HttpClient::HttpHeaders answerHeaders; + client.ApplyAndThrowException(answer, answerHeaders); + + DecodeAnswer(answer, answerHeaders); + + OrthancRestApiCommand::SuccessMessage message(command, answerHeaders, answer); + emitter.EmitMessage(receiver, message); + } + + + static void Execute(IMessageEmitter& emitter, + const Orthanc::WebServiceParameters& orthanc, + const IObserver& receiver, + const GetOrthancImageCommand& command) + { + Orthanc::HttpClient client(orthanc, command.GetUri()); + client.SetTimeout(command.GetTimeout()); + + CopyHttpHeaders(client, command.GetHttpHeaders()); + + std::string answer; + Orthanc::HttpClient::HttpHeaders answerHeaders; + client.ApplyAndThrowException(answer, answerHeaders); + + DecodeAnswer(answer, answerHeaders); + + command.ProcessHttpAnswer(emitter, receiver, answer, answerHeaders); + } + + + static void Execute(IMessageEmitter& emitter, + const Orthanc::WebServiceParameters& orthanc, + const IObserver& receiver, + const GetOrthancWebViewerJpegCommand& command) + { + Orthanc::HttpClient client(orthanc, command.GetUri()); + client.SetTimeout(command.GetTimeout()); + + CopyHttpHeaders(client, command.GetHttpHeaders()); + + std::string answer; + Orthanc::HttpClient::HttpHeaders answerHeaders; + client.ApplyAndThrowException(answer, answerHeaders); + + DecodeAnswer(answer, answerHeaders); + + command.ProcessHttpAnswer(emitter, receiver, answer); + } + + + void ThreadedOracle::Step() + { + std::auto_ptr object(queue_.Dequeue(100)); + + if (object.get() != NULL) + { + Item& item = dynamic_cast(*object); + + try + { + switch (item.GetCommand().GetType()) + { + case IOracleCommand::Type_Sleep: + { + SleepOracleCommand& command = dynamic_cast(item.GetCommand()); + + std::auto_ptr copy(new SleepOracleCommand(command.GetDelay())); + + if (command.HasPayload()) + { + copy->SetPayload(command.ReleasePayload()); + } + + sleepingCommands_->Add(item.GetReceiver(), copy.release()); + + break; + } + + case IOracleCommand::Type_OrthancRestApi: + Execute(emitter_, orthanc_, item.GetReceiver(), + dynamic_cast(item.GetCommand())); + break; + + case IOracleCommand::Type_GetOrthancImage: + Execute(emitter_, orthanc_, item.GetReceiver(), + dynamic_cast(item.GetCommand())); + break; + + case IOracleCommand::Type_GetOrthancWebViewerJpeg: + Execute(emitter_, orthanc_, item.GetReceiver(), + dynamic_cast(item.GetCommand())); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception within the oracle: " << e.What(); + emitter_.EmitMessage(item.GetReceiver(), OracleCommandExceptionMessage(item.GetCommand(), e)); + } + catch (...) + { + LOG(ERROR) << "Threaded exception within the oracle"; + emitter_.EmitMessage(item.GetReceiver(), OracleCommandExceptionMessage + (item.GetCommand(), Orthanc::ErrorCode_InternalError)); + } + } + } + + + void ThreadedOracle::Worker(ThreadedOracle* that) + { + assert(that != NULL); + + for (;;) + { + { + boost::mutex::scoped_lock lock(that->mutex_); + if (that->state_ != State_Running) + { + return; + } + } + + that->Step(); + } + } + + + void ThreadedOracle::SleepingWorker(ThreadedOracle* that) + { + assert(that != NULL); + + for (;;) + { + { + boost::mutex::scoped_lock lock(that->mutex_); + if (that->state_ != State_Running) + { + return; + } + } + + that->sleepingCommands_->AwakeExpired(that->emitter_); + + boost::this_thread::sleep(boost::posix_time::milliseconds(that->sleepingTimeResolution_)); + } + } + + + void ThreadedOracle::StopInternal() + { + { + boost::mutex::scoped_lock lock(mutex_); + + if (state_ == State_Setup || + state_ == State_Stopped) + { + return; + } + else + { + state_ = State_Stopped; + } + } + + if (sleepingWorker_.joinable()) + { + sleepingWorker_.join(); + } + + for (size_t i = 0; i < workers_.size(); i++) + { + if (workers_[i] != NULL) + { + if (workers_[i]->joinable()) + { + workers_[i]->join(); + } + + delete workers_[i]; + } + } + } + + + ThreadedOracle::ThreadedOracle(IMessageEmitter& emitter) : + emitter_(emitter), + state_(State_Setup), + workers_(4), + sleepingCommands_(new SleepingCommands), + sleepingTimeResolution_(50) // By default, time resolution of 50ms + { + } + + + void ThreadedOracle::SetOrthancParameters(const Orthanc::WebServiceParameters& orthanc) + { + boost::mutex::scoped_lock lock(mutex_); + + if (state_ != State_Setup) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + orthanc_ = orthanc; + } + } + + + void ThreadedOracle::SetWorkersCount(unsigned int count) + { + boost::mutex::scoped_lock lock(mutex_); + + if (count <= 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else if (state_ != State_Setup) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + workers_.resize(count); + } + } + + + void ThreadedOracle::SetSleepingTimeResolution(unsigned int milliseconds) + { + boost::mutex::scoped_lock lock(mutex_); + + if (milliseconds <= 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else if (state_ != State_Setup) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + sleepingTimeResolution_ = milliseconds; + } + } + + + void ThreadedOracle::Start() + { + boost::mutex::scoped_lock lock(mutex_); + + if (state_ != State_Setup) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + state_ = State_Running; + + for (unsigned int i = 0; i < workers_.size(); i++) + { + workers_[i] = new boost::thread(Worker, this); + } + + sleepingWorker_ = boost::thread(SleepingWorker, this); + } + } + + + void ThreadedOracle::Schedule(const IObserver& receiver, + IOracleCommand* command) + { + queue_.Enqueue(new Item(receiver, command)); + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Oracle/ThreadedOracle.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Oracle/ThreadedOracle.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,96 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#if !defined(ORTHANC_ENABLE_THREADS) +# error The macro ORTHANC_ENABLE_THREADS must be defined +#endif + +#if ORTHANC_ENABLE_THREADS != 1 +# error This file can only compiled for native targets +#endif + +#include "../Messages/IMessageEmitter.h" +#include "IOracle.h" + +#include +#include + + +namespace OrthancStone +{ + class ThreadedOracle : public IOracle + { + private: + enum State + { + State_Setup, + State_Running, + State_Stopped + }; + + class Item; + class SleepingCommands; + + IMessageEmitter& emitter_; + Orthanc::WebServiceParameters orthanc_; + Orthanc::SharedMessageQueue queue_; + State state_; + boost::mutex mutex_; + std::vector workers_; + boost::shared_ptr sleepingCommands_; + boost::thread sleepingWorker_; + unsigned int sleepingTimeResolution_; + + void Step(); + + static void Worker(ThreadedOracle* that); + + static void SleepingWorker(ThreadedOracle* that); + + void StopInternal(); + + public: + ThreadedOracle(IMessageEmitter& emitter); + + virtual ~ThreadedOracle() + { + StopInternal(); + } + + void SetOrthancParameters(const Orthanc::WebServiceParameters& orthanc); + + void SetWorkersCount(unsigned int count); + + void SetSleepingTimeResolution(unsigned int milliseconds); + + void Start(); + + void Stop() + { + StopInternal(); + } + + virtual void Schedule(const IObserver& receiver, + IOracleCommand* command); + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographyDicomLayer.cpp --- a/Framework/Radiography/RadiographyDicomLayer.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographyDicomLayer.cpp Wed May 22 16:13:46 2019 +0200 @@ -22,7 +22,7 @@ #include "RadiographyDicomLayer.h" #include "RadiographyScene.h" -#include "../Toolbox/DicomFrameConverter.h" +#include "../Deprecated/Toolbox/DicomFrameConverter.h" #include #include @@ -85,11 +85,11 @@ { if (tmp == "MONOCHROME1") { - SetPreferredPhotomotricDisplayMode(PhotometricDisplayMode_Monochrome1); + SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode_Monochrome1); } else if (tmp == "MONOCHROME2") { - SetPreferredPhotomotricDisplayMode(PhotometricDisplayMode_Monochrome2); + SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode_Monochrome2); } } } diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographyDicomLayer.h --- a/Framework/Radiography/RadiographyDicomLayer.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographyDicomLayer.h Wed May 22 16:13:46 2019 +0200 @@ -21,7 +21,7 @@ #pragma once -#include "../Toolbox/DicomFrameConverter.h" +#include "../Deprecated/Toolbox/DicomFrameConverter.h" #include "RadiographyLayer.h" #include diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographyLayer.cpp --- a/Framework/Radiography/RadiographyLayer.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographyLayer.cpp Wed May 22 16:13:46 2019 +0200 @@ -125,7 +125,7 @@ hasSize_(false), width_(0), height_(0), - prefferedPhotometricDisplayMode_(PhotometricDisplayMode_Default), + prefferedPhotometricDisplayMode_(RadiographyPhotometricDisplayMode_Default), scene_(scene) { UpdateTransform(); @@ -137,7 +137,7 @@ UpdateTransform(); } - void RadiographyLayer::SetPreferredPhotomotricDisplayMode(PhotometricDisplayMode prefferedPhotometricDisplayMode) + void RadiographyLayer::SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode prefferedPhotometricDisplayMode) { prefferedPhotometricDisplayMode_ = prefferedPhotometricDisplayMode; @@ -349,21 +349,21 @@ ControlPoint cp; switch (index) { - case ControlPoint_TopLeftCorner: - cp = ControlPoint(cropX, cropY, ControlPoint_TopLeftCorner); - break; + case RadiographyControlPointType_TopLeftCorner: + cp = ControlPoint(cropX, cropY, RadiographyControlPointType_TopLeftCorner); + break; - case ControlPoint_TopRightCorner: - cp = ControlPoint(cropX + cropWidth, cropY, ControlPoint_TopRightCorner); - break; + case RadiographyControlPointType_TopRightCorner: + cp = ControlPoint(cropX + cropWidth, cropY, RadiographyControlPointType_TopRightCorner); + break; - case ControlPoint_BottomLeftCorner: - cp = ControlPoint(cropX, cropY + cropHeight, ControlPoint_BottomLeftCorner); - break; + case RadiographyControlPointType_BottomLeftCorner: + cp = ControlPoint(cropX, cropY + cropHeight, RadiographyControlPointType_BottomLeftCorner); + break; - case ControlPoint_BottomRightCorner: - cp = ControlPoint(cropX + cropWidth, cropY + cropHeight, ControlPoint_BottomRightCorner); - break; + case RadiographyControlPointType_BottomRightCorner: + cp = ControlPoint(cropX + cropWidth, cropY + cropHeight, RadiographyControlPointType_BottomRightCorner); + break; default: throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographyLayer.h --- a/Framework/Radiography/RadiographyLayer.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographyLayer.h Wed May 22 16:13:46 2019 +0200 @@ -31,6 +31,23 @@ { class RadiographyScene; + enum RadiographyControlPointType + { + RadiographyControlPointType_TopLeftCorner = 0, + RadiographyControlPointType_TopRightCorner = 1, + RadiographyControlPointType_BottomRightCorner = 2, + RadiographyControlPointType_BottomLeftCorner = 3 + }; + + enum RadiographyPhotometricDisplayMode + { + RadiographyPhotometricDisplayMode_Default, + + RadiographyPhotometricDisplayMode_Monochrome1, + RadiographyPhotometricDisplayMode_Monochrome2 + }; + + struct ControlPoint { double x; @@ -196,7 +213,7 @@ AffineTransform2D transform_; AffineTransform2D transformInverse_; Geometry geometry_; - PhotometricDisplayMode prefferedPhotometricDisplayMode_; + RadiographyPhotometricDisplayMode prefferedPhotometricDisplayMode_; const RadiographyScene& scene_; protected: @@ -210,7 +227,7 @@ return transformInverse_; } - void SetPreferredPhotomotricDisplayMode(PhotometricDisplayMode prefferedPhotometricDisplayMode); + void SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode prefferedPhotometricDisplayMode); private: void UpdateTransform(); @@ -325,7 +342,7 @@ virtual bool GetDefaultWindowing(float& center, float& width) const = 0; - PhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const + RadiographyPhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const { return prefferedPhotometricDisplayMode_; } diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographyLayerCropTracker.cpp --- a/Framework/Radiography/RadiographyLayerCropTracker.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographyLayerCropTracker.cpp Wed May 22 16:13:46 2019 +0200 @@ -66,7 +66,7 @@ RadiographyLayerCropTracker::RadiographyLayerCropTracker(UndoRedoStack& undoRedoStack, RadiographyScene& scene, - const ViewportGeometry& view, + const Deprecated::ViewportGeometry& view, size_t layer, const ControlPoint& startControlPoint) : undoRedoStack_(undoRedoStack), @@ -100,8 +100,8 @@ int displayY, double sceneX, double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches) + const std::vector& displayTouches, + const std::vector& sceneTouches) { if (accessor_.IsValid()) { @@ -112,8 +112,8 @@ { unsigned int targetX, targetWidth; - if (startControlPoint_.index == ControlPoint_TopLeftCorner || - startControlPoint_.index == ControlPoint_BottomLeftCorner) + if (startControlPoint_.index == RadiographyControlPointType_TopLeftCorner || + startControlPoint_.index == RadiographyControlPointType_BottomLeftCorner) { targetX = std::min(x, cropX_ + cropWidth_); targetWidth = cropX_ + cropWidth_ - targetX; @@ -126,8 +126,8 @@ unsigned int targetY, targetHeight; - if (startControlPoint_.index == ControlPoint_TopLeftCorner || - startControlPoint_.index == ControlPoint_TopRightCorner) + if (startControlPoint_.index == RadiographyControlPointType_TopLeftCorner || + startControlPoint_.index == RadiographyControlPointType_TopRightCorner) { targetY = std::min(y, cropY_ + cropHeight_); targetHeight = cropY_ + cropHeight_ - targetY; diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographyLayerCropTracker.h --- a/Framework/Radiography/RadiographyLayerCropTracker.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographyLayerCropTracker.h Wed May 22 16:13:46 2019 +0200 @@ -22,13 +22,13 @@ #pragma once #include "../Toolbox/UndoRedoStack.h" -#include "../Toolbox/ViewportGeometry.h" -#include "../Widgets/IWorldSceneMouseTracker.h" +#include "../Deprecated/Toolbox/ViewportGeometry.h" +#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" #include "RadiographyScene.h" namespace OrthancStone { - class RadiographyLayerCropTracker : public IWorldSceneMouseTracker + class RadiographyLayerCropTracker : public Deprecated::IWorldSceneMouseTracker { private: class UndoRedoCommand; @@ -44,7 +44,7 @@ public: RadiographyLayerCropTracker(UndoRedoStack& undoRedoStack, RadiographyScene& scene, - const ViewportGeometry& view, + const Deprecated::ViewportGeometry& view, size_t layer, const ControlPoint& startControlPoint); @@ -62,7 +62,7 @@ int displayY, double sceneX, double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches); + const std::vector& displayTouches, + const std::vector& sceneTouches); }; } diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographyLayerMaskTracker.cpp --- a/Framework/Radiography/RadiographyLayerMaskTracker.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographyLayerMaskTracker.cpp Wed May 22 16:13:46 2019 +0200 @@ -85,7 +85,7 @@ RadiographyLayerMaskTracker::RadiographyLayerMaskTracker(UndoRedoStack& undoRedoStack, RadiographyScene& scene, - const ViewportGeometry& view, + const Deprecated::ViewportGeometry& view, size_t layer, const ControlPoint& startSceneControlPoint) : undoRedoStack_(undoRedoStack), @@ -116,8 +116,8 @@ int displayY, double sceneX, double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches) + const std::vector& displayTouches, + const std::vector& sceneTouches) { if (accessor_.IsValid()) { diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographyLayerMaskTracker.h --- a/Framework/Radiography/RadiographyLayerMaskTracker.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographyLayerMaskTracker.h Wed May 22 16:13:46 2019 +0200 @@ -22,13 +22,13 @@ #pragma once #include "../Toolbox/UndoRedoStack.h" -#include "../Toolbox/ViewportGeometry.h" -#include "../Widgets/IWorldSceneMouseTracker.h" +#include "../Deprecated/Toolbox/ViewportGeometry.h" +#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" #include "RadiographyScene.h" namespace OrthancStone { - class RadiographyLayerMaskTracker : public IWorldSceneMouseTracker + class RadiographyLayerMaskTracker : public Deprecated::IWorldSceneMouseTracker { private: class UndoRedoCommand; @@ -41,7 +41,7 @@ public: RadiographyLayerMaskTracker(UndoRedoStack& undoRedoStack, RadiographyScene& scene, - const ViewportGeometry& view, + const Deprecated::ViewportGeometry& view, size_t layer, const ControlPoint& startSceneControlPoint); @@ -59,7 +59,7 @@ int displayY, double sceneX, double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches); + const std::vector& displayTouches, + const std::vector& sceneTouches); }; } diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographyLayerMoveTracker.cpp --- a/Framework/Radiography/RadiographyLayerMoveTracker.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographyLayerMoveTracker.cpp Wed May 22 16:13:46 2019 +0200 @@ -98,8 +98,8 @@ int displayY, double sceneX, double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches) + const std::vector& displayTouches, + const std::vector& sceneTouches) { if (accessor_.IsValid()) { diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographyLayerMoveTracker.h --- a/Framework/Radiography/RadiographyLayerMoveTracker.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographyLayerMoveTracker.h Wed May 22 16:13:46 2019 +0200 @@ -22,12 +22,12 @@ #pragma once #include "../Toolbox/UndoRedoStack.h" -#include "../Widgets/IWorldSceneMouseTracker.h" +#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" #include "RadiographyScene.h" namespace OrthancStone { - class RadiographyLayerMoveTracker : public IWorldSceneMouseTracker + class RadiographyLayerMoveTracker : public Deprecated::IWorldSceneMouseTracker { private: class UndoRedoCommand; @@ -62,7 +62,7 @@ int displayY, double sceneX, double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches); + const std::vector& displayTouches, + const std::vector& sceneTouches); }; } diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographyLayerResizeTracker.cpp --- a/Framework/Radiography/RadiographyLayerResizeTracker.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographyLayerResizeTracker.cpp Wed May 22 16:13:46 2019 +0200 @@ -102,20 +102,20 @@ size_t oppositeControlPointType; switch (startControlPoint.index) { - case ControlPoint_TopLeftCorner: - oppositeControlPointType = ControlPoint_BottomRightCorner; + case RadiographyControlPointType_TopLeftCorner: + oppositeControlPointType = RadiographyControlPointType_BottomRightCorner; break; - case ControlPoint_TopRightCorner: - oppositeControlPointType = ControlPoint_BottomLeftCorner; + case RadiographyControlPointType_TopRightCorner: + oppositeControlPointType = RadiographyControlPointType_BottomLeftCorner; break; - case ControlPoint_BottomLeftCorner: - oppositeControlPointType = ControlPoint_TopRightCorner; + case RadiographyControlPointType_BottomLeftCorner: + oppositeControlPointType = RadiographyControlPointType_TopRightCorner; break; - case ControlPoint_BottomRightCorner: - oppositeControlPointType = ControlPoint_TopLeftCorner; + case RadiographyControlPointType_BottomRightCorner: + oppositeControlPointType = RadiographyControlPointType_TopLeftCorner; break; default: @@ -159,8 +159,8 @@ int displayY, double sceneX, double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches) + const std::vector& displayTouches, + const std::vector& sceneTouches) { static const double ROUND_SCALING = 0.1; diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographyLayerResizeTracker.h --- a/Framework/Radiography/RadiographyLayerResizeTracker.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographyLayerResizeTracker.h Wed May 22 16:13:46 2019 +0200 @@ -22,12 +22,12 @@ #pragma once #include "../Toolbox/UndoRedoStack.h" -#include "../Widgets/IWorldSceneMouseTracker.h" +#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" #include "RadiographyScene.h" namespace OrthancStone { - class RadiographyLayerResizeTracker : public IWorldSceneMouseTracker + class RadiographyLayerResizeTracker : public Deprecated::IWorldSceneMouseTracker { private: class UndoRedoCommand; @@ -63,7 +63,7 @@ int displayY, double sceneX, double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches); + const std::vector& displayTouches, + const std::vector& sceneTouches); }; } diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographyLayerRotateTracker.cpp --- a/Framework/Radiography/RadiographyLayerRotateTracker.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographyLayerRotateTracker.cpp Wed May 22 16:13:46 2019 +0200 @@ -88,7 +88,7 @@ RadiographyLayerRotateTracker::RadiographyLayerRotateTracker(UndoRedoStack& undoRedoStack, RadiographyScene& scene, - const ViewportGeometry& view, + const Deprecated::ViewportGeometry& view, size_t layer, double x, double y, @@ -133,8 +133,8 @@ int displayY, double sceneX, double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches) + const std::vector& displayTouches, + const std::vector& sceneTouches) { static const double ROUND_ANGLE = 15.0 / 180.0 * boost::math::constants::pi(); diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographyLayerRotateTracker.h --- a/Framework/Radiography/RadiographyLayerRotateTracker.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographyLayerRotateTracker.h Wed May 22 16:13:46 2019 +0200 @@ -22,14 +22,14 @@ #pragma once #include "../Toolbox/UndoRedoStack.h" -#include "../Toolbox/ViewportGeometry.h" -#include "../Widgets/IWorldSceneMouseTracker.h" +#include "../Deprecated/Toolbox/ViewportGeometry.h" +#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" #include "RadiographyScene.h" namespace OrthancStone { - class RadiographyLayerRotateTracker : public IWorldSceneMouseTracker + class RadiographyLayerRotateTracker : public Deprecated::IWorldSceneMouseTracker { private: class UndoRedoCommand; @@ -49,7 +49,7 @@ public: RadiographyLayerRotateTracker(UndoRedoStack& undoRedoStack, RadiographyScene& scene, - const ViewportGeometry& view, + const Deprecated::ViewportGeometry& view, size_t layer, double x, double y, @@ -69,7 +69,7 @@ int displayY, double sceneX, double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches); + const std::vector& displayTouches, + const std::vector& sceneTouches); }; } diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographyMaskLayer.cpp --- a/Framework/Radiography/RadiographyMaskLayer.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographyMaskLayer.cpp Wed May 22 16:13:46 2019 +0200 @@ -135,8 +135,16 @@ // first fill the complete image Orthanc::ImageProcessing::Set(*mask_, OUT_MASK_VALUE); + // clip corners + std::vector clippedCorners; + for (size_t i = 0; i < corners_.size(); i++) + { + clippedCorners.push_back(corners_[i]); + clippedCorners[i].ClipTo(0, mask_->GetWidth() - 1, 0, mask_->GetHeight() - 1); + } + // fill mask - Orthanc::ImageProcessing::FillPolygon(*mask_, corners_, IN_MASK_VALUE); + Orthanc::ImageProcessing::FillPolygon(*mask_, clippedCorners, IN_MASK_VALUE); } diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographyScene.cpp --- a/Framework/Radiography/RadiographyScene.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographyScene.cpp Wed May 22 16:13:46 2019 +0200 @@ -25,7 +25,7 @@ #include "RadiographyDicomLayer.h" #include "RadiographyTextLayer.h" #include "RadiographyMaskLayer.h" -#include "../Toolbox/DicomFrameConverter.h" +#include "../Deprecated/Toolbox/DicomFrameConverter.h" #include #include @@ -172,18 +172,18 @@ } } - PhotometricDisplayMode RadiographyScene::GetPreferredPhotomotricDisplayMode() const + RadiographyPhotometricDisplayMode RadiographyScene::GetPreferredPhotomotricDisplayMode() const { // return the mode of the first layer who "cares" about its display mode (normaly, the one and only layer that is a DicomLayer) for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++) { - if (it->second->GetPreferredPhotomotricDisplayMode() != PhotometricDisplayMode_Default) + if (it->second->GetPreferredPhotomotricDisplayMode() != RadiographyPhotometricDisplayMode_Default) { return it->second->GetPreferredPhotomotricDisplayMode(); } } - return PhotometricDisplayMode_Default; + return RadiographyPhotometricDisplayMode_Default; } @@ -345,7 +345,7 @@ const std::string& instance, unsigned int frame, Deprecated::DicomFrameConverter* converter, // takes ownership - PhotometricDisplayMode preferredPhotometricDisplayMode, + RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode, RadiographyLayer::Geometry* geometry) { RadiographyDicomLayer& layer = dynamic_cast(RegisterLayer(new RadiographyDicomLayer(IObservable::GetBroker(), *this))); @@ -364,7 +364,7 @@ return layer; } - RadiographyLayer& RadiographyScene::LoadDicomFrame(OrthancApiClient& orthanc, + RadiographyLayer& RadiographyScene::LoadDicomFrame(Deprecated::OrthancApiClient& orthanc, const std::string& instance, unsigned int frame, bool httpCompression, @@ -379,18 +379,18 @@ } { - IWebService::HttpHeaders headers; + Deprecated::IWebService::HttpHeaders headers; std::string uri = "/instances/" + instance + "/tags"; orthanc.GetBinaryAsync( uri, headers, - new Callable + new Callable (*this, &RadiographyScene::OnTagsReceived), NULL, new Orthanc::SingleValueObject(layer.GetIndex())); } { - IWebService::HttpHeaders headers; + Deprecated::IWebService::HttpHeaders headers; headers["Accept"] = "image/x-portable-arbitrarymap"; if (httpCompression) @@ -403,7 +403,7 @@ orthanc.GetBinaryAsync( uri, headers, - new Callable + new Callable (*this, &RadiographyScene::OnFrameReceived), NULL, new Orthanc::SingleValueObject(layer.GetIndex())); } @@ -412,7 +412,7 @@ } - RadiographyLayer& RadiographyScene::LoadDicomWebFrame(IWebService& web) + RadiographyLayer& RadiographyScene::LoadDicomWebFrame(Deprecated::IWebService& web) { RadiographyLayer& layer = RegisterLayer(new RadiographyDicomLayer(IObservable::GetBroker(), *this)); @@ -422,7 +422,7 @@ - void RadiographyScene::OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) + void RadiographyScene::OnTagsReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message) { size_t index = dynamic_cast&> (message.GetPayload()).GetValue(); @@ -452,7 +452,7 @@ } - void RadiographyScene::OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) + void RadiographyScene::OnFrameReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message) { size_t index = dynamic_cast&>(message.GetPayload()).GetValue(); @@ -642,9 +642,9 @@ createDicomRequestContent["Tags"] = dicomTags; - PhotometricDisplayMode photometricMode = GetPreferredPhotomotricDisplayMode(); - if ((invert && photometricMode != PhotometricDisplayMode_Monochrome2) || - (!invert && photometricMode == PhotometricDisplayMode_Monochrome1)) + RadiographyPhotometricDisplayMode photometricMode = GetPreferredPhotomotricDisplayMode(); + if ((invert && photometricMode != RadiographyPhotometricDisplayMode_Monochrome2) || + (!invert && photometricMode == RadiographyPhotometricDisplayMode_Monochrome1)) { createDicomRequestContent["Tags"]["PhotometricInterpretation"] = "MONOCHROME1"; } @@ -726,7 +726,7 @@ } - void RadiographyScene::ExportDicom(OrthancApiClient& orthanc, + void RadiographyScene::ExportDicom(Deprecated::OrthancApiClient& orthanc, const Json::Value& dicomTags, const std::string& parentOrthancId, double pixelSpacingX, @@ -741,7 +741,7 @@ orthanc.PostJsonAsyncExpectJson( "/tools/create-dicom", createDicomRequestContent, - new Callable + new Callable (*this, &RadiographyScene::OnDicomExported), NULL, NULL); @@ -750,7 +750,7 @@ // Export using PAM is faster than using PNG, but requires Orthanc // core >= 1.4.3 - void RadiographyScene::ExportDicom(OrthancApiClient& orthanc, + void RadiographyScene::ExportDicom(Deprecated::OrthancApiClient& orthanc, const Orthanc::DicomMap& dicom, const std::string& parentOrthancId, double pixelSpacingX, @@ -778,19 +778,19 @@ ExportDicom(orthanc, jsonTags, parentOrthancId, pixelSpacingX, pixelSpacingY, invert, interpolation, usePam); } - void RadiographyScene::OnDicomExported(const OrthancApiClient::JsonResponseReadyMessage& message) + void RadiographyScene::OnDicomExported(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) { LOG(INFO) << "DICOM export was successful: " << message.GetJson().toStyledString(); } - void RadiographyScene::OnDicomWebReceived(const IWebService::HttpRequestSuccessMessage& message) + void RadiographyScene::OnDicomWebReceived(const Deprecated::IWebService::HttpRequestSuccessMessage& message) { LOG(INFO) << "DICOMweb WADO-RS received: " << message.GetAnswerSize() << " bytes"; - const IWebService::HttpHeaders& h = message.GetAnswerHttpHeaders(); - for (IWebService::HttpHeaders::const_iterator + const Deprecated::IWebService::HttpHeaders& h = message.GetAnswerHttpHeaders(); + for (Deprecated::IWebService::HttpHeaders::const_iterator it = h.begin(); it != h.end(); ++it) { printf("[%s] = [%s]\n", it->first.c_str(), it->second.c_str()); diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographyScene.h --- a/Framework/Radiography/RadiographyScene.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographyScene.h Wed May 22 16:13:46 2019 +0200 @@ -22,9 +22,9 @@ #pragma once #include "RadiographyLayer.h" -#include "../Toolbox/DicomFrameConverter.h" -#include "../Toolbox/OrthancApiClient.h" -#include "Framework/StoneEnumerations.h" +#include "../Deprecated/Toolbox/DicomFrameConverter.h" +#include "../Deprecated/Toolbox/OrthancApiClient.h" +#include "../StoneEnumerations.h" #include "Core/Images/Image.h" #include "Core/Images/ImageProcessing.h" @@ -149,15 +149,15 @@ protected: RadiographyLayer& RegisterLayer(RadiographyLayer* layer); - void OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message); + void OnTagsReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message); - virtual void OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message); + virtual void OnFrameReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message); - void OnDicomExported(const OrthancApiClient::JsonResponseReadyMessage& message); + void OnDicomExported(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message); - void OnDicomWebReceived(const IWebService::HttpRequestSuccessMessage& message); + void OnDicomWebReceived(const Deprecated::IWebService::HttpRequestSuccessMessage& message); - void OnLayerEdited(const RadiographyLayer::LayerEditedMessage& message); + virtual void OnLayerEdited(const RadiographyLayer::LayerEditedMessage& message); public: RadiographyScene(MessageBroker& broker); @@ -169,10 +169,10 @@ void GetWindowingWithDefault(float& center, float& width) const; - void SetWindowing(float center, - float width); + virtual void SetWindowing(float center, + float width); - PhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const; + RadiographyPhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const; RadiographyLayer& LoadText(const Orthanc::Font& font, const std::string& utf8, @@ -194,16 +194,16 @@ const std::string& instance, unsigned int frame, Deprecated::DicomFrameConverter* converter, // takes ownership - PhotometricDisplayMode preferredPhotometricDisplayMode, + RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode, RadiographyLayer::Geometry* geometry); - virtual RadiographyLayer& LoadDicomFrame(OrthancApiClient& orthanc, + virtual RadiographyLayer& LoadDicomFrame(Deprecated::OrthancApiClient& orthanc, const std::string& instance, unsigned int frame, bool httpCompression, RadiographyLayer::Geometry* geometry); // pass NULL if you want default geometry - RadiographyLayer& LoadDicomWebFrame(IWebService& web); + RadiographyLayer& LoadDicomWebFrame(Deprecated::IWebService& web); void RemoveLayer(size_t layerIndex); @@ -278,7 +278,7 @@ // Export using PAM is faster than using PNG, but requires Orthanc // core >= 1.4.3 - void ExportDicom(OrthancApiClient& orthanc, + void ExportDicom(Deprecated::OrthancApiClient& orthanc, const Orthanc::DicomMap& dicom, const std::string& parentOrthancId, double pixelSpacingX, @@ -287,7 +287,7 @@ ImageInterpolation interpolation, bool usePam); - void ExportDicom(OrthancApiClient& orthanc, + void ExportDicom(Deprecated::OrthancApiClient& orthanc, const Json::Value& dicomTags, const std::string& parentOrthancId, double pixelSpacingX, diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographySceneReader.cpp --- a/Framework/Radiography/RadiographySceneReader.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographySceneReader.cpp Wed May 22 16:13:46 2019 +0200 @@ -21,7 +21,7 @@ #include "RadiographySceneReader.h" -#include +#include "../Deprecated/Toolbox/DicomFrameConverter.h" #include #include @@ -33,7 +33,7 @@ void RadiographySceneBuilder::Read(const Json::Value& input, Orthanc::ImageAccessor* dicomImage /* takes ownership */, Deprecated::DicomFrameConverter* dicomFrameConverter /* takes ownership */, - PhotometricDisplayMode preferredPhotometricDisplayMode + RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode ) { dicomImage_.reset(dicomImage); diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographySceneReader.h --- a/Framework/Radiography/RadiographySceneReader.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographySceneReader.h Wed May 22 16:13:46 2019 +0200 @@ -26,13 +26,13 @@ #include "RadiographyDicomLayer.h" #include "RadiographyMaskLayer.h" #include "RadiographyTextLayer.h" +#include "../Deprecated/Toolbox/OrthancApiClient.h" + #include #include namespace OrthancStone { - class OrthancApiClient; - // HACK: I had to introduce this builder class in order to be able to recreate a RadiographyScene // from a serialized scene that is passed to web-workers. // It needs some architecturing... @@ -42,8 +42,8 @@ RadiographyScene& scene_; const Orthanc::FontRegistry* fontRegistry_; std::auto_ptr dicomImage_; - std::auto_ptr dicomFrameConverter_; - PhotometricDisplayMode preferredPhotometricDisplayMode_; + std::auto_ptr dicomFrameConverter_; + RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode_; public: RadiographySceneBuilder(RadiographyScene& scene) : @@ -56,7 +56,7 @@ void Read(const Json::Value& input, Orthanc::ImageAccessor* dicomImage, // takes ownership Deprecated::DicomFrameConverter* dicomFrameConverter, // takes ownership - PhotometricDisplayMode preferredPhotometricDisplayMode + RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode ); void SetFontRegistry(const Orthanc::FontRegistry& fontRegistry) @@ -72,10 +72,10 @@ class RadiographySceneReader : public RadiographySceneBuilder { - OrthancApiClient& orthancApiClient_; + Deprecated::OrthancApiClient& orthancApiClient_; public: - RadiographySceneReader(RadiographyScene& scene, OrthancApiClient& orthancApiClient) : + RadiographySceneReader(RadiographyScene& scene, Deprecated::OrthancApiClient& orthancApiClient) : RadiographySceneBuilder(scene), orthancApiClient_(orthancApiClient) { diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographyWidget.cpp --- a/Framework/Radiography/RadiographyWidget.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographyWidget.cpp Wed May 22 16:13:46 2019 +0200 @@ -35,7 +35,7 @@ // MONOCHROME1 images must be inverted and the user can invert the // image, too -> XOR the two return (scene_->GetPreferredPhotomotricDisplayMode() == - PhotometricDisplayMode_Monochrome1) ^ invert_; + RadiographyPhotometricDisplayMode_Monochrome1) ^ invert_; } void RadiographyWidget::RenderBackground( @@ -46,14 +46,14 @@ switch (scene_->GetPreferredPhotomotricDisplayMode()) { - case PhotometricDisplayMode_Monochrome1: - case PhotometricDisplayMode_Default: + case RadiographyPhotometricDisplayMode_Monochrome1: + case RadiographyPhotometricDisplayMode_Default: if (IsInvertedInternal()) backgroundValue = maxValue; else backgroundValue = minValue; break; - case PhotometricDisplayMode_Monochrome2: + case RadiographyPhotometricDisplayMode_Monochrome2: if (IsInvertedInternal()) backgroundValue = minValue; else @@ -151,7 +151,7 @@ bool RadiographyWidget::RenderScene(CairoContext& context, - const ViewportGeometry& view) + const Deprecated::ViewportGeometry& view) { cairo_t* cr = context.GetObject(); diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographyWidget.h --- a/Framework/Radiography/RadiographyWidget.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographyWidget.h Wed May 22 16:13:46 2019 +0200 @@ -21,7 +21,7 @@ #pragma once -#include "../Widgets/WorldSceneWidget.h" +#include "../Deprecated/Widgets/WorldSceneWidget.h" #include "RadiographyScene.h" @@ -30,7 +30,7 @@ class RadiographyMaskLayer; class RadiographyWidget : - public WorldSceneWidget, + public Deprecated::WorldSceneWidget, public IObserver { private: @@ -53,7 +53,7 @@ } virtual bool RenderScene(CairoContext& context, - const ViewportGeometry& view); + const Deprecated::ViewportGeometry& view); virtual void RenderBackground(Orthanc::ImageAccessor& image, float minValue, float maxValue); diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographyWindowingTracker.cpp --- a/Framework/Radiography/RadiographyWindowingTracker.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographyWindowingTracker.cpp Wed May 22 16:13:46 2019 +0200 @@ -164,8 +164,8 @@ int displayY, double sceneX, double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches) + const std::vector& displayTouches, + const std::vector& sceneTouches) { // This follows the behavior of the Osimis Web viewer: // https://bitbucket.org/osimis/osimis-webviewer-plugin/src/master/frontend/src/app/viewport/image-plugins/windowing-viewport-tool.class.js diff -r a386bbc955dc -r 92c400a09f1b Framework/Radiography/RadiographyWindowingTracker.h --- a/Framework/Radiography/RadiographyWindowingTracker.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Radiography/RadiographyWindowingTracker.h Wed May 22 16:13:46 2019 +0200 @@ -22,12 +22,12 @@ #pragma once #include "../Toolbox/UndoRedoStack.h" -#include "../Widgets/IWorldSceneMouseTracker.h" +#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" #include "RadiographyScene.h" namespace OrthancStone { - class RadiographyWindowingTracker : public IWorldSceneMouseTracker + class RadiographyWindowingTracker : public Deprecated::IWorldSceneMouseTracker { public: enum Action @@ -83,7 +83,7 @@ int displayY, double sceneX, double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches); + const std::vector& displayTouches, + const std::vector& sceneTouches); }; } diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2D/Internals/FixedPointAligner.cpp --- a/Framework/Scene2D/Internals/FixedPointAligner.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2D/Internals/FixedPointAligner.cpp Wed May 22 16:13:46 2019 +0200 @@ -18,7 +18,7 @@ * along with this program. If not, see . **/ -#include +#include "../../Scene2DViewport/ViewportController.h" #include "FixedPointAligner.h" namespace OrthancStone diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2D/Internals/FixedPointAligner.h --- a/Framework/Scene2D/Internals/FixedPointAligner.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2D/Internals/FixedPointAligner.h Wed May 22 16:13:46 2019 +0200 @@ -20,15 +20,15 @@ #pragma once -#include -#include +#include "../../Scene2DViewport/PointerTypes.h" +#include "../../Scene2D/ScenePoint2D.h" namespace OrthancStone { namespace Internals { // During a mouse event that modifies the view of a scene, keeps - // one point (the pivot) at the same position on the canvas + // one point (the pivot) at a fixed position on the canvas class FixedPointAligner : public boost::noncopyable { private: diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2D/PanSceneTracker.cpp --- a/Framework/Scene2D/PanSceneTracker.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2D/PanSceneTracker.cpp Wed May 22 16:13:46 2019 +0200 @@ -20,7 +20,7 @@ #include "PanSceneTracker.h" -#include +#include "../Scene2DViewport/ViewportController.h" namespace OrthancStone { diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2D/PanSceneTracker.h --- a/Framework/Scene2D/PanSceneTracker.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2D/PanSceneTracker.h Wed May 22 16:13:46 2019 +0200 @@ -25,8 +25,6 @@ namespace OrthancStone { - class ViewportController; - class PanSceneTracker : public OneGesturePointerTracker { public: diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2D/RotateSceneTracker.cpp --- a/Framework/Scene2D/RotateSceneTracker.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2D/RotateSceneTracker.cpp Wed May 22 16:13:46 2019 +0200 @@ -19,7 +19,7 @@ **/ #include "RotateSceneTracker.h" -#include +#include "../Scene2DViewport/ViewportController.h" namespace OrthancStone { diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2D/RotateSceneTracker.h --- a/Framework/Scene2D/RotateSceneTracker.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2D/RotateSceneTracker.h Wed May 22 16:13:46 2019 +0200 @@ -26,8 +26,6 @@ namespace OrthancStone { - class ViewportController; - class RotateSceneTracker : public OneGesturePointerTracker { public: diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2D/Scene2D.h --- a/Framework/Scene2D/Scene2D.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2D/Scene2D.h Wed May 22 16:13:46 2019 +0200 @@ -23,8 +23,8 @@ #include "ISceneLayer.h" #include "../Toolbox/AffineTransform2D.h" -#include -#include +#include "../Messages/IObservable.h" +#include "../Messages/IMessage.h" #include diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2D/ZoomSceneTracker.cpp --- a/Framework/Scene2D/ZoomSceneTracker.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2D/ZoomSceneTracker.cpp Wed May 22 16:13:46 2019 +0200 @@ -20,7 +20,7 @@ #include "ZoomSceneTracker.h" -#include +#include "../Scene2DViewport/ViewportController.h" namespace OrthancStone { diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2D/ZoomSceneTracker.h --- a/Framework/Scene2D/ZoomSceneTracker.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2D/ZoomSceneTracker.h Wed May 22 16:13:46 2019 +0200 @@ -27,8 +27,6 @@ namespace OrthancStone { - class Scene2D; - class ZoomSceneTracker : public OneGesturePointerTracker { public: diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2DViewport/AngleMeasureTool.h --- a/Framework/Scene2DViewport/AngleMeasureTool.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2DViewport/AngleMeasureTool.h Wed May 22 16:13:46 2019 +0200 @@ -22,13 +22,12 @@ #include "MeasureTools.h" +#include "../Scene2DViewport/LayerHolder.h" #include "../Scene2D/Scene2D.h" #include "../Scene2D/ScenePoint2D.h" #include "../Scene2D/PolylineSceneLayer.h" #include "../Scene2D/TextSceneLayer.h" -#include "../Scene2DViewport/LayerHolder.h" - #include #include diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2DViewport/CreateAngleMeasureTracker.cpp --- a/Framework/Scene2DViewport/CreateAngleMeasureTracker.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2DViewport/CreateAngleMeasureTracker.cpp Wed May 22 16:13:46 2019 +0200 @@ -21,8 +21,6 @@ #include "CreateAngleMeasureTracker.h" #include -using namespace Orthanc; - namespace OrthancStone { CreateAngleMeasureTracker::CreateAngleMeasureTracker( @@ -49,7 +47,7 @@ if (!alive_) { - throw OrthancException(ErrorCode_InternalError, + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Internal error: wrong state in CreateAngleMeasureTracker::" "PointerMove: active_ == false"); } @@ -66,7 +64,7 @@ GetCommand()->SetSide2End(scenePos); break; default: - throw OrthancException(ErrorCode_InternalError, + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong state in CreateAngleMeasureTracker::" "PointerMove: state_ invalid"); } @@ -88,12 +86,12 @@ state_ = CreatingSide2; break; case CreatingSide2: - throw OrthancException(ErrorCode_InternalError, + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong state in CreateAngleMeasureTracker::" "PointerUp: state_ == CreatingSide2 ; this should not happen"); break; default: - throw OrthancException(ErrorCode_InternalError, + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong state in CreateAngleMeasureTracker::" "PointerMove: state_ invalid"); } @@ -104,7 +102,7 @@ switch (state_) { case CreatingSide1: - throw OrthancException(ErrorCode_InternalError, + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong state in CreateAngleMeasureTracker::" "PointerDown: state_ == CreatingSide1 ; this should not happen"); break; @@ -113,7 +111,7 @@ alive_ = false; break; default: - throw OrthancException(ErrorCode_InternalError, + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong state in CreateAngleMeasureTracker::" "PointerMove: state_ invalid"); } diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2DViewport/CreateLineMeasureTracker.cpp --- a/Framework/Scene2DViewport/CreateLineMeasureTracker.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2DViewport/CreateLineMeasureTracker.cpp Wed May 22 16:13:46 2019 +0200 @@ -21,8 +21,6 @@ #include "CreateLineMeasureTracker.h" #include -using namespace Orthanc; - namespace OrthancStone { CreateLineMeasureTracker::CreateLineMeasureTracker( @@ -49,7 +47,7 @@ if (!alive_) { - throw OrthancException(ErrorCode_InternalError, + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Internal error: wrong state in CreateLineMeasureTracker::" "PointerMove: active_ == false"); } diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2DViewport/CreateSimpleTrackerAdapter.cpp --- a/Framework/Scene2DViewport/CreateSimpleTrackerAdapter.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2DViewport/CreateSimpleTrackerAdapter.cpp Wed May 22 16:13:46 2019 +0200 @@ -19,7 +19,7 @@ **/ #include "IFlexiblePointerTracker.h" -#include +#include "../Scene2D/IPointerTracker.h" namespace OrthancStone diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2DViewport/IFlexiblePointerTracker.h --- a/Framework/Scene2DViewport/IFlexiblePointerTracker.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2DViewport/IFlexiblePointerTracker.h Wed May 22 16:13:46 2019 +0200 @@ -23,7 +23,7 @@ #include "PointerTypes.h" -#include +#include "../Scene2D/PointerEvent.h" namespace OrthancStone { diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2DViewport/LineMeasureTool.h --- a/Framework/Scene2DViewport/LineMeasureTool.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2DViewport/LineMeasureTool.h Wed May 22 16:13:46 2019 +0200 @@ -20,13 +20,12 @@ #pragma once +#include "../Scene2D/PolylineSceneLayer.h" +#include "../Scene2D/Scene2D.h" +#include "../Scene2D/ScenePoint2D.h" +#include "../Scene2D/TextSceneLayer.h" #include "MeasureTools.h" -#include -#include -#include -#include - #include #include diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2DViewport/MeasureCommands.h --- a/Framework/Scene2DViewport/MeasureCommands.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2DViewport/MeasureCommands.h Wed May 22 16:13:46 2019 +0200 @@ -19,9 +19,7 @@ **/ #pragma once -#include -#include -#include +#include "../Scene2D/Scene2D.h" // to be moved into Stone #include "PointerTypes.h" @@ -29,6 +27,9 @@ #include "LineMeasureTool.h" #include "AngleMeasureTool.h" +#include +#include + namespace OrthancStone { class TrackerCommand : public boost::noncopyable diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2DViewport/MeasureTools.cpp --- a/Framework/Scene2DViewport/MeasureTools.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2DViewport/MeasureTools.cpp Wed May 22 16:13:46 2019 +0200 @@ -26,8 +26,6 @@ #include -using namespace Orthanc; - namespace OrthancStone { @@ -58,7 +56,7 @@ { ViewportControllerPtr controller = controllerW_.lock(); if (!controller) - throw OrthancException(ErrorCode_InternalError, + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Using dead ViewportController object!"); return controller; } diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2DViewport/MeasureTools.h --- a/Framework/Scene2DViewport/MeasureTools.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2DViewport/MeasureTools.h Wed May 22 16:13:46 2019 +0200 @@ -20,13 +20,12 @@ #pragma once -#include -#include - -#include -#include -#include -#include +#include "../Scene2D/PolylineSceneLayer.h" +#include "../Scene2D/Scene2D.h" +#include "../Scene2D/ScenePoint2D.h" +#include "../Scene2D/TextSceneLayer.h" +#include "../Scene2DViewport/PointerTypes.h" +#include "../Scene2DViewport/ViewportController.h" #include #include diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2DViewport/MeasureToolsToolbox.cpp diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2DViewport/MeasureToolsToolbox.h --- a/Framework/Scene2DViewport/MeasureToolsToolbox.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2DViewport/MeasureToolsToolbox.h Wed May 22 16:13:46 2019 +0200 @@ -18,8 +18,9 @@ * along with this program. If not, see . **/ +#include "PointerTypes.h" #include "../Scene2D/PolylineSceneLayer.h" -#include "PointerTypes.h" +#include "../Scene2D/Scene2D.h" namespace OrthancStone { diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2DViewport/MeasureTrackers.cpp --- a/Framework/Scene2DViewport/MeasureTrackers.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2DViewport/MeasureTrackers.cpp Wed May 22 16:13:46 2019 +0200 @@ -21,8 +21,6 @@ #include "MeasureTrackers.h" #include -using namespace Orthanc; - namespace OrthancStone { diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2DViewport/MeasureTrackers.h --- a/Framework/Scene2DViewport/MeasureTrackers.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2DViewport/MeasureTrackers.h Wed May 22 16:13:46 2019 +0200 @@ -21,8 +21,8 @@ #pragma once #include "IFlexiblePointerTracker.h" -#include "../../Framework/Scene2D/Scene2D.h" -#include "../../Framework/Scene2D/PointerEvent.h" +#include "../Scene2D/Scene2D.h" +#include "../Scene2D/PointerEvent.h" #include "MeasureTools.h" #include "MeasureCommands.h" diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2DViewport/OneGesturePointerTracker.cpp --- a/Framework/Scene2DViewport/OneGesturePointerTracker.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2DViewport/OneGesturePointerTracker.cpp Wed May 22 16:13:46 2019 +0200 @@ -23,9 +23,7 @@ #include -#include - -using namespace Orthanc; +#include "../StoneException.h" namespace OrthancStone { diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2DViewport/ViewportController.cpp --- a/Framework/Scene2DViewport/ViewportController.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2DViewport/ViewportController.cpp Wed May 22 16:13:46 2019 +0200 @@ -21,12 +21,10 @@ #include "ViewportController.h" #include "MeasureCommands.h" -#include +#include "../StoneException.h" #include -using namespace Orthanc; - namespace OrthancStone { ViewportController::ViewportController(MessageBroker& broker) diff -r a386bbc955dc -r 92c400a09f1b Framework/Scene2DViewport/ViewportController.h --- a/Framework/Scene2DViewport/ViewportController.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Scene2DViewport/ViewportController.h Wed May 22 16:13:46 2019 +0200 @@ -22,9 +22,9 @@ #include "PointerTypes.h" -#include -#include -#include +#include "../Scene2D/Scene2D.h" +#include "../Scene2D/PointerEvent.h" +#include "../Scene2DViewport/IFlexiblePointerTracker.h" #include diff -r a386bbc955dc -r 92c400a09f1b Framework/SmartLoader.cpp --- a/Framework/SmartLoader.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,291 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "SmartLoader.h" -#include "Layers/DicomSeriesVolumeSlicer.h" -#include "Messages/MessageForwarder.h" -#include "Core/Images/Image.h" -#include "Framework/Widgets/SliceViewerWidget.h" -#include "Framework/StoneException.h" -#include "Framework/Layers/FrameRenderer.h" -#include "Core/Logging.h" - -namespace OrthancStone -{ - enum CachedSliceStatus - { - CachedSliceStatus_ScheduledToLoad, - CachedSliceStatus_GeometryLoaded, - CachedSliceStatus_ImageLoaded - }; - - class SmartLoader::CachedSlice : public IVolumeSlicer - { - public: - class RendererFactory : public LayerReadyMessage::IRendererFactory - { - private: - const CachedSlice& that_; - - public: - RendererFactory(const CachedSlice& that) : - that_(that) - { - } - - virtual ILayerRenderer* CreateRenderer() const - { - bool isFull = (that_.effectiveQuality_ == SliceImageQuality_FullPng || - that_.effectiveQuality_ == SliceImageQuality_FullPam); - - return FrameRenderer::CreateRenderer(*that_.image_, *that_.slice_, isFull); - } - }; - - unsigned int sliceIndex_; - std::auto_ptr slice_; - boost::shared_ptr image_; - SliceImageQuality effectiveQuality_; - CachedSliceStatus status_; - - public: - CachedSlice(MessageBroker& broker) : - IVolumeSlicer(broker) - { - } - - virtual ~CachedSlice() - { - } - - virtual bool GetExtent(std::vector& points, - const CoordinateSystem3D& viewportSlice) - { - // TODO: viewportSlice is not used !!!! - slice_->GetExtent(points); - return true; - } - - virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice) - { - // TODO: viewportSlice is not used !!!! - - // it has already been loaded -> trigger the "layer ready" message immediately otherwise, do nothing now. The LayerReady will be triggered - // once the VolumeSlicer is ready - if (status_ == CachedSliceStatus_ImageLoaded) - { - LOG(WARNING) << "ScheduleLayerCreation for CachedSlice (image is loaded): " << slice_->GetOrthancInstanceId(); - - RendererFactory factory(*this); - BroadcastMessage(IVolumeSlicer::LayerReadyMessage(*this, factory, slice_->GetGeometry())); - } - else - { - LOG(WARNING) << "ScheduleLayerCreation for CachedSlice (image is not loaded yet): " << slice_->GetOrthancInstanceId(); - } - } - - CachedSlice* Clone() const - { - CachedSlice* output = new CachedSlice(GetBroker()); - output->sliceIndex_ = sliceIndex_; - output->slice_.reset(slice_->Clone()); - output->image_ = image_; - output->effectiveQuality_ = effectiveQuality_; - output->status_ = status_; - - return output; - } - - }; - - - SmartLoader::SmartLoader(MessageBroker& broker, - OrthancApiClient& orthancApiClient) : - IObservable(broker), - IObserver(broker), - imageQuality_(SliceImageQuality_FullPam), - orthancApiClient_(orthancApiClient) - { - } - - void SmartLoader::SetFrameInWidget(SliceViewerWidget& sliceViewer, - size_t layerIndex, - const std::string& instanceId, - unsigned int frame) - { - // TODO: check if this frame has already been loaded or is already being loaded. - // - if already loaded: create a "clone" that will emit the GeometryReady/ImageReady messages "immediately" - // (it can not be immediate because Observers needs to register first and this is done after this method returns) - // - if currently loading, we need to return an object that will observe the existing VolumeSlicer and forward - // the messages to its observables - // in both cases, we must be carefull about objects lifecycle !!! - - std::auto_ptr layerSource; - std::string sliceKeyId = instanceId + ":" + boost::lexical_cast(frame); - SmartLoader::CachedSlice* cachedSlice = NULL; - - if (cachedSlices_.find(sliceKeyId) != cachedSlices_.end()) // && cachedSlices_[sliceKeyId]->status_ == CachedSliceStatus_Loaded) - { - layerSource.reset(cachedSlices_[sliceKeyId]->Clone()); - cachedSlice = dynamic_cast(layerSource.get()); - } - else - { - layerSource.reset(new DicomSeriesVolumeSlicer(IObserver::GetBroker(), orthancApiClient_)); - dynamic_cast(layerSource.get())->SetImageQuality(imageQuality_); - layerSource->RegisterObserverCallback(new Callable(*this, &SmartLoader::OnLayerGeometryReady)); - layerSource->RegisterObserverCallback(new Callable(*this, &SmartLoader::OnFrameReady)); - layerSource->RegisterObserverCallback(new Callable(*this, &SmartLoader::OnLayerReady)); - dynamic_cast(layerSource.get())->LoadFrame(instanceId, frame); - } - - // make sure that the widget registers the events before we trigger them - if (sliceViewer.GetLayerCount() == layerIndex) - { - sliceViewer.AddLayer(layerSource.release()); - } - else if (sliceViewer.GetLayerCount() > layerIndex) - { - sliceViewer.ReplaceLayer(layerIndex, layerSource.release()); - } - else - { - throw StoneException(ErrorCode_CanOnlyAddOneLayerAtATime); - } - - if (cachedSlice != NULL) - { - BroadcastMessage(IVolumeSlicer::GeometryReadyMessage(*cachedSlice)); - } - - } - - void SmartLoader::PreloadSlice(const std::string instanceId, - unsigned int frame) - { - // TODO: reactivate -> need to be able to ScheduleLayerLoading in IVolumeSlicer without calling ScheduleLayerCreation - return; - // TODO: check if it is already in the cache - - - - // create the slice in the cache with "empty" data - boost::shared_ptr cachedSlice(new CachedSlice(IObserver::GetBroker())); - cachedSlice->slice_.reset(new Deprecated::Slice(instanceId, frame)); - cachedSlice->status_ = CachedSliceStatus_ScheduledToLoad; - std::string sliceKeyId = instanceId + ":" + boost::lexical_cast(frame); - - LOG(WARNING) << "Will preload: " << sliceKeyId; - - cachedSlices_[sliceKeyId] = boost::shared_ptr(cachedSlice); - - std::auto_ptr layerSource(new DicomSeriesVolumeSlicer(IObserver::GetBroker(), orthancApiClient_)); - - dynamic_cast(layerSource.get())->SetImageQuality(imageQuality_); - layerSource->RegisterObserverCallback(new Callable(*this, &SmartLoader::OnLayerGeometryReady)); - layerSource->RegisterObserverCallback(new Callable(*this, &SmartLoader::OnFrameReady)); - layerSource->RegisterObserverCallback(new Callable(*this, &SmartLoader::OnLayerReady)); - dynamic_cast(layerSource.get())->LoadFrame(instanceId, frame); - - // keep a ref to the VolumeSlicer until the slice is fully loaded and saved to cache - preloadingInstances_[sliceKeyId] = boost::shared_ptr(layerSource.release()); - } - - -// void PreloadStudy(const std::string studyId) -// { -// /* TODO */ -// } - -// void PreloadSeries(const std::string seriesId) -// { -// /* TODO */ -// } - - - void SmartLoader::OnLayerGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message) - { - const DicomSeriesVolumeSlicer& source = - dynamic_cast(message.GetOrigin()); - - // save/replace the slice in cache - const Deprecated::Slice& slice = source.GetSlice(0); // TODO handle GetSliceCount() - std::string sliceKeyId = (slice.GetOrthancInstanceId() + ":" + - boost::lexical_cast(slice.GetFrame())); - - LOG(WARNING) << "Geometry ready: " << sliceKeyId; - - boost::shared_ptr cachedSlice(new CachedSlice(IObserver::GetBroker())); - cachedSlice->slice_.reset(slice.Clone()); - cachedSlice->effectiveQuality_ = source.GetImageQuality(); - cachedSlice->status_ = CachedSliceStatus_GeometryLoaded; - - cachedSlices_[sliceKeyId] = boost::shared_ptr(cachedSlice); - - // re-emit original Layer message to observers - BroadcastMessage(message); - } - - - void SmartLoader::OnFrameReady(const DicomSeriesVolumeSlicer::FrameReadyMessage& message) - { - // save/replace the slice in cache - const Deprecated::Slice& slice = message.GetSlice(); - std::string sliceKeyId = (slice.GetOrthancInstanceId() + ":" + - boost::lexical_cast(slice.GetFrame())); - - LOG(WARNING) << "Image ready: " << sliceKeyId; - - boost::shared_ptr cachedSlice(new CachedSlice(IObserver::GetBroker())); - cachedSlice->image_.reset(Orthanc::Image::Clone(message.GetFrame())); - cachedSlice->effectiveQuality_ = message.GetImageQuality(); - cachedSlice->slice_.reset(message.GetSlice().Clone()); - cachedSlice->status_ = CachedSliceStatus_ImageLoaded; - - cachedSlices_[sliceKeyId] = cachedSlice; - - // re-emit original Layer message to observers - BroadcastMessage(message); - } - - - void SmartLoader::OnLayerReady(const IVolumeSlicer::LayerReadyMessage& message) - { - const DicomSeriesVolumeSlicer& source = - dynamic_cast(message.GetOrigin()); - - const Deprecated::Slice& slice = source.GetSlice(0); // TODO handle GetSliceCount() ? - std::string sliceKeyId = (slice.GetOrthancInstanceId() + ":" + - boost::lexical_cast(slice.GetFrame())); - - LOG(WARNING) << "Layer ready: " << sliceKeyId; - - // remove the slice from the preloading slices now that it has been fully loaded and it is referenced in the cache - if (preloadingInstances_.find(sliceKeyId) != preloadingInstances_.end()) - { - preloadingInstances_.erase(sliceKeyId); - } - - // re-emit original Layer message to observers - BroadcastMessage(message); - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/SmartLoader.h --- a/Framework/SmartLoader.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once -#include - -#include "Layers/DicomSeriesVolumeSlicer.h" -#include "Messages/IObservable.h" -#include "Toolbox/OrthancApiClient.h" - -namespace OrthancStone -{ - class SliceViewerWidget; - - class SmartLoader : public IObservable, public IObserver - { - class CachedSlice; - - protected: - typedef std::map > CachedSlices; - CachedSlices cachedSlices_; - - typedef std::map > PreloadingInstances; - PreloadingInstances preloadingInstances_; - - SliceImageQuality imageQuality_; - OrthancApiClient& orthancApiClient_; - - public: - SmartLoader(MessageBroker& broker, OrthancApiClient& orthancApiClient); // TODO: add maxPreloadStorageSizeInBytes - -// void PreloadStudy(const std::string studyId); -// void PreloadSeries(const std::string seriesId); - void PreloadSlice(const std::string instanceId, unsigned int frame); - - void SetImageQuality(SliceImageQuality imageQuality) { imageQuality_ = imageQuality; } - - void SetFrameInWidget(SliceViewerWidget& sliceViewer, size_t layerIndex, const std::string& instanceId, unsigned int frame); - - void GetFirstInstanceIdForSeries(std::string& output, const std::string& seriesId); - - private: - void OnLayerGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message); - void OnFrameReady(const DicomSeriesVolumeSlicer::FrameReadyMessage& message); - void OnLayerReady(const IVolumeSlicer::LayerReadyMessage& message); - - }; - -} diff -r a386bbc955dc -r 92c400a09f1b Framework/StoneEnumerations.h --- a/Framework/StoneEnumerations.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/StoneEnumerations.h Wed May 22 16:13:46 2019 +0200 @@ -23,6 +23,22 @@ #include + +namespace Deprecated +{ + enum SliceImageQuality + { + SliceImageQuality_FullPng, // smaller to transmit but longer to generate on Orthanc side (better choice when on low bandwidth) + SliceImageQuality_FullPam, // bigger to transmit but faster to generate on Orthanc side (better choice when on localhost or LAN) + SliceImageQuality_Jpeg50, + SliceImageQuality_Jpeg90, + SliceImageQuality_Jpeg95, + + SliceImageQuality_InternalRaw // downloads the raw pixels data as they are stored in the DICOM file (internal use only) + }; +} + + namespace OrthancStone { enum SliceOffsetMode @@ -85,17 +101,6 @@ KeyboardKeys_Down = 40 }; - enum SliceImageQuality - { - SliceImageQuality_FullPng, // smaller to transmit but longer to generate on Orthanc side (better choice when on low bandwidth) - SliceImageQuality_FullPam, // bigger to transmit but faster to generate on Orthanc side (better choice when on localhost or LAN) - SliceImageQuality_Jpeg50, - SliceImageQuality_Jpeg90, - SliceImageQuality_Jpeg95, - - SliceImageQuality_InternalRaw // downloads the raw pixels data as they are stored in the DICOM file (internal use only) - }; - enum SopClassUid { SopClassUid_Other, @@ -115,23 +120,6 @@ BitmapAnchor_TopRight }; - enum ControlPointType - { - ControlPoint_TopLeftCorner = 0, - ControlPoint_TopRightCorner = 1, - ControlPoint_BottomRightCorner = 2, - ControlPoint_BottomLeftCorner = 3 - }; - - enum PhotometricDisplayMode - { - PhotometricDisplayMode_Default, - - PhotometricDisplayMode_Monochrome1, - PhotometricDisplayMode_Monochrome2 - }; - - SopClassUid StringToSopClassUid(const std::string& source); void ComputeWindowing(float& targetCenter, diff -r a386bbc955dc -r 92c400a09f1b Framework/StoneException.h --- a/Framework/StoneException.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/StoneException.h Wed May 22 16:13:46 2019 +0200 @@ -120,7 +120,7 @@ std::stringstream sst; \ sst << "Assertion failed. Condition = \"" #cond "\" Message = \"" << streamChainMessage << "\""; \ std::string sstr = sst.str(); \ - throw OrthancException(ErrorCode_InternalError,sstr.c_str()); \ + throw ::Orthanc::OrthancException(::Orthanc::ErrorCode_InternalError,sstr.c_str()); \ } else (void)0 #define ORTHANC_ASSERT1(cond) \ diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/BaseWebService.cpp --- a/Framework/Toolbox/BaseWebService.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,143 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2018 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "BaseWebService.h" - -#include -#include "Framework/Messages/IObservable.h" -#include "Platforms/Generic/IOracleCommand.h" -#include - -namespace OrthancStone -{ - - - class BaseWebService::BaseWebServicePayload : public Orthanc::IDynamicObject - { - private: - std::auto_ptr< MessageHandler > userSuccessHandler_; - std::auto_ptr< MessageHandler > userFailureHandler_; - std::auto_ptr< Orthanc::IDynamicObject> userPayload_; - - public: - BaseWebServicePayload(MessageHandler* userSuccessHandler, - MessageHandler* userFailureHandler, - Orthanc::IDynamicObject* userPayload) : - userSuccessHandler_(userSuccessHandler), - userFailureHandler_(userFailureHandler), - userPayload_(userPayload) - { - } - - void HandleSuccess(const IWebService::HttpRequestSuccessMessage& message) const - { - if (userSuccessHandler_.get() != NULL) - { - // recreate a success message with the user payload - IWebService::HttpRequestSuccessMessage successMessage(message.GetUri(), - message.GetAnswer(), - message.GetAnswerSize(), - message.GetAnswerHttpHeaders(), - userPayload_.get()); - userSuccessHandler_->Apply(successMessage); - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - - void HandleFailure(const IWebService::HttpRequestErrorMessage& message) const - { - if (userFailureHandler_.get() != NULL) - { - // recreate a failure message with the user payload - IWebService::HttpRequestErrorMessage failureMessage(message.GetUri(), - userPayload_.get()); - - userFailureHandler_->Apply(failureMessage); - } - } - - }; - - - void BaseWebService::GetAsync(const std::string& uri, - const HttpHeaders& headers, - Orthanc::IDynamicObject* payload /* takes ownership */, - MessageHandler* successCallback, - MessageHandler* failureCallback, - unsigned int timeoutInSeconds) - { - if (cache_.find(uri) == cache_.end()) - { - GetAsyncInternal(uri, headers, - new BaseWebService::BaseWebServicePayload(successCallback, failureCallback, payload), // ownership is transfered - new Callable - (*this, &BaseWebService::CacheAndNotifyHttpSuccess), - new Callable - (*this, &BaseWebService::NotifyHttpError), - timeoutInSeconds); - } - else - { - // create a command and "post" it to the Oracle so it is executed and commited "later" - NotifyHttpSuccessLater(cache_[uri], payload, successCallback); - } - - } - - - - void BaseWebService::NotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message) - { - if (message.HasPayload()) - { - dynamic_cast(message.GetPayload()).HandleSuccess(message); - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - - void BaseWebService::CacheAndNotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message) - { - cache_[message.GetUri()] = boost::shared_ptr(new CachedHttpRequestSuccessMessage(message)); - NotifyHttpSuccess(message); - } - - void BaseWebService::NotifyHttpError(const IWebService::HttpRequestErrorMessage& message) - { - if (message.HasPayload()) - { - dynamic_cast(message.GetPayload()).HandleFailure(message); - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - - - - -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/BaseWebService.h --- a/Framework/Toolbox/BaseWebService.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,131 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2018 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "IWebService.h" - -#include -#include - -namespace OrthancStone -{ - // This is an intermediate of IWebService that implements some caching on - // the HTTP GET requests - class BaseWebService : public IWebService, public IObserver - { - public: - class CachedHttpRequestSuccessMessage - { - protected: - std::string uri_; - void* answer_; - size_t answerSize_; - IWebService::HttpHeaders answerHeaders_; - - public: - CachedHttpRequestSuccessMessage(const IWebService::HttpRequestSuccessMessage& message) : - uri_(message.GetUri()), - answerSize_(message.GetAnswerSize()), - answerHeaders_(message.GetAnswerHttpHeaders()) - { - answer_ = malloc(answerSize_); - memcpy(answer_, message.GetAnswer(), answerSize_); - } - - ~CachedHttpRequestSuccessMessage() - { - free(answer_); - } - - const std::string& GetUri() const - { - return uri_; - } - - const void* GetAnswer() const - { - return answer_; - } - - size_t GetAnswerSize() const - { - return answerSize_; - } - - const IWebService::HttpHeaders& GetAnswerHttpHeaders() const - { - return answerHeaders_; - } - - }; - protected: - class BaseWebServicePayload; - - bool cacheEnabled_; - std::map > cache_; // TODO: this is currently an infinite cache ! - - public: - - BaseWebService(MessageBroker& broker) : - IWebService(broker), - IObserver(broker), - cacheEnabled_(true) - { - } - - virtual ~BaseWebService() - { - } - - virtual void EnableCache(bool enable) - { - cacheEnabled_ = enable; - } - - virtual void GetAsync(const std::string& uri, - const HttpHeaders& headers, - Orthanc::IDynamicObject* payload /* takes ownership */, - MessageHandler* successCallback, - MessageHandler* failureCallback = NULL, - unsigned int timeoutInSeconds = 60); - - protected: - virtual void GetAsyncInternal(const std::string& uri, - const HttpHeaders& headers, - Orthanc::IDynamicObject* payload /* takes ownership */, - MessageHandler* successCallback, - MessageHandler* failureCallback = NULL, - unsigned int timeoutInSeconds = 60) = 0; - - virtual void NotifyHttpSuccessLater(boost::shared_ptr cachedHttpMessage, - Orthanc::IDynamicObject* payload, // takes ownership - MessageHandler* successCallback) = 0; - - private: - void NotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message); - - void NotifyHttpError(const IWebService::HttpRequestErrorMessage& message); - - void CacheAndNotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message); - - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/DicomFrameConverter.cpp --- a/Framework/Toolbox/DicomFrameConverter.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,282 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "DicomFrameConverter.h" - -#include "LinearAlgebra.h" - -#include -#include -#include -#include - -namespace Deprecated -{ - static const Orthanc::DicomTag IMAGE_TAGS[] = - { - Orthanc::DICOM_TAG_BITS_STORED, - Orthanc::DICOM_TAG_DOSE_GRID_SCALING, - Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION, - Orthanc::DICOM_TAG_PIXEL_REPRESENTATION, - Orthanc::DICOM_TAG_RESCALE_INTERCEPT, - Orthanc::DICOM_TAG_RESCALE_SLOPE, - Orthanc::DICOM_TAG_WINDOW_CENTER, - Orthanc::DICOM_TAG_WINDOW_WIDTH - }; - - - void DicomFrameConverter::SetDefaultParameters() - { - isSigned_ = true; - isColor_ = false; - hasRescale_ = false; - rescaleIntercept_ = 0; - rescaleSlope_ = 1; - hasDefaultWindow_ = false; - defaultWindowCenter_ = 128; - defaultWindowWidth_ = 256; - expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16; - } - - - void DicomFrameConverter::ReadParameters(const Orthanc::DicomMap& dicom) - { - SetDefaultParameters(); - - OrthancStone::Vector c, w; - if (OrthancStone::LinearAlgebra::ParseVector(c, dicom, Orthanc::DICOM_TAG_WINDOW_CENTER) && - OrthancStone::LinearAlgebra::ParseVector(w, dicom, Orthanc::DICOM_TAG_WINDOW_WIDTH) && - c.size() > 0 && - w.size() > 0) - { - hasDefaultWindow_ = true; - defaultWindowCenter_ = static_cast(c[0]); - defaultWindowWidth_ = static_cast(w[0]); - } - - int32_t tmp; - if (!dicom.ParseInteger32(tmp, Orthanc::DICOM_TAG_PIXEL_REPRESENTATION)) - { - // Type 1 tag, must be present - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - isSigned_ = (tmp == 1); - - double doseGridScaling; - bool isRTDose = false; - - if (dicom.ParseDouble(rescaleIntercept_, Orthanc::DICOM_TAG_RESCALE_INTERCEPT) && - dicom.ParseDouble(rescaleSlope_, Orthanc::DICOM_TAG_RESCALE_SLOPE)) - { - hasRescale_ = true; - } - else if (dicom.ParseDouble(doseGridScaling, Orthanc::DICOM_TAG_DOSE_GRID_SCALING)) - { - // This is for RT-DOSE - hasRescale_ = true; - isRTDose = true; - rescaleIntercept_ = 0; - rescaleSlope_ = doseGridScaling; - - if (!dicom.ParseInteger32(tmp, Orthanc::DICOM_TAG_BITS_STORED)) - { - // Type 1 tag, must be present - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - switch (tmp) - { - case 16: - expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16; - break; - - case 32: - expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale32; - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - } - - std::string photometric; - if (dicom.CopyToString(photometric, Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION, false)) - { - photometric = Orthanc::Toolbox::StripSpaces(photometric); - } - else - { - // Type 1 tag, must be present - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - photometric_ = Orthanc::StringToPhotometricInterpretation(photometric.c_str()); - - isColor_ = (photometric != "MONOCHROME1" && - photometric != "MONOCHROME2"); - - // TODO Add more checks, e.g. on the number of bytes per value - // (cf. DicomImageInformation.h in Orthanc) - - if (!isRTDose) - { - if (isColor_) - { - expectedPixelFormat_ = Orthanc::PixelFormat_RGB24; - } - else if (isSigned_) - { - expectedPixelFormat_ = Orthanc::PixelFormat_SignedGrayscale16; - } - else - { - expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16; - } - } - } - - - void DicomFrameConverter::ReadParameters(const OrthancPlugins::IDicomDataset& dicom) - { - Orthanc::DicomMap converted; - - for (size_t i = 0; i < sizeof(IMAGE_TAGS) / sizeof(Orthanc::DicomTag); i++) - { - OrthancPlugins::DicomTag tag(IMAGE_TAGS[i].GetGroup(), IMAGE_TAGS[i].GetElement()); - - std::string value; - if (dicom.GetStringValue(value, tag)) - { - converted.SetValue(IMAGE_TAGS[i], value, false); - } - } - - ReadParameters(converted); - } - - - void DicomFrameConverter::ConvertFrameInplace(std::auto_ptr& source) const - { - assert(sizeof(float) == 4); - - if (source.get() == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - if (source->GetFormat() == GetExpectedPixelFormat() && - source->GetFormat() == Orthanc::PixelFormat_RGB24) - { - // No conversion has to be done, check out (*) - return; - } - else - { - source.reset(ConvertFrame(*source)); - } - } - - - Orthanc::ImageAccessor* DicomFrameConverter::ConvertFrame(const Orthanc::ImageAccessor& source) const - { - assert(sizeof(float) == 4); - - Orthanc::PixelFormat sourceFormat = source.GetFormat(); - - if (sourceFormat != GetExpectedPixelFormat()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); - } - - if (sourceFormat == Orthanc::PixelFormat_RGB24) - { - // This is the case of a color image. No conversion has to be done (*) - std::auto_ptr converted(new Orthanc::Image(Orthanc::PixelFormat_RGB24, - source.GetWidth(), - source.GetHeight(), - false)); - Orthanc::ImageProcessing::Copy(*converted, source); - return converted.release(); - } - else - { - assert(sourceFormat == Orthanc::PixelFormat_Grayscale16 || - sourceFormat == Orthanc::PixelFormat_Grayscale32 || - sourceFormat == Orthanc::PixelFormat_SignedGrayscale16); - - // This is the case of a grayscale frame. Convert it to Float32. - std::auto_ptr converted(new Orthanc::Image(Orthanc::PixelFormat_Float32, - source.GetWidth(), - source.GetHeight(), - false)); - Orthanc::ImageProcessing::Convert(*converted, source); - - // Correct rescale slope/intercept if need be - ApplyRescale(*converted, sourceFormat != Orthanc::PixelFormat_Grayscale32); - - return converted.release(); - } - } - - - void DicomFrameConverter::ApplyRescale(Orthanc::ImageAccessor& image, - bool useDouble) const - { - if (image.GetFormat() != Orthanc::PixelFormat_Float32) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); - } - - if (hasRescale_) - { - for (unsigned int y = 0; y < image.GetHeight(); y++) - { - float* p = reinterpret_cast(image.GetRow(y)); - - if (useDouble) - { - // Slower, accurate implementation using double - for (unsigned int x = 0; x < image.GetWidth(); x++, p++) - { - double value = static_cast(*p); - *p = static_cast(value * rescaleSlope_ + rescaleIntercept_); - } - } - else - { - // Fast, approximate implementation using float - for (unsigned int x = 0; x < image.GetWidth(); x++, p++) - { - *p = (*p) * static_cast(rescaleSlope_) + static_cast(rescaleIntercept_); - } - } - } - } - } - - - double DicomFrameConverter::Apply(double x) const - { - return x * rescaleSlope_ + rescaleIntercept_; - } - -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/DicomFrameConverter.h --- a/Framework/Toolbox/DicomFrameConverter.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,169 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include -#include -#include - -#include - -namespace Deprecated -{ - /** - * This class is responsible for converting the pixel format of a - * DICOM frame coming from Orthanc, into a pixel format that is - * suitable for Stone, given the relevant DICOM tags: - * - Color frames will stay in the RGB24 format. - * - Grayscale frames will be converted to the Float32 format. - **/ - class DicomFrameConverter - { - private: - bool isSigned_; - bool isColor_; - bool hasRescale_; - double rescaleIntercept_; - double rescaleSlope_; - bool hasDefaultWindow_; - double defaultWindowCenter_; - double defaultWindowWidth_; - - Orthanc::PhotometricInterpretation photometric_; - Orthanc::PixelFormat expectedPixelFormat_; - - void SetDefaultParameters(); - - public: - DicomFrameConverter() - { - SetDefaultParameters(); - } - - ~DicomFrameConverter() - { - // TODO: check whether this dtor is called or not - // An MSVC warning explains that declaring an - // std::auto_ptr with a forward-declared type - // prevents its dtor from being called. Does not - // seem an issue here (only POD types inside), but - // definitely something to keep an eye on. - (void)0; - } - - // AM: this is required to serialize/deserialize it - DicomFrameConverter( - bool isSigned, - bool isColor, - bool hasRescale, - double rescaleIntercept, - double rescaleSlope, - bool hasDefaultWindow, - double defaultWindowCenter, - double defaultWindowWidth, - Orthanc::PhotometricInterpretation photometric, - Orthanc::PixelFormat expectedPixelFormat - ): - isSigned_(isSigned), - isColor_(isColor), - hasRescale_(hasRescale), - rescaleIntercept_(rescaleIntercept), - rescaleSlope_(rescaleSlope), - hasDefaultWindow_(hasDefaultWindow), - defaultWindowCenter_(defaultWindowCenter), - defaultWindowWidth_(defaultWindowWidth), - photometric_(photometric), - expectedPixelFormat_(expectedPixelFormat) - {} - - void GetParameters(bool& isSigned, - bool& isColor, - bool& hasRescale, - double& rescaleIntercept, - double& rescaleSlope, - bool& hasDefaultWindow, - double& defaultWindowCenter, - double& defaultWindowWidth, - Orthanc::PhotometricInterpretation& photometric, - Orthanc::PixelFormat& expectedPixelFormat) const - { - isSigned = isSigned_; - isColor = isColor_; - hasRescale = hasRescale_; - rescaleIntercept = rescaleIntercept_; - rescaleSlope = rescaleSlope_; - hasDefaultWindow = hasDefaultWindow_; - defaultWindowCenter = defaultWindowCenter_; - defaultWindowWidth = defaultWindowWidth_; - photometric = photometric_; - expectedPixelFormat = expectedPixelFormat_; - } - - Orthanc::PixelFormat GetExpectedPixelFormat() const - { - return expectedPixelFormat_; - } - - Orthanc::PhotometricInterpretation GetPhotometricInterpretation() const - { - return photometric_; - } - - void ReadParameters(const Orthanc::DicomMap& dicom); - - void ReadParameters(const OrthancPlugins::IDicomDataset& dicom); - - bool HasDefaultWindow() const - { - return hasDefaultWindow_; - } - - double GetDefaultWindowCenter() const - { - return defaultWindowCenter_; - } - - double GetDefaultWindowWidth() const - { - return defaultWindowWidth_; - } - - double GetRescaleIntercept() const - { - return rescaleIntercept_; - } - - double GetRescaleSlope() const - { - return rescaleSlope_; - } - - void ConvertFrameInplace(std::auto_ptr& source) const; - - Orthanc::ImageAccessor* ConvertFrame(const Orthanc::ImageAccessor& source) const; - - void ApplyRescale(Orthanc::ImageAccessor& image, - bool useDouble) const; - - double Apply(double x) const; - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/DicomInstanceParameters.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/DicomInstanceParameters.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,378 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "DicomInstanceParameters.h" + +#include "../Scene2D/ColorTextureSceneLayer.h" +#include "../Scene2D/FloatTextureSceneLayer.h" +#include "../Toolbox/GeometryToolbox.h" + +#include +#include +#include +#include +#include + + +namespace OrthancStone +{ + void DicomInstanceParameters::Data::ComputeDoseOffsets(const Orthanc::DicomMap& dicom) + { + // http://dicom.nema.org/medical/Dicom/2016a/output/chtml/part03/sect_C.8.8.3.2.html + + { + std::string increment; + + if (dicom.CopyToString(increment, Orthanc::DICOM_TAG_FRAME_INCREMENT_POINTER, false)) + { + Orthanc::Toolbox::ToUpperCase(increment); + if (increment != "3004,000C") // This is the "Grid Frame Offset Vector" tag + { + LOG(ERROR) << "RT-DOSE: Bad value for the \"FrameIncrementPointer\" tag"; + return; + } + } + } + + if (!LinearAlgebra::ParseVector(frameOffsets_, dicom, Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR) || + frameOffsets_.size() < imageInformation_.GetNumberOfFrames()) + { + LOG(ERROR) << "RT-DOSE: No information about the 3D location of some slice(s)"; + frameOffsets_.clear(); + } + else + { + if (frameOffsets_.size() >= 2) + { + thickness_ = frameOffsets_[1] - frameOffsets_[0]; + + if (thickness_ < 0) + { + thickness_ = -thickness_; + } + } + } + } + + + DicomInstanceParameters::Data::Data(const Orthanc::DicomMap& dicom) : + imageInformation_(dicom) + { + if (imageInformation_.GetNumberOfFrames() <= 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + if (!dicom.CopyToString(studyInstanceUid_, Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, false) || + !dicom.CopyToString(seriesInstanceUid_, Orthanc::DICOM_TAG_SERIES_INSTANCE_UID, false) || + !dicom.CopyToString(sopInstanceUid_, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + std::string s; + if (!dicom.CopyToString(s, Orthanc::DICOM_TAG_SOP_CLASS_UID, false)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + else + { + sopClassUid_ = StringToSopClassUid(s); + } + + if (!dicom.ParseDouble(thickness_, Orthanc::DICOM_TAG_SLICE_THICKNESS)) + { + thickness_ = 100.0 * std::numeric_limits::epsilon(); + } + + GeometryToolbox::GetPixelSpacing(pixelSpacingX_, pixelSpacingY_, dicom); + + std::string position, orientation; + if (dicom.CopyToString(position, Orthanc::DICOM_TAG_IMAGE_POSITION_PATIENT, false) && + dicom.CopyToString(orientation, Orthanc::DICOM_TAG_IMAGE_ORIENTATION_PATIENT, false)) + { + geometry_ = CoordinateSystem3D(position, orientation); + } + + if (sopClassUid_ == SopClassUid_RTDose) + { + ComputeDoseOffsets(dicom); + } + + isColor_ = (imageInformation_.GetPhotometricInterpretation() != Orthanc::PhotometricInterpretation_Monochrome1 && + imageInformation_.GetPhotometricInterpretation() != Orthanc::PhotometricInterpretation_Monochrome2); + + double doseGridScaling; + + if (dicom.ParseDouble(rescaleIntercept_, Orthanc::DICOM_TAG_RESCALE_INTERCEPT) && + dicom.ParseDouble(rescaleSlope_, Orthanc::DICOM_TAG_RESCALE_SLOPE)) + { + hasRescale_ = true; + } + else if (dicom.ParseDouble(doseGridScaling, Orthanc::DICOM_TAG_DOSE_GRID_SCALING)) + { + hasRescale_ = true; + rescaleIntercept_ = 0; + rescaleSlope_ = doseGridScaling; + } + else + { + hasRescale_ = false; + } + + Vector c, w; + if (LinearAlgebra::ParseVector(c, dicom, Orthanc::DICOM_TAG_WINDOW_CENTER) && + LinearAlgebra::ParseVector(w, dicom, Orthanc::DICOM_TAG_WINDOW_WIDTH) && + c.size() > 0 && + w.size() > 0) + { + hasDefaultWindowing_ = true; + defaultWindowingCenter_ = static_cast(c[0]); + defaultWindowingWidth_ = static_cast(w[0]); + } + else + { + hasDefaultWindowing_ = false; + } + + if (sopClassUid_ == SopClassUid_RTDose) + { + switch (imageInformation_.GetBitsStored()) + { + case 16: + expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16; + break; + + case 32: + expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale32; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } + else if (isColor_) + { + expectedPixelFormat_ = Orthanc::PixelFormat_RGB24; + } + else if (imageInformation_.IsSigned()) + { + expectedPixelFormat_ = Orthanc::PixelFormat_SignedGrayscale16; + } + else + { + expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16; + } + } + + + CoordinateSystem3D DicomInstanceParameters::Data::GetFrameGeometry(unsigned int frame) const + { + if (frame == 0) + { + return geometry_; + } + else if (frame >= imageInformation_.GetNumberOfFrames()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else if (sopClassUid_ == SopClassUid_RTDose) + { + if (frame >= frameOffsets_.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + return CoordinateSystem3D( + geometry_.GetOrigin() + frameOffsets_[frame] * geometry_.GetNormal(), + geometry_.GetAxisX(), + geometry_.GetAxisY()); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } + + + bool DicomInstanceParameters::Data::IsPlaneWithinSlice(unsigned int frame, + const CoordinateSystem3D& plane) const + { + if (frame >= imageInformation_.GetNumberOfFrames()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + CoordinateSystem3D tmp = geometry_; + + if (frame != 0) + { + tmp = GetFrameGeometry(frame); + } + + double distance; + + return (CoordinateSystem3D::GetDistance(distance, tmp, plane) && + distance <= thickness_ / 2.0); + } + + + void DicomInstanceParameters::Data::ApplyRescale(Orthanc::ImageAccessor& image, + bool useDouble) const + { + if (image.GetFormat() != Orthanc::PixelFormat_Float32) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + + if (hasRescale_) + { + const unsigned int width = image.GetWidth(); + const unsigned int height = image.GetHeight(); + + for (unsigned int y = 0; y < height; y++) + { + float* p = reinterpret_cast(image.GetRow(y)); + + if (useDouble) + { + // Slower, accurate implementation using double + for (unsigned int x = 0; x < width; x++, p++) + { + double value = static_cast(*p); + *p = static_cast(value * rescaleSlope_ + rescaleIntercept_); + } + } + else + { + // Fast, approximate implementation using float + for (unsigned int x = 0; x < width; x++, p++) + { + *p = (*p) * static_cast(rescaleSlope_) + static_cast(rescaleIntercept_); + } + } + } + } + } + + double DicomInstanceParameters::GetRescaleIntercept() const + { + if (data_.hasRescale_) + { + return data_.rescaleIntercept_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + + double DicomInstanceParameters::GetRescaleSlope() const + { + if (data_.hasRescale_) + { + return data_.rescaleSlope_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + + float DicomInstanceParameters::GetDefaultWindowingCenter() const + { + if (data_.hasDefaultWindowing_) + { + return data_.defaultWindowingCenter_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + + float DicomInstanceParameters::GetDefaultWindowingWidth() const + { + if (data_.hasDefaultWindowing_) + { + return data_.defaultWindowingWidth_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + + TextureBaseSceneLayer* DicomInstanceParameters::CreateTexture(const Orthanc::ImageAccessor& pixelData) const + { + assert(sizeof(float) == 4); + + Orthanc::PixelFormat sourceFormat = pixelData.GetFormat(); + + if (sourceFormat != GetExpectedPixelFormat()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + + if (sourceFormat == Orthanc::PixelFormat_RGB24) + { + // This is the case of a color image. No conversion has to be done. + return new ColorTextureSceneLayer(pixelData); + } + else + { + if (sourceFormat != Orthanc::PixelFormat_Grayscale16 && + sourceFormat != Orthanc::PixelFormat_Grayscale32 && + sourceFormat != Orthanc::PixelFormat_SignedGrayscale16) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + std::auto_ptr texture; + + { + // This is the case of a grayscale frame. Convert it to Float32. + std::auto_ptr converted(new Orthanc::Image(Orthanc::PixelFormat_Float32, + pixelData.GetWidth(), + pixelData.GetHeight(), + false)); + Orthanc::ImageProcessing::Convert(*converted, pixelData); + + // Correct rescale slope/intercept if need be + data_.ApplyRescale(*converted, (sourceFormat == Orthanc::PixelFormat_Grayscale32)); + + texture.reset(new FloatTextureSceneLayer(*converted)); + } + + if (data_.hasDefaultWindowing_) + { + texture->SetCustomWindowing(data_.defaultWindowingCenter_, + data_.defaultWindowingWidth_); + } + + return texture.release(); + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/DicomInstanceParameters.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/DicomInstanceParameters.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,185 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../StoneEnumerations.h" +#include "../Scene2D/TextureBaseSceneLayer.h" +#include "../Toolbox/CoordinateSystem3D.h" + +#include +#include + +namespace OrthancStone +{ + class DicomInstanceParameters : + public Orthanc::IDynamicObject /* to be used as a payload to SlicesSorter */ + { + // This class supersedes the deprecated "DicomFrameConverter" + + private: + struct Data // Struct to ease the copy constructor + { + std::string orthancInstanceId_; + std::string studyInstanceUid_; + std::string seriesInstanceUid_; + std::string sopInstanceUid_; + Orthanc::DicomImageInformation imageInformation_; + SopClassUid sopClassUid_; + double thickness_; + double pixelSpacingX_; + double pixelSpacingY_; + CoordinateSystem3D geometry_; + Vector frameOffsets_; + bool isColor_; + bool hasRescale_; + double rescaleIntercept_; + double rescaleSlope_; + bool hasDefaultWindowing_; + float defaultWindowingCenter_; + float defaultWindowingWidth_; + Orthanc::PixelFormat expectedPixelFormat_; + + void ComputeDoseOffsets(const Orthanc::DicomMap& dicom); + + Data(const Orthanc::DicomMap& dicom); + + CoordinateSystem3D GetFrameGeometry(unsigned int frame) const; + + bool IsPlaneWithinSlice(unsigned int frame, + const CoordinateSystem3D& plane) const; + + void ApplyRescale(Orthanc::ImageAccessor& image, + bool useDouble) const; + }; + + + Data data_; + + + public: + DicomInstanceParameters(const DicomInstanceParameters& other) : + data_(other.data_) + { + } + + DicomInstanceParameters(const Orthanc::DicomMap& dicom) : + data_(dicom) + { + } + + void SetOrthancInstanceIdentifier(const std::string& id) + { + data_.orthancInstanceId_ = id; + } + + const std::string& GetOrthancInstanceIdentifier() const + { + return data_.orthancInstanceId_; + } + + const Orthanc::DicomImageInformation& GetImageInformation() const + { + return data_.imageInformation_; + } + + const std::string& GetStudyInstanceUid() const + { + return data_.studyInstanceUid_; + } + + const std::string& GetSeriesInstanceUid() const + { + return data_.seriesInstanceUid_; + } + + const std::string& GetSopInstanceUid() const + { + return data_.sopInstanceUid_; + } + + SopClassUid GetSopClassUid() const + { + return data_.sopClassUid_; + } + + double GetThickness() const + { + return data_.thickness_; + } + + double GetPixelSpacingX() const + { + return data_.pixelSpacingX_; + } + + double GetPixelSpacingY() const + { + return data_.pixelSpacingY_; + } + + const CoordinateSystem3D& GetGeometry() const + { + return data_.geometry_; + } + + CoordinateSystem3D GetFrameGeometry(unsigned int frame) const + { + return data_.GetFrameGeometry(frame); + } + + bool IsPlaneWithinSlice(unsigned int frame, + const CoordinateSystem3D& plane) const + { + return data_.IsPlaneWithinSlice(frame, plane); + } + + bool IsColor() const + { + return data_.isColor_; + } + + bool HasRescale() const + { + return data_.hasRescale_; + } + + double GetRescaleIntercept() const; + + double GetRescaleSlope() const; + + bool HasDefaultWindowing() const + { + return data_.hasDefaultWindowing_; + } + + float GetDefaultWindowingCenter() const; + + float GetDefaultWindowingWidth() const; + + Orthanc::PixelFormat GetExpectedPixelFormat() const + { + return data_.expectedPixelFormat_; + } + + TextureBaseSceneLayer* CreateTexture(const Orthanc::ImageAccessor& pixelData) const; + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/DicomStructureSet.cpp --- a/Framework/Toolbox/DicomStructureSet.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Toolbox/DicomStructureSet.cpp Wed May 22 16:13:46 2019 +0200 @@ -367,9 +367,7 @@ DicomStructureSet::DicomStructureSet(const OrthancPlugins::FullOrthancDataset& tags) { - using namespace OrthancPlugins; - - DicomDatasetReader reader(tags); + OrthancPlugins::DicomDatasetReader reader(tags); size_t count, tmp; if (!tags.GetSequenceSize(count, DICOM_TAG_RT_ROI_OBSERVATIONS_SEQUENCE) || @@ -385,18 +383,18 @@ for (size_t i = 0; i < count; i++) { structures_[i].interpretation_ = reader.GetStringValue - (DicomPath(DICOM_TAG_RT_ROI_OBSERVATIONS_SEQUENCE, i, - DICOM_TAG_RT_ROI_INTERPRETED_TYPE), + (OrthancPlugins::DicomPath(DICOM_TAG_RT_ROI_OBSERVATIONS_SEQUENCE, i, + DICOM_TAG_RT_ROI_INTERPRETED_TYPE), "No interpretation"); structures_[i].name_ = reader.GetStringValue - (DicomPath(DICOM_TAG_STRUCTURE_SET_ROI_SEQUENCE, i, - DICOM_TAG_ROI_NAME), + (OrthancPlugins::DicomPath(DICOM_TAG_STRUCTURE_SET_ROI_SEQUENCE, i, + DICOM_TAG_ROI_NAME), "No interpretation"); Vector color; - if (ParseVector(color, tags, DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, - DICOM_TAG_ROI_DISPLAY_COLOR)) && + if (ParseVector(color, tags, OrthancPlugins::DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, + DICOM_TAG_ROI_DISPLAY_COLOR)) && color.size() == 3) { structures_[i].red_ = ConvertColor(color[0]); @@ -411,8 +409,8 @@ } size_t countSlices; - if (!tags.GetSequenceSize(countSlices, DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, - DICOM_TAG_CONTOUR_SEQUENCE))) + if (!tags.GetSequenceSize(countSlices, OrthancPlugins::DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, + DICOM_TAG_CONTOUR_SEQUENCE))) { countSlices = 0; } @@ -429,9 +427,9 @@ unsigned int countPoints; if (!reader.GetUnsignedIntegerValue - (countPoints, DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, - DICOM_TAG_CONTOUR_SEQUENCE, j, - DICOM_TAG_NUMBER_OF_CONTOUR_POINTS))) + (countPoints, OrthancPlugins::DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, + DICOM_TAG_CONTOUR_SEQUENCE, j, + DICOM_TAG_NUMBER_OF_CONTOUR_POINTS))) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); } @@ -439,9 +437,9 @@ //LOG(INFO) << "Parsing slice containing " << countPoints << " vertices"; std::string type = reader.GetMandatoryStringValue - (DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, - DICOM_TAG_CONTOUR_SEQUENCE, j, - DICOM_TAG_CONTOUR_GEOMETRIC_TYPE)); + (OrthancPlugins::DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, + DICOM_TAG_CONTOUR_SEQUENCE, j, + DICOM_TAG_CONTOUR_GEOMETRIC_TYPE)); if (type != "CLOSED_PLANAR") { LOG(WARNING) << "Ignoring contour with geometry type: " << type; @@ -449,24 +447,24 @@ } size_t size; - if (!tags.GetSequenceSize(size, DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, - DICOM_TAG_CONTOUR_SEQUENCE, j, - DICOM_TAG_CONTOUR_IMAGE_SEQUENCE)) || + if (!tags.GetSequenceSize(size, OrthancPlugins::DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, + DICOM_TAG_CONTOUR_SEQUENCE, j, + DICOM_TAG_CONTOUR_IMAGE_SEQUENCE)) || size != 1) { throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); } std::string sopInstanceUid = reader.GetMandatoryStringValue - (DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, - DICOM_TAG_CONTOUR_SEQUENCE, j, - DICOM_TAG_CONTOUR_IMAGE_SEQUENCE, 0, - DICOM_TAG_REFERENCED_SOP_INSTANCE_UID)); + (OrthancPlugins::DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, + DICOM_TAG_CONTOUR_SEQUENCE, j, + DICOM_TAG_CONTOUR_IMAGE_SEQUENCE, 0, + DICOM_TAG_REFERENCED_SOP_INSTANCE_UID)); std::string slicesData = reader.GetMandatoryStringValue - (DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, - DICOM_TAG_CONTOUR_SEQUENCE, j, - DICOM_TAG_CONTOUR_DATA)); + (OrthancPlugins::DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, + DICOM_TAG_CONTOUR_SEQUENCE, j, + DICOM_TAG_CONTOUR_DATA)); Vector points; if (!LinearAlgebra::ParseVector(points, slicesData) || diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/DownloadStack.cpp --- a/Framework/Toolbox/DownloadStack.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,196 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "DownloadStack.h" - -#include - -#include - -namespace OrthancStone -{ - bool DownloadStack::CheckInvariants() const - { - std::vector dequeued(nodes_.size(), true); - - int i = firstNode_; - while (i != NIL) - { - const Node& node = nodes_[i]; - - dequeued[i] = false; - - if (node.next_ != NIL && - nodes_[node.next_].prev_ != i) - { - return false; - } - - if (node.prev_ != NIL && - nodes_[node.prev_].next_ != i) - { - return false; - } - - i = nodes_[i].next_; - } - - for (size_t i = 0; i < nodes_.size(); i++) - { - if (nodes_[i].dequeued_ != dequeued[i]) - { - return false; - } - } - - return true; - } - - - DownloadStack::DownloadStack(unsigned int size) - { - nodes_.resize(size); - - if (size == 0) - { - firstNode_ = NIL; - } - else - { - for (size_t i = 0; i < size; i++) - { - nodes_[i].prev_ = static_cast(i - 1); - nodes_[i].next_ = static_cast(i + 1); - nodes_[i].dequeued_ = false; - } - - nodes_.front().prev_ = NIL; - nodes_.back().next_ = NIL; - firstNode_ = 0; - } - - assert(CheckInvariants()); - } - - - DownloadStack::~DownloadStack() - { - assert(CheckInvariants()); - } - - - bool DownloadStack::Pop(unsigned int& value) - { - assert(CheckInvariants()); - - if (firstNode_ == NIL) - { - for (size_t i = 0; i < nodes_.size(); i++) - { - assert(nodes_[i].dequeued_); - } - - return false; - } - else - { - assert(firstNode_ >= 0 && firstNode_ < static_cast(nodes_.size())); - value = firstNode_; - - Node& node = nodes_[firstNode_]; - assert(node.prev_ == NIL); - assert(!node.dequeued_); - - node.dequeued_ = true; - firstNode_ = node.next_; - - if (firstNode_ != NIL) - { - nodes_[firstNode_].prev_ = NIL; - } - - return true; - } - } - - - void DownloadStack::SetTopNodeInternal(unsigned int value) - { - assert(CheckInvariants()); - - Node& node = nodes_[value]; - - if (node.dequeued_) - { - // This node has already been processed by the download thread, nothing to do - return; - } - - // Remove the node from the list - if (node.prev_ == NIL) - { - assert(firstNode_ == static_cast(value)); - - // This is already the top node in the list, nothing to do - return; - } - - nodes_[node.prev_].next_ = node.next_; - - if (node.next_ != NIL) - { - nodes_[node.next_].prev_ = node.prev_; - } - - // Add back the node at the top of the list - assert(firstNode_ != NIL); - - Node& old = nodes_[firstNode_]; - assert(old.prev_ == NIL); - assert(!old.dequeued_); - node.prev_ = NIL; - node.next_ = firstNode_; - old.prev_ = value; - - firstNode_ = value; - } - - - void DownloadStack::SetTopNode(unsigned int value) - { - if (value >= nodes_.size()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - SetTopNodeInternal(value); - } - - - void DownloadStack::SetTopNodePermissive(int value) - { - if (value >= 0 && - value < static_cast(nodes_.size())) - { - SetTopNodeInternal(value); - } - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/DownloadStack.h --- a/Framework/Toolbox/DownloadStack.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include -#include - -namespace OrthancStone -{ - class DownloadStack : public boost::noncopyable - { - private: - static const int NIL = -1; - - // This is a doubly-linked list - struct Node - { - int next_; - int prev_; - bool dequeued_; - }; - - std::vector nodes_; - int firstNode_; - - bool CheckInvariants() const; - - void SetTopNodeInternal(unsigned int value); - - public: - DownloadStack(unsigned int size); - - ~DownloadStack(); - - bool Pop(unsigned int& value); - - void SetTopNode(unsigned int value); - - void SetTopNodePermissive(int value); - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/FiniteProjectiveCamera.cpp --- a/Framework/Toolbox/FiniteProjectiveCamera.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Toolbox/FiniteProjectiveCamera.cpp Wed May 22 16:13:46 2019 +0200 @@ -23,6 +23,7 @@ #include "GeometryToolbox.h" #include "SubpixelReader.h" +#include "ParallelSlices.h" #include #include @@ -299,6 +300,7 @@ static void ApplyRaytracerInternal(Orthanc::ImageAccessor& target, const FiniteProjectiveCamera& camera, const ImageBuffer3D& source, + const VolumeImageGeometry& geometry, VolumeProjection projection) { if (source.GetFormat() != SourceFormat || @@ -315,8 +317,8 @@ LOG(WARNING) << "Output image size: " << target.GetWidth() << "x" << target.GetHeight(); LOG(WARNING) << "Output pixel format: " << Orthanc::EnumerationToString(target.GetFormat()); - std::auto_ptr slices(source.GetGeometry(projection)); - const OrthancStone::Vector pixelSpacing = source.GetGeometry().GetVoxelDimensions(projection); + std::auto_ptr slices(OrthancStone::ParallelSlices::FromVolumeImage(geometry, projection)); + const OrthancStone::Vector pixelSpacing = geometry.GetVoxelDimensions(projection); const unsigned int targetWidth = target.GetWidth(); const unsigned int targetHeight = target.GetHeight(); @@ -422,6 +424,7 @@ Orthanc::ImageAccessor* FiniteProjectiveCamera::ApplyRaytracer(const ImageBuffer3D& source, + const VolumeImageGeometry& geometry, Orthanc::PixelFormat targetFormat, unsigned int targetWidth, unsigned int targetHeight, @@ -440,14 +443,14 @@ { ApplyRaytracerInternal - (*target, *this, source, projection); + (*target, *this, source, geometry, projection); } else if (targetFormat == Orthanc::PixelFormat_Grayscale16 && source.GetFormat() == Orthanc::PixelFormat_Grayscale16 && !mip) { ApplyRaytracerInternal - (*target, *this, source, projection); + (*target, *this, source, geometry, projection); } else { diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/FiniteProjectiveCamera.h --- a/Framework/Toolbox/FiniteProjectiveCamera.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Toolbox/FiniteProjectiveCamera.h Wed May 22 16:13:46 2019 +0200 @@ -23,6 +23,7 @@ #include "LinearAlgebra.h" #include "../Volumes/ImageBuffer3D.h" +#include "../Volumes/VolumeImageGeometry.h" namespace OrthancStone { @@ -109,6 +110,7 @@ Vector ApplyGeneral(const Vector& v) const; Orthanc::ImageAccessor* ApplyRaytracer(const ImageBuffer3D& source, + const VolumeImageGeometry& geometry, Orthanc::PixelFormat targetFormat, unsigned int targetWidth, unsigned int targetHeight, diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/IDelayedCallExecutor.h --- a/Framework/Toolbox/IDelayedCallExecutor.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2018 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../../Framework/Messages/IObserver.h" -#include "../../Framework/Messages/ICallable.h" - -#include -#include - -#include -#include - -namespace OrthancStone -{ - // The IDelayedCall executes a callback after a delay (equivalent to timeout() function in javascript). - class IDelayedCallExecutor : public boost::noncopyable - { - protected: - MessageBroker& broker_; - - public: - ORTHANC_STONE_DEFINE_EMPTY_MESSAGE(__FILE__, __LINE__, TimeoutMessage); - - IDelayedCallExecutor(MessageBroker& broker) : - broker_(broker) - { - } - - - virtual ~IDelayedCallExecutor() - { - } - - - virtual void Schedule(MessageHandler* callback, - unsigned int timeoutInMs = 1000) = 0; - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/IWebService.cpp --- a/Framework/Toolbox/IWebService.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "IWebService.h" - -#include - - -namespace OrthancStone -{ - const Orthanc::IDynamicObject& - IWebService::HttpRequestSuccessMessage::GetPayload() const - { - if (HasPayload()) - { - return *payload_; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - } - - - const Orthanc::IDynamicObject& - IWebService::HttpRequestErrorMessage::GetPayload() const - { - if (HasPayload()) - { - return *payload_; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/IWebService.h --- a/Framework/Toolbox/IWebService.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,166 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../../Framework/Messages/IObserver.h" -#include "../../Framework/Messages/ICallable.h" - -#include -#include - -#include -#include - -namespace OrthancStone -{ - // The IWebService performs HTTP requests. - // Since applications can run in native or WASM environment and, since - // in a WASM environment, the WebService is asynchronous, the IWebservice - // also implements an asynchronous interface: you must schedule a request - // and you'll be notified when the response/error is ready. - class IWebService : public boost::noncopyable - { - protected: - MessageBroker& broker_; - - public: - typedef std::map HttpHeaders; - - class HttpRequestSuccessMessage : public IMessage - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - const std::string& uri_; - const void* answer_; - size_t answerSize_; - const HttpHeaders& answerHeaders_; - const Orthanc::IDynamicObject* payload_; - - public: - HttpRequestSuccessMessage(const std::string& uri, - const void* answer, - size_t answerSize, - const HttpHeaders& answerHeaders, - const Orthanc::IDynamicObject* payload) : - uri_(uri), - answer_(answer), - answerSize_(answerSize), - answerHeaders_(answerHeaders), - payload_(payload) - { - } - - const std::string& GetUri() const - { - return uri_; - } - - const void* GetAnswer() const - { - return answer_; - } - - size_t GetAnswerSize() const - { - return answerSize_; - } - - const HttpHeaders& GetAnswerHttpHeaders() const - { - return answerHeaders_; - } - - bool HasPayload() const - { - return payload_ != NULL; - } - - const Orthanc::IDynamicObject& GetPayload() const; - }; - - - class HttpRequestErrorMessage : public IMessage - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - const std::string& uri_; - const Orthanc::IDynamicObject* payload_; - - public: - HttpRequestErrorMessage(const std::string& uri, - const Orthanc::IDynamicObject* payload) : - uri_(uri), - payload_(payload) - { - } - - const std::string& GetUri() const - { - return uri_; - } - - bool HasPayload() const - { - return payload_ != NULL; - } - - const Orthanc::IDynamicObject& GetPayload() const; - }; - - - IWebService(MessageBroker& broker) : - broker_(broker) - { - } - - - virtual ~IWebService() - { - } - - virtual void EnableCache(bool enable) = 0; - - virtual void GetAsync(const std::string& uri, - const HttpHeaders& headers, - Orthanc::IDynamicObject* payload /* takes ownership */, - MessageHandler* successCallback, - MessageHandler* failureCallback = NULL, - unsigned int timeoutInSeconds = 60) = 0; - - virtual void PostAsync(const std::string& uri, - const HttpHeaders& headers, - const std::string& body, - Orthanc::IDynamicObject* payload /* takes ownership */, - MessageHandler* successCallback, - MessageHandler* failureCallback = NULL, - unsigned int timeoutInSeconds = 60) = 0; - - virtual void DeleteAsync(const std::string& uri, - const HttpHeaders& headers, - Orthanc::IDynamicObject* payload /* takes ownership */, - MessageHandler* successCallback, - MessageHandler* failureCallback = NULL, - unsigned int timeoutInSeconds = 60) = 0; - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/OrientedBoundingBox.cpp --- a/Framework/Toolbox/OrientedBoundingBox.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,268 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "OrientedBoundingBox.h" - -#include "GeometryToolbox.h" - -#include - -#include - -namespace OrthancStone -{ - OrientedBoundingBox::OrientedBoundingBox(const ImageBuffer3D& image) - { - unsigned int n = image.GetDepth(); - if (n < 1) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize); - } - - const CoordinateSystem3D& geometry = image.GetGeometry().GetAxialGeometry(); - Vector dim = image.GetGeometry().GetVoxelDimensions(VolumeProjection_Axial); - - u_ = geometry.GetAxisX(); - v_ = geometry.GetAxisY(); - w_ = geometry.GetNormal(); - - hu_ = static_cast(image.GetWidth() * dim[0] / 2.0); - hv_ = static_cast(image.GetHeight() * dim[1] / 2.0); - hw_ = static_cast(image.GetDepth() * dim[2] / 2.0); - - c_ = (geometry.GetOrigin() + - (hu_ - dim[0] / 2.0) * u_ + - (hv_ - dim[1] / 2.0) * v_ + - (hw_ - dim[2] / 2.0) * w_); - } - - - bool OrientedBoundingBox::HasIntersectionWithPlane(std::vector& points, - const Vector& normal, - double d) const - { - assert(normal.size() == 3); - - double r = (hu_ * fabs(boost::numeric::ublas::inner_prod(normal, u_)) + - hv_ * fabs(boost::numeric::ublas::inner_prod(normal, v_)) + - hw_ * fabs(boost::numeric::ublas::inner_prod(normal, w_))); - - double s = boost::numeric::ublas::inner_prod(normal, c_) + d; - - if (fabs(s) >= r) - { - // No intersection, or intersection is reduced to a single point - return false; - } - else - { - Vector p; - - // Loop over all the 12 edges (segments) of the oriented - // bounding box, and check whether they intersect the plane - - // X-aligned edges - if (GeometryToolbox::IntersectPlaneAndSegment - (p, normal, d, - c_ - u_ * hu_ - v_ * hv_ - w_ * hw_, - c_ + u_ * hu_ - v_ * hv_ - w_ * hw_)) - { - points.push_back(p); - } - - if (GeometryToolbox::IntersectPlaneAndSegment - (p, normal, d, - c_ - u_ * hu_ + v_ * hv_ - w_ * hw_, - c_ + u_ * hu_ + v_ * hv_ - w_ * hw_)) - { - points.push_back(p); - } - - if (GeometryToolbox::IntersectPlaneAndSegment - (p, normal, d, - c_ - u_ * hu_ - v_ * hv_ + w_ * hw_, - c_ + u_ * hu_ - v_ * hv_ + w_ * hw_)) - { - points.push_back(p); - } - - if (GeometryToolbox::IntersectPlaneAndSegment - (p, normal, d, - c_ - u_ * hu_ + v_ * hv_ + w_ * hw_, - c_ + u_ * hu_ + v_ * hv_ + w_ * hw_)) - { - points.push_back(p); - } - - // Y-aligned edges - if (GeometryToolbox::IntersectPlaneAndSegment - (p, normal, d, - c_ - u_ * hu_ - v_ * hv_ - w_ * hw_, - c_ - u_ * hu_ + v_ * hv_ - w_ * hw_)) - { - points.push_back(p); - } - - if (GeometryToolbox::IntersectPlaneAndSegment - (p, normal, d, - c_ + u_ * hu_ - v_ * hv_ - w_ * hw_, - c_ + u_ * hu_ + v_ * hv_ - w_ * hw_)) - { - points.push_back(p); - } - - if (GeometryToolbox::IntersectPlaneAndSegment - (p, normal, d, - c_ - u_ * hu_ - v_ * hv_ + w_ * hw_, - c_ - u_ * hu_ + v_ * hv_ + w_ * hw_)) - { - points.push_back(p); - } - - if (GeometryToolbox::IntersectPlaneAndSegment - (p, normal, d, - c_ + u_ * hu_ - v_ * hv_ + w_ * hw_, - c_ + u_ * hu_ + v_ * hv_ + w_ * hw_)) - { - points.push_back(p); - } - - // Z-aligned edges - if (GeometryToolbox::IntersectPlaneAndSegment - (p, normal, d, - c_ - u_ * hu_ - v_ * hv_ - w_ * hw_, - c_ - u_ * hu_ - v_ * hv_ + w_ * hw_)) - { - points.push_back(p); - } - - if (GeometryToolbox::IntersectPlaneAndSegment - (p, normal, d, - c_ + u_ * hu_ - v_ * hv_ - w_ * hw_, - c_ + u_ * hu_ - v_ * hv_ + w_ * hw_)) - { - points.push_back(p); - } - - if (GeometryToolbox::IntersectPlaneAndSegment - (p, normal, d, - c_ - u_ * hu_ + v_ * hv_ - w_ * hw_, - c_ - u_ * hu_ + v_ * hv_ + w_ * hw_)) - { - points.push_back(p); - } - - if (GeometryToolbox::IntersectPlaneAndSegment - (p, normal, d, - c_ + u_ * hu_ + v_ * hv_ - w_ * hw_, - c_ + u_ * hu_ + v_ * hv_ + w_ * hw_)) - { - points.push_back(p); - } - - return true; - } - } - - - bool OrientedBoundingBox::HasIntersection(std::vector& points, - const CoordinateSystem3D& plane) const - { - // From the vector equation of a 3D plane (specified by origin - // and normal), to the general equation of a 3D plane (which - // looses information about the origin of the coordinate system) - const Vector& normal = plane.GetNormal(); - const Vector& origin = plane.GetOrigin(); - double d = -(normal[0] * origin[0] + normal[1] * origin[1] + normal[2] * origin[2]); - - return HasIntersectionWithPlane(points, normal, d); - } - - - bool OrientedBoundingBox::Contains(const Vector& p) const - { - assert(p.size() == 3); - - const Vector q = p - c_; - - return (fabs(boost::numeric::ublas::inner_prod(q, u_)) <= hu_ && - fabs(boost::numeric::ublas::inner_prod(q, v_)) <= hv_ && - fabs(boost::numeric::ublas::inner_prod(q, w_)) <= hw_); - } - - - void OrientedBoundingBox::FromInternalCoordinates(Vector& target, - double x, - double y, - double z) const - { - target = (c_ + - u_ * 2.0 * hu_ * (x - 0.5) + - v_ * 2.0 * hv_ * (y - 0.5) + - w_ * 2.0 * hw_ * (z - 0.5)); - } - - - void OrientedBoundingBox::FromInternalCoordinates(Vector& target, - const Vector& source) const - { - assert(source.size() == 3); - FromInternalCoordinates(target, source[0], source[1], source[2]); - } - - - void OrientedBoundingBox::ToInternalCoordinates(Vector& target, - const Vector& source) const - { - assert(source.size() == 3); - const Vector q = source - c_; - - double x = boost::numeric::ublas::inner_prod(q, u_) / (2.0 * hu_) + 0.5; - double y = boost::numeric::ublas::inner_prod(q, v_) / (2.0 * hv_) + 0.5; - double z = boost::numeric::ublas::inner_prod(q, w_) / (2.0 * hw_) + 0.5; - - LinearAlgebra::AssignVector(target, x, y, z); - } - - - bool OrientedBoundingBox::ComputeExtent(Extent2D& extent, - const CoordinateSystem3D& plane) const - { - extent.Reset(); - - std::vector points; - if (HasIntersection(points, plane)) - { - for (size_t i = 0; i < points.size(); i++) - { - double x, y; - plane.ProjectPoint(x, y, points[i]); - extent.AddPoint(x, y); - } - - return true; - } - else - { - return false; - } - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/OrientedBoundingBox.h --- a/Framework/Toolbox/OrientedBoundingBox.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "Extent2D.h" -#include "LinearAlgebra.h" -#include "../Volumes/ImageBuffer3D.h" - -namespace OrthancStone -{ - class OrientedBoundingBox : public boost::noncopyable - { - private: - Vector c_; // center - Vector u_; // normalized width vector - Vector v_; // normalized height vector - Vector w_; // normalized depth vector - double hu_; // half width - double hv_; // half height - double hw_; // half depth - - public: - OrientedBoundingBox(const ImageBuffer3D& image); - - const Vector& GetCenter() const - { - return c_; - } - - bool HasIntersectionWithPlane(std::vector& points, - const Vector& normal, - double d) const; - - bool HasIntersection(std::vector& points, - const CoordinateSystem3D& plane) const; - - bool Contains(const Vector& p) const; - - void FromInternalCoordinates(Vector& target, - double x, - double y, - double z) const; - - void FromInternalCoordinates(Vector& target, - const Vector& source) const; - - void ToInternalCoordinates(Vector& target, - const Vector& source) const; - - bool ComputeExtent(Extent2D& extent, - const CoordinateSystem3D& plane) const; - }; -} - diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/OrthancApiClient.cpp --- a/Framework/Toolbox/OrthancApiClient.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,337 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - -#include "OrthancApiClient.h" - -#include "MessagingToolbox.h" -#include "Framework/Toolbox/MessagingToolbox.h" - -#include - -namespace OrthancStone -{ - const Orthanc::IDynamicObject& OrthancApiClient::JsonResponseReadyMessage::GetPayload() const - { - if (HasPayload()) - { - return *payload_; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - } - - - const Orthanc::IDynamicObject& OrthancApiClient::BinaryResponseReadyMessage::GetPayload() const - { - if (HasPayload()) - { - return *payload_; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - } - - - const Orthanc::IDynamicObject& OrthancApiClient::EmptyResponseReadyMessage::GetPayload() const - { - if (HasPayload()) - { - return *payload_; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - } - - - class OrthancApiClient::WebServicePayload : public Orthanc::IDynamicObject - { - private: - std::auto_ptr< MessageHandler > emptyHandler_; - std::auto_ptr< MessageHandler > jsonHandler_; - std::auto_ptr< MessageHandler > binaryHandler_; - std::auto_ptr< MessageHandler > failureHandler_; - std::auto_ptr< Orthanc::IDynamicObject > userPayload_; - - void NotifyConversionError(const IWebService::HttpRequestSuccessMessage& message) const - { - if (failureHandler_.get() != NULL) - { - failureHandler_->Apply(IWebService::HttpRequestErrorMessage - (message.GetUri(), userPayload_.get())); - } - } - - public: - WebServicePayload(MessageHandler* handler, - MessageHandler* failureHandler, - Orthanc::IDynamicObject* userPayload) : - emptyHandler_(handler), - failureHandler_(failureHandler), - userPayload_(userPayload) - { - if (handler == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - } - - WebServicePayload(MessageHandler* handler, - MessageHandler* failureHandler, - Orthanc::IDynamicObject* userPayload) : - binaryHandler_(handler), - failureHandler_(failureHandler), - userPayload_(userPayload) - { - if (handler == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - } - - WebServicePayload(MessageHandler* handler, - MessageHandler* failureHandler, - Orthanc::IDynamicObject* userPayload) : - jsonHandler_(handler), - failureHandler_(failureHandler), - userPayload_(userPayload) - { - if (handler == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - } - - void HandleSuccess(const IWebService::HttpRequestSuccessMessage& message) const - { - if (emptyHandler_.get() != NULL) - { - emptyHandler_->Apply(OrthancApiClient::EmptyResponseReadyMessage - (message.GetUri(), userPayload_.get())); - } - else if (binaryHandler_.get() != NULL) - { - binaryHandler_->Apply(OrthancApiClient::BinaryResponseReadyMessage - (message.GetUri(), message.GetAnswer(), - message.GetAnswerSize(), userPayload_.get())); - } - else if (jsonHandler_.get() != NULL) - { - Json::Value response; - if (MessagingToolbox::ParseJson(response, message.GetAnswer(), message.GetAnswerSize())) - { - jsonHandler_->Apply(OrthancApiClient::JsonResponseReadyMessage - (message.GetUri(), response, userPayload_.get())); - } - else - { - NotifyConversionError(message); - } - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - - void HandleFailure(const IWebService::HttpRequestErrorMessage& message) const - { - if (failureHandler_.get() != NULL) - { - failureHandler_->Apply(IWebService::HttpRequestErrorMessage - (message.GetUri(), userPayload_.get())); - } - } - }; - - - OrthancApiClient::OrthancApiClient(MessageBroker& broker, - IWebService& web, - const std::string& baseUrl) : - IObservable(broker), - IObserver(broker), - web_(web), - baseUrl_(baseUrl) - { - } - - - void OrthancApiClient::GetJsonAsync( - const std::string& uri, - MessageHandler* successCallback, - MessageHandler* failureCallback, - Orthanc::IDynamicObject* payload) - { - IWebService::HttpHeaders emptyHeaders; - web_.GetAsync(baseUrl_ + uri, - emptyHeaders, - new WebServicePayload(successCallback, failureCallback, payload), - new Callable - (*this, &OrthancApiClient::NotifyHttpSuccess), - new Callable - (*this, &OrthancApiClient::NotifyHttpError)); - } - - - void OrthancApiClient::GetBinaryAsync( - const std::string& uri, - const std::string& contentType, - MessageHandler* successCallback, - MessageHandler* failureCallback, - Orthanc::IDynamicObject* payload) - { - IWebService::HttpHeaders headers; - headers["Accept"] = contentType; - GetBinaryAsync(uri, headers, successCallback, failureCallback, payload); - } - - void OrthancApiClient::GetBinaryAsync( - const std::string& uri, - const IWebService::HttpHeaders& headers, - MessageHandler* successCallback, - MessageHandler* failureCallback, - Orthanc::IDynamicObject* payload) - { - // printf("GET [%s] [%s]\n", baseUrl_.c_str(), uri.c_str()); - - web_.GetAsync(baseUrl_ + uri, headers, - new WebServicePayload(successCallback, failureCallback, payload), - new Callable - (*this, &OrthancApiClient::NotifyHttpSuccess), - new Callable - (*this, &OrthancApiClient::NotifyHttpError)); - } - - - void OrthancApiClient::PostBinaryAsyncExpectJson( - const std::string& uri, - const std::string& body, - MessageHandler* successCallback, - MessageHandler* failureCallback, - Orthanc::IDynamicObject* payload) - { - web_.PostAsync(baseUrl_ + uri, IWebService::HttpHeaders(), body, - new WebServicePayload(successCallback, failureCallback, payload), - new Callable - (*this, &OrthancApiClient::NotifyHttpSuccess), - new Callable - (*this, &OrthancApiClient::NotifyHttpError)); - - } - - void OrthancApiClient::PostBinaryAsync( - const std::string& uri, - const std::string& body) - { - web_.PostAsync(baseUrl_ + uri, IWebService::HttpHeaders(), body, NULL, NULL, NULL); - } - - void OrthancApiClient::PostBinaryAsync( - const std::string& uri, - const std::string& body, - MessageHandler* successCallback, - MessageHandler* failureCallback, - Orthanc::IDynamicObject* payload /* takes ownership */) - { - web_.PostAsync(baseUrl_ + uri, IWebService::HttpHeaders(), body, - new WebServicePayload(successCallback, failureCallback, payload), - new Callable - (*this, &OrthancApiClient::NotifyHttpSuccess), - new Callable - (*this, &OrthancApiClient::NotifyHttpError)); - } - - void OrthancApiClient::PostJsonAsyncExpectJson( - const std::string& uri, - const Json::Value& data, - MessageHandler* successCallback, - MessageHandler* failureCallback, - Orthanc::IDynamicObject* payload) - { - std::string body; - MessagingToolbox::JsonToString(body, data); - return PostBinaryAsyncExpectJson(uri, body, successCallback, failureCallback, payload); - } - - void OrthancApiClient::PostJsonAsync( - const std::string& uri, - const Json::Value& data) - { - std::string body; - MessagingToolbox::JsonToString(body, data); - return PostBinaryAsync(uri, body); - } - - void OrthancApiClient::PostJsonAsync( - const std::string& uri, - const Json::Value& data, - MessageHandler* successCallback, - MessageHandler* failureCallback, - Orthanc::IDynamicObject* payload /* takes ownership */) - { - std::string body; - MessagingToolbox::JsonToString(body, data); - return PostBinaryAsync(uri, body, successCallback, failureCallback, payload); - } - - void OrthancApiClient::DeleteAsync( - const std::string& uri, - MessageHandler* successCallback, - MessageHandler* failureCallback, - Orthanc::IDynamicObject* payload) - { - web_.DeleteAsync(baseUrl_ + uri, IWebService::HttpHeaders(), - new WebServicePayload(successCallback, failureCallback, payload), - new Callable - (*this, &OrthancApiClient::NotifyHttpSuccess), - new Callable - (*this, &OrthancApiClient::NotifyHttpError)); - } - - - void OrthancApiClient::NotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message) - { - if (message.HasPayload()) - { - dynamic_cast(message.GetPayload()).HandleSuccess(message); - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - - void OrthancApiClient::NotifyHttpError(const IWebService::HttpRequestErrorMessage& message) - { - if (message.HasPayload()) - { - dynamic_cast(message.GetPayload()).HandleFailure(message); - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/OrthancApiClient.h --- a/Framework/Toolbox/OrthancApiClient.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,243 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include -#include - -#include "IWebService.h" -#include "../Messages/IObservable.h" -#include "../Messages/Promise.h" - -namespace OrthancStone -{ - class OrthancApiClient : - public IObservable, - public IObserver - { - public: - class JsonResponseReadyMessage : public IMessage - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - const std::string& uri_; - const Json::Value& json_; - const Orthanc::IDynamicObject* payload_; - - public: - JsonResponseReadyMessage(const std::string& uri, - const Json::Value& json, - const Orthanc::IDynamicObject* payload) : - uri_(uri), - json_(json), - payload_(payload) - { - } - - const std::string& GetUri() const - { - return uri_; - } - - const Json::Value& GetJson() const - { - return json_; - } - - bool HasPayload() const - { - return payload_ != NULL; - } - - const Orthanc::IDynamicObject& GetPayload() const; - }; - - - class BinaryResponseReadyMessage : public IMessage - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - const std::string& uri_; - const void* answer_; - size_t answerSize_; - const Orthanc::IDynamicObject* payload_; - - public: - BinaryResponseReadyMessage(const std::string& uri, - const void* answer, - size_t answerSize, - const Orthanc::IDynamicObject* payload) : - uri_(uri), - answer_(answer), - answerSize_(answerSize), - payload_(payload) - { - } - - const std::string& GetUri() const - { - return uri_; - } - - const void* GetAnswer() const - { - return answer_; - } - - size_t GetAnswerSize() const - { - return answerSize_; - } - - bool HasPayload() const - { - return payload_ != NULL; - } - - const Orthanc::IDynamicObject& GetPayload() const; - }; - - - class EmptyResponseReadyMessage : public IMessage - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - const std::string& uri_; - const Orthanc::IDynamicObject* payload_; - - public: - EmptyResponseReadyMessage(const std::string& uri, - const Orthanc::IDynamicObject* payload) : - uri_(uri), - payload_(payload) - { - } - - const std::string& GetUri() const - { - return uri_; - } - - bool HasPayload() const - { - return payload_ != NULL; - } - - const Orthanc::IDynamicObject& GetPayload() const; - }; - - - - private: - class WebServicePayload; - - protected: - IWebService& web_; - std::string baseUrl_; - - public: - OrthancApiClient(MessageBroker& broker, - IWebService& web, - const std::string& baseUrl); - - virtual ~OrthancApiClient() - { - } - - const std::string& GetBaseUrl() const {return baseUrl_;} - - // schedule a GET request expecting a JSON response. - void GetJsonAsync(const std::string& uri, - MessageHandler* successCallback, - MessageHandler* failureCallback = NULL, - Orthanc::IDynamicObject* payload = NULL /* takes ownership */); - - // schedule a GET request expecting a binary response. - void GetBinaryAsync(const std::string& uri, - const std::string& contentType, - MessageHandler* successCallback, - MessageHandler* failureCallback = NULL, - Orthanc::IDynamicObject* payload = NULL /* takes ownership */); - - // schedule a GET request expecting a binary response. - void GetBinaryAsync(const std::string& uri, - const IWebService::HttpHeaders& headers, - MessageHandler* successCallback, - MessageHandler* failureCallback = NULL, - Orthanc::IDynamicObject* payload = NULL /* takes ownership */); - - // schedule a POST request expecting a JSON response. - void PostBinaryAsyncExpectJson(const std::string& uri, - const std::string& body, - MessageHandler* successCallback, - MessageHandler* failureCallback = NULL, - Orthanc::IDynamicObject* payload = NULL /* takes ownership */); - - // schedule a POST request expecting a JSON response. - void PostJsonAsyncExpectJson(const std::string& uri, - const Json::Value& data, - MessageHandler* successCallback, - MessageHandler* failureCallback = NULL, - Orthanc::IDynamicObject* payload = NULL /* takes ownership */); - - // schedule a POST request and don't mind the response. - void PostJsonAsync(const std::string& uri, - const Json::Value& data); - - // schedule a POST request and don't expect any response. - void PostJsonAsync(const std::string& uri, - const Json::Value& data, - MessageHandler* successCallback, - MessageHandler* failureCallback = NULL, - Orthanc::IDynamicObject* payload = NULL /* takes ownership */); - - - // schedule a POST request and don't mind the response. - void PostBinaryAsync(const std::string& uri, - const std::string& body); - - // schedule a POST request and don't expect any response. - void PostBinaryAsync(const std::string& uri, - const std::string& body, - MessageHandler* successCallback, - MessageHandler* failureCallback = NULL, - Orthanc::IDynamicObject* payload = NULL /* takes ownership */); - - // schedule a DELETE request expecting an empty response. - void DeleteAsync(const std::string& uri, - MessageHandler* successCallback, - MessageHandler* failureCallback = NULL, - Orthanc::IDynamicObject* payload = NULL /* takes ownership */); - - void NotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message); - - void NotifyHttpError(const IWebService::HttpRequestErrorMessage& message); - - private: - void HandleFromCache(const std::string& uri, - const IWebService::HttpHeaders& headers, - Orthanc::IDynamicObject* payload /* takes ownership */); - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/OrthancSlicesLoader.cpp --- a/Framework/Toolbox/OrthancSlicesLoader.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,904 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "OrthancSlicesLoader.h" - -#include "MessagingToolbox.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - - - -/** - * TODO This is a SLOW implementation of base64 decoding, because - * "Orthanc::Toolbox::DecodeBase64()" does not work properly with - * WASM. UNDERSTAND WHY. - * https://stackoverflow.com/a/34571089/881731 - **/ -static std::string base64_decode(const std::string &in) -{ - std::string out; - - std::vector T(256,-1); - for (int i=0; i<64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i; - - int val=0, valb=-8; - for (size_t i = 0; i < in.size(); i++) { - unsigned char c = in[i]; - if (T[c] == -1) break; - val = (val<<6) + T[c]; - valb += 6; - if (valb>=0) { - out.push_back(char((val>>valb)&0xFF)); - valb-=8; - } - } - return out; -} - - - -namespace OrthancStone -{ - class OrthancSlicesLoader::Operation : public Orthanc::IDynamicObject - { - private: - Mode mode_; - unsigned int frame_; - unsigned int sliceIndex_; - const Deprecated::Slice* slice_; - std::string instanceId_; - SliceImageQuality quality_; - - Operation(Mode mode) : - mode_(mode) - { - } - - public: - Mode GetMode() const - { - return mode_; - } - - SliceImageQuality GetQuality() const - { - assert(mode_ == Mode_LoadImage || - mode_ == Mode_LoadRawImage); - return quality_; - } - - unsigned int GetSliceIndex() const - { - assert(mode_ == Mode_LoadImage || - mode_ == Mode_LoadRawImage); - return sliceIndex_; - } - - const Deprecated::Slice& GetSlice() const - { - assert(mode_ == Mode_LoadImage || - mode_ == Mode_LoadRawImage); - assert(slice_ != NULL); - return *slice_; - } - - unsigned int GetFrame() const - { - assert(mode_ == Mode_FrameGeometry); - return frame_; - } - - const std::string& GetInstanceId() const - { - assert(mode_ == Mode_FrameGeometry || - mode_ == Mode_InstanceGeometry); - return instanceId_; - } - - static Operation* DownloadInstanceGeometry(const std::string& instanceId) - { - std::auto_ptr operation(new Operation(Mode_InstanceGeometry)); - operation->instanceId_ = instanceId; - return operation.release(); - } - - static Operation* DownloadFrameGeometry(const std::string& instanceId, - unsigned int frame) - { - std::auto_ptr operation(new Operation(Mode_FrameGeometry)); - operation->instanceId_ = instanceId; - operation->frame_ = frame; - return operation.release(); - } - - static Operation* DownloadSliceImage(unsigned int sliceIndex, - const Deprecated::Slice& slice, - SliceImageQuality quality) - { - std::auto_ptr tmp(new Operation(Mode_LoadImage)); - tmp->sliceIndex_ = sliceIndex; - tmp->slice_ = &slice; - tmp->quality_ = quality; - return tmp.release(); - } - - static Operation* DownloadSliceRawImage(unsigned int sliceIndex, - const Deprecated::Slice& slice) - { - std::auto_ptr tmp(new Operation(Mode_LoadRawImage)); - tmp->sliceIndex_ = sliceIndex; - tmp->slice_ = &slice; - tmp->quality_ = SliceImageQuality_InternalRaw; - return tmp.release(); - } - - static Operation* DownloadDicomFile(const Deprecated::Slice& slice) - { - std::auto_ptr tmp(new Operation(Mode_LoadDicomFile)); - tmp->slice_ = &slice; - return tmp.release(); - } - - }; - - void OrthancSlicesLoader::NotifySliceImageSuccess(const Operation& operation, - const Orthanc::ImageAccessor& image) - { - OrthancSlicesLoader::SliceImageReadyMessage msg - (*this, operation.GetSliceIndex(), operation.GetSlice(), image, operation.GetQuality()); - BroadcastMessage(msg); - } - - - void OrthancSlicesLoader::NotifySliceImageError(const Operation& operation) - { - OrthancSlicesLoader::SliceImageErrorMessage msg - (*this, operation.GetSliceIndex(), operation.GetSlice(), operation.GetQuality()); - BroadcastMessage(msg); - } - - - void OrthancSlicesLoader::SortAndFinalizeSlices() - { - bool ok = slices_.Sort(); - - state_ = State_GeometryReady; - - if (ok) - { - LOG(INFO) << "Loaded a series with " << slices_.GetSlicesCount() << " slice(s)"; - BroadcastMessage(SliceGeometryReadyMessage(*this)); - } - else - { - LOG(ERROR) << "This series is empty"; - BroadcastMessage(SliceGeometryErrorMessage(*this)); - } - } - - void OrthancSlicesLoader::OnGeometryError(const IWebService::HttpRequestErrorMessage& message) - { - BroadcastMessage(SliceGeometryErrorMessage(*this)); - state_ = State_Error; - } - - void OrthancSlicesLoader::OnSliceImageError(const IWebService::HttpRequestErrorMessage& message) - { - NotifySliceImageError(dynamic_cast(message.GetPayload())); - state_ = State_Error; - } - - void OrthancSlicesLoader::ParseSeriesGeometry(const OrthancApiClient::JsonResponseReadyMessage& message) - { - const Json::Value& series = message.GetJson(); - Json::Value::Members instances = series.getMemberNames(); - - slices_.Reserve(instances.size()); - - for (size_t i = 0; i < instances.size(); i++) - { - OrthancPlugins::FullOrthancDataset dataset(series[instances[i]]); - - Orthanc::DicomMap dicom; - MessagingToolbox::ConvertDataset(dicom, dataset); - - unsigned int frames; - if (!dicom.ParseUnsignedInteger32(frames, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES)) - { - frames = 1; - } - - for (unsigned int frame = 0; frame < frames; frame++) - { - std::auto_ptr slice(new Deprecated::Slice); - if (slice->ParseOrthancFrame(dicom, instances[i], frame)) - { - CoordinateSystem3D geometry = slice->GetGeometry(); - slices_.AddSlice(geometry, slice.release()); - } - else - { - LOG(WARNING) << "Skipping invalid frame " << frame << " within instance " << instances[i]; - } - } - } - - SortAndFinalizeSlices(); - } - - void OrthancSlicesLoader::ParseInstanceGeometry(const OrthancApiClient::JsonResponseReadyMessage& message) - { - const Json::Value& tags = message.GetJson(); - const std::string& instanceId = dynamic_cast(message.GetPayload()).GetInstanceId(); - - OrthancPlugins::FullOrthancDataset dataset(tags); - - Orthanc::DicomMap dicom; - MessagingToolbox::ConvertDataset(dicom, dataset); - - unsigned int frames; - if (!dicom.ParseUnsignedInteger32(frames, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES)) - { - frames = 1; - } - - LOG(INFO) << "Instance " << instanceId << " contains " << frames << " frame(s)"; - - for (unsigned int frame = 0; frame < frames; frame++) - { - std::auto_ptr slice(new Deprecated::Slice); - if (slice->ParseOrthancFrame(dicom, instanceId, frame)) - { - CoordinateSystem3D geometry = slice->GetGeometry(); - slices_.AddSlice(geometry, slice.release()); - } - else - { - LOG(WARNING) << "Skipping invalid multi-frame instance " << instanceId; - BroadcastMessage(SliceGeometryErrorMessage(*this)); - return; - } - } - - SortAndFinalizeSlices(); - } - - - void OrthancSlicesLoader::ParseFrameGeometry(const OrthancApiClient::JsonResponseReadyMessage& message) - { - const Json::Value& tags = message.GetJson(); - const std::string& instanceId = dynamic_cast(message.GetPayload()).GetInstanceId(); - unsigned int frame = dynamic_cast(message.GetPayload()).GetFrame(); - - OrthancPlugins::FullOrthancDataset dataset(tags); - - state_ = State_GeometryReady; - - Orthanc::DicomMap dicom; - MessagingToolbox::ConvertDataset(dicom, dataset); - - std::auto_ptr slice(new Deprecated::Slice); - if (slice->ParseOrthancFrame(dicom, instanceId, frame)) - { - LOG(INFO) << "Loaded instance geometry " << instanceId; - - CoordinateSystem3D geometry = slice->GetGeometry(); - slices_.AddSlice(geometry, slice.release()); - - BroadcastMessage(SliceGeometryReadyMessage(*this)); - } - else - { - LOG(WARNING) << "Skipping invalid instance " << instanceId; - BroadcastMessage(SliceGeometryErrorMessage(*this)); - } - } - - - void OrthancSlicesLoader::ParseSliceImagePng(const OrthancApiClient::BinaryResponseReadyMessage& message) - { - const Operation& operation = dynamic_cast(message.GetPayload()); - std::auto_ptr image; - - try - { - image.reset(new Orthanc::PngReader); - dynamic_cast(*image).ReadFromMemory(message.GetAnswer(), message.GetAnswerSize()); - } - catch (Orthanc::OrthancException&) - { - NotifySliceImageError(operation); - return; - } - - if (image->GetWidth() != operation.GetSlice().GetWidth() || - image->GetHeight() != operation.GetSlice().GetHeight()) - { - NotifySliceImageError(operation); - return; - } - - if (operation.GetSlice().GetConverter().GetExpectedPixelFormat() == - Orthanc::PixelFormat_SignedGrayscale16) - { - if (image->GetFormat() == Orthanc::PixelFormat_Grayscale16) - { - image->SetFormat(Orthanc::PixelFormat_SignedGrayscale16); - } - else - { - NotifySliceImageError(operation); - return; - } - } - - NotifySliceImageSuccess(operation, *image); - } - - void OrthancSlicesLoader::ParseSliceImagePam(const OrthancApiClient::BinaryResponseReadyMessage& message) - { - const Operation& operation = dynamic_cast(message.GetPayload()); - std::auto_ptr image; - - try - { - image.reset(new Orthanc::PamReader); - dynamic_cast(*image).ReadFromMemory(message.GetAnswer(), message.GetAnswerSize()); - } - catch (Orthanc::OrthancException&) - { - NotifySliceImageError(operation); - return; - } - - if (image->GetWidth() != operation.GetSlice().GetWidth() || - image->GetHeight() != operation.GetSlice().GetHeight()) - { - NotifySliceImageError(operation); - return; - } - - if (operation.GetSlice().GetConverter().GetExpectedPixelFormat() == - Orthanc::PixelFormat_SignedGrayscale16) - { - if (image->GetFormat() == Orthanc::PixelFormat_Grayscale16) - { - image->SetFormat(Orthanc::PixelFormat_SignedGrayscale16); - } - else - { - NotifySliceImageError(operation); - return; - } - } - - NotifySliceImageSuccess(operation, *image); - } - - - void OrthancSlicesLoader::ParseSliceImageJpeg(const OrthancApiClient::JsonResponseReadyMessage& message) - { - const Operation& operation = dynamic_cast(message.GetPayload()); - - const Json::Value& encoded = message.GetJson(); - if (encoded.type() != Json::objectValue || - !encoded.isMember("Orthanc") || - encoded["Orthanc"].type() != Json::objectValue) - { - NotifySliceImageError(operation); - return; - } - - const Json::Value& info = encoded["Orthanc"]; - if (!info.isMember("PixelData") || - !info.isMember("Stretched") || - !info.isMember("Compression") || - info["Compression"].type() != Json::stringValue || - info["PixelData"].type() != Json::stringValue || - info["Stretched"].type() != Json::booleanValue || - info["Compression"].asString() != "Jpeg") - { - NotifySliceImageError(operation); - return; - } - - bool isSigned = false; - bool isStretched = info["Stretched"].asBool(); - - if (info.isMember("IsSigned")) - { - if (info["IsSigned"].type() != Json::booleanValue) - { - NotifySliceImageError(operation); - return; - } - else - { - isSigned = info["IsSigned"].asBool(); - } - } - - std::auto_ptr reader; - - { - std::string jpeg; - //Orthanc::Toolbox::DecodeBase64(jpeg, info["PixelData"].asString()); - jpeg = base64_decode(info["PixelData"].asString()); - - try - { - reader.reset(new Orthanc::JpegReader); - dynamic_cast(*reader).ReadFromMemory(jpeg); - } - catch (Orthanc::OrthancException&) - { - NotifySliceImageError(operation); - return; - } - } - - Orthanc::PixelFormat expectedFormat = - operation.GetSlice().GetConverter().GetExpectedPixelFormat(); - - if (reader->GetFormat() == Orthanc::PixelFormat_RGB24) // This is a color image - { - if (expectedFormat != Orthanc::PixelFormat_RGB24) - { - NotifySliceImageError(operation); - return; - } - - if (isSigned || isStretched) - { - NotifySliceImageError(operation); - return; - } - else - { - NotifySliceImageSuccess(operation, *reader); - return; - } - } - - if (reader->GetFormat() != Orthanc::PixelFormat_Grayscale8) - { - NotifySliceImageError(operation); - return; - } - - if (!isStretched) - { - if (expectedFormat != reader->GetFormat()) - { - NotifySliceImageError(operation); - return; - } - else - { - NotifySliceImageSuccess(operation, *reader); - return; - } - } - - int32_t stretchLow = 0; - int32_t stretchHigh = 0; - - if (!info.isMember("StretchLow") || - !info.isMember("StretchHigh") || - info["StretchLow"].type() != Json::intValue || - info["StretchHigh"].type() != Json::intValue) - { - NotifySliceImageError(operation); - return; - } - - stretchLow = info["StretchLow"].asInt(); - stretchHigh = info["StretchHigh"].asInt(); - - if (stretchLow < -32768 || - stretchHigh > 65535 || - (stretchLow < 0 && stretchHigh > 32767)) - { - // This range cannot be represented with a uint16_t or an int16_t - NotifySliceImageError(operation); - return; - } - - // Decode a grayscale JPEG 8bpp image coming from the Web viewer - std::auto_ptr image - (new Orthanc::Image(expectedFormat, reader->GetWidth(), reader->GetHeight(), false)); - - Orthanc::ImageProcessing::Convert(*image, *reader); - reader.reset(); - - float scaling = static_cast(stretchHigh - stretchLow) / 255.0f; - - if (!LinearAlgebra::IsCloseToZero(scaling)) - { - float offset = static_cast(stretchLow) / scaling; - Orthanc::ImageProcessing::ShiftScale(*image, offset, scaling, true); - } - - NotifySliceImageSuccess(operation, *image); - } - - - class StringImage : public Orthanc::ImageAccessor - { - private: - std::string buffer_; - - public: - StringImage(Orthanc::PixelFormat format, - unsigned int width, - unsigned int height, - std::string& buffer) - { - if (buffer.size() != Orthanc::GetBytesPerPixel(format) * width * height) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); - } - - buffer_.swap(buffer); // The source buffer is now empty - - void* data = (buffer_.empty() ? NULL : &buffer_[0]); - - AssignWritable(format, width, height, - Orthanc::GetBytesPerPixel(format) * width, data); - } - }; - - void OrthancSlicesLoader::ParseSliceRawImage(const OrthancApiClient::BinaryResponseReadyMessage& message) - { - const Operation& operation = dynamic_cast(message.GetPayload()); - Orthanc::GzipCompressor compressor; - - std::string raw; - compressor.Uncompress(raw, message.GetAnswer(), message.GetAnswerSize()); - - const Orthanc::DicomImageInformation& info = operation.GetSlice().GetImageInformation(); - - if (info.GetBitsAllocated() == 32 && - info.GetBitsStored() == 32 && - info.GetHighBit() == 31 && - info.GetChannelCount() == 1 && - !info.IsSigned() && - info.GetPhotometricInterpretation() == Orthanc::PhotometricInterpretation_Monochrome2 && - raw.size() == info.GetWidth() * info.GetHeight() * 4) - { - // This is the case of RT-DOSE (uint32_t values) - - std::auto_ptr image - (new StringImage(Orthanc::PixelFormat_Grayscale32, info.GetWidth(), - info.GetHeight(), raw)); - - // TODO - Only for big endian - for (unsigned int y = 0; y < image->GetHeight(); y++) - { - uint32_t *p = reinterpret_cast(image->GetRow(y)); - for (unsigned int x = 0; x < image->GetWidth(); x++, p++) - { - *p = le32toh(*p); - } - } - - NotifySliceImageSuccess(operation, *image); - } - else if (info.GetBitsAllocated() == 16 && - info.GetBitsStored() == 16 && - info.GetHighBit() == 15 && - info.GetChannelCount() == 1 && - !info.IsSigned() && - info.GetPhotometricInterpretation() == Orthanc::PhotometricInterpretation_Monochrome2 && - raw.size() == info.GetWidth() * info.GetHeight() * 2) - { - std::auto_ptr image - (new StringImage(Orthanc::PixelFormat_Grayscale16, info.GetWidth(), - info.GetHeight(), raw)); - - // TODO - Big endian ? - - NotifySliceImageSuccess(operation, *image); - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - - } - - - OrthancSlicesLoader::OrthancSlicesLoader(MessageBroker& broker, - OrthancApiClient& orthanc) : - IObservable(broker), - IObserver(broker), - orthanc_(orthanc), - state_(State_Initialization) - { - } - - - void OrthancSlicesLoader::ScheduleLoadSeries(const std::string& seriesId) - { - if (state_ != State_Initialization) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else - { - state_ = State_LoadingGeometry; - orthanc_.GetJsonAsync("/series/" + seriesId + "/instances-tags", - new Callable(*this, &OrthancSlicesLoader::ParseSeriesGeometry), - new Callable(*this, &OrthancSlicesLoader::OnGeometryError), - NULL); - } - } - - void OrthancSlicesLoader::ScheduleLoadInstance(const std::string& instanceId) - { - if (state_ != State_Initialization) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else - { - state_ = State_LoadingGeometry; - - // Tag "3004-000c" is "Grid Frame Offset Vector", which is - // mandatory to read RT DOSE, but is too long to be returned by default - orthanc_.GetJsonAsync("/instances/" + instanceId + "/tags?ignore-length=3004-000c", - new Callable(*this, &OrthancSlicesLoader::ParseInstanceGeometry), - new Callable(*this, &OrthancSlicesLoader::OnGeometryError), - Operation::DownloadInstanceGeometry(instanceId)); - } - } - - - void OrthancSlicesLoader::ScheduleLoadFrame(const std::string& instanceId, - unsigned int frame) - { - if (state_ != State_Initialization) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else - { - state_ = State_LoadingGeometry; - - orthanc_.GetJsonAsync("/instances/" + instanceId + "/tags", - new Callable(*this, &OrthancSlicesLoader::ParseFrameGeometry), - new Callable(*this, &OrthancSlicesLoader::OnGeometryError), - Operation::DownloadFrameGeometry(instanceId, frame)); - } - } - - - bool OrthancSlicesLoader::IsGeometryReady() const - { - return state_ == State_GeometryReady; - } - - - size_t OrthancSlicesLoader::GetSlicesCount() const - { - if (state_ != State_GeometryReady) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return slices_.GetSlicesCount(); - } - - - const Deprecated::Slice& OrthancSlicesLoader::GetSlice(size_t index) const - { - if (state_ != State_GeometryReady) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return dynamic_cast(slices_.GetSlicePayload(index)); - } - - - bool OrthancSlicesLoader::LookupSlice(size_t& index, - const CoordinateSystem3D& plane) const - { - if (state_ != State_GeometryReady) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - double distance; - return (slices_.LookupClosestSlice(index, distance, plane) && - distance <= GetSlice(index).GetThickness() / 2.0); - } - - - void OrthancSlicesLoader::ScheduleSliceImagePng(const Deprecated::Slice& slice, - size_t index) - { - std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + - boost::lexical_cast(slice.GetFrame())); - - switch (slice.GetConverter().GetExpectedPixelFormat()) - { - case Orthanc::PixelFormat_RGB24: - uri += "/preview"; - break; - - case Orthanc::PixelFormat_Grayscale16: - uri += "/image-uint16"; - break; - - case Orthanc::PixelFormat_SignedGrayscale16: - uri += "/image-int16"; - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - orthanc_.GetBinaryAsync(uri, "image/png", - new Callable - (*this, &OrthancSlicesLoader::ParseSliceImagePng), - new Callable - (*this, &OrthancSlicesLoader::OnSliceImageError), - Operation::DownloadSliceImage( - static_cast(index), slice, SliceImageQuality_FullPng)); -} - - void OrthancSlicesLoader::ScheduleSliceImagePam(const Deprecated::Slice& slice, - size_t index) - { - std::string uri = - ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + - boost::lexical_cast(slice.GetFrame())); - - switch (slice.GetConverter().GetExpectedPixelFormat()) - { - case Orthanc::PixelFormat_RGB24: - uri += "/preview"; - break; - - case Orthanc::PixelFormat_Grayscale16: - uri += "/image-uint16"; - break; - - case Orthanc::PixelFormat_SignedGrayscale16: - uri += "/image-int16"; - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - orthanc_.GetBinaryAsync(uri, "image/x-portable-arbitrarymap", - new Callable - (*this, &OrthancSlicesLoader::ParseSliceImagePam), - new Callable - (*this, &OrthancSlicesLoader::OnSliceImageError), - Operation::DownloadSliceImage(static_cast(index), - slice, SliceImageQuality_FullPam)); - } - - - - void OrthancSlicesLoader::ScheduleSliceImageJpeg(const Deprecated::Slice& slice, - size_t index, - SliceImageQuality quality) - { - unsigned int value; - - switch (quality) - { - case SliceImageQuality_Jpeg50: - value = 50; - break; - - case SliceImageQuality_Jpeg90: - value = 90; - break; - - case SliceImageQuality_Jpeg95: - value = 95; - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - // This requires the official Web viewer plugin to be installed! - std::string uri = ("/web-viewer/instances/jpeg" + - boost::lexical_cast(value) + - "-" + slice.GetOrthancInstanceId() + "_" + - boost::lexical_cast(slice.GetFrame())); - - orthanc_.GetJsonAsync(uri, - new Callable - (*this, &OrthancSlicesLoader::ParseSliceImageJpeg), - new Callable - (*this, &OrthancSlicesLoader::OnSliceImageError), - Operation::DownloadSliceImage( - static_cast(index), slice, quality)); - } - - - - void OrthancSlicesLoader::ScheduleLoadSliceImage(size_t index, - SliceImageQuality quality) - { - if (state_ != State_GeometryReady) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - const Deprecated::Slice& slice = GetSlice(index); - - if (slice.HasOrthancDecoding()) - { - switch (quality) - { - case SliceImageQuality_FullPng: - ScheduleSliceImagePng(slice, index); - break; - case SliceImageQuality_FullPam: - ScheduleSliceImagePam(slice, index); - break; - default: - ScheduleSliceImageJpeg(slice, index, quality); - } - } - else - { - std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + - boost::lexical_cast(slice.GetFrame()) + "/raw.gz"); - orthanc_.GetBinaryAsync(uri, IWebService::HttpHeaders(), - new Callable - (*this, &OrthancSlicesLoader::ParseSliceRawImage), - new Callable - (*this, &OrthancSlicesLoader::OnSliceImageError), - Operation::DownloadSliceRawImage( - static_cast(index), slice)); - } - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/OrthancSlicesLoader.h --- a/Framework/Toolbox/OrthancSlicesLoader.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,209 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../Messages/IObservable.h" -#include "../StoneEnumerations.h" -#include "IWebService.h" -#include "OrthancApiClient.h" -#include "SlicesSorter.h" -#include "Slice.h" - -#include - - -namespace OrthancStone -{ - class OrthancSlicesLoader : public IObservable, public IObserver - { - public: - ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, SliceGeometryReadyMessage, OrthancSlicesLoader); - ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, SliceGeometryErrorMessage, OrthancSlicesLoader); - - - class SliceImageReadyMessage : public OriginMessage - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - unsigned int sliceIndex_; - const Deprecated::Slice& slice_; - const Orthanc::ImageAccessor& image_; - SliceImageQuality effectiveQuality_; - - public: - SliceImageReadyMessage(const OrthancSlicesLoader& origin, - unsigned int sliceIndex, - const Deprecated::Slice& slice, - const Orthanc::ImageAccessor& image, - SliceImageQuality effectiveQuality) : - OriginMessage(origin), - sliceIndex_(sliceIndex), - slice_(slice), - image_(image), - effectiveQuality_(effectiveQuality) - { - } - - unsigned int GetSliceIndex() const - { - return sliceIndex_; - } - - const Deprecated::Slice& GetSlice() const - { - return slice_; - } - - const Orthanc::ImageAccessor& GetImage() const - { - return image_; - } - - SliceImageQuality GetEffectiveQuality() const - { - return effectiveQuality_; - } - }; - - - class SliceImageErrorMessage : public OriginMessage - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - const Deprecated::Slice& slice_; - unsigned int sliceIndex_; - SliceImageQuality effectiveQuality_; - - public: - SliceImageErrorMessage(const OrthancSlicesLoader& origin, - unsigned int sliceIndex, - const Deprecated::Slice& slice, - SliceImageQuality effectiveQuality) : - OriginMessage(origin), - slice_(slice), - sliceIndex_(sliceIndex), - effectiveQuality_(effectiveQuality) - { - } - unsigned int GetSliceIndex() const - { - return sliceIndex_; - } - - const Deprecated::Slice& GetSlice() const - { - return slice_; - } - - SliceImageQuality GetEffectiveQuality() const - { - return effectiveQuality_; - } - }; - - private: - enum State - { - State_Error, - State_Initialization, - State_LoadingGeometry, - State_GeometryReady - }; - - enum Mode - { - Mode_SeriesGeometry, - Mode_InstanceGeometry, - Mode_FrameGeometry, - Mode_LoadImage, - Mode_LoadRawImage, - Mode_LoadDicomFile - }; - - class Operation; - - OrthancApiClient& orthanc_; - State state_; - SlicesSorter slices_; - - void NotifySliceImageSuccess(const Operation& operation, - const Orthanc::ImageAccessor& image); - - void NotifySliceImageError(const Operation& operation); - - void OnGeometryError(const IWebService::HttpRequestErrorMessage& message); - - void OnSliceImageError(const IWebService::HttpRequestErrorMessage& message); - - void ParseSeriesGeometry(const OrthancApiClient::JsonResponseReadyMessage& message); - - void ParseInstanceGeometry(const OrthancApiClient::JsonResponseReadyMessage& message); - - void ParseFrameGeometry(const OrthancApiClient::JsonResponseReadyMessage& message); - - void ParseSliceImagePng(const OrthancApiClient::BinaryResponseReadyMessage& message); - - void ParseSliceImagePam(const OrthancApiClient::BinaryResponseReadyMessage& message); - - void ParseSliceImageJpeg(const OrthancApiClient::JsonResponseReadyMessage& message); - - void ParseSliceRawImage(const OrthancApiClient::BinaryResponseReadyMessage& message); - - void ScheduleSliceImagePng(const Deprecated::Slice& slice, - size_t index); - - void ScheduleSliceImagePam(const Deprecated::Slice& slice, - size_t index); - - void ScheduleSliceImageJpeg(const Deprecated::Slice& slice, - size_t index, - SliceImageQuality quality); - - void SortAndFinalizeSlices(); - - public: - OrthancSlicesLoader(MessageBroker& broker, - //ISliceLoaderObserver& callback, - OrthancApiClient& orthancApi); - - void ScheduleLoadSeries(const std::string& seriesId); - - void ScheduleLoadInstance(const std::string& instanceId); - - void ScheduleLoadFrame(const std::string& instanceId, - unsigned int frame); - - bool IsGeometryReady() const; - - size_t GetSlicesCount() const; - - const Deprecated::Slice& GetSlice(size_t index) const; - - bool LookupSlice(size_t& index, - const CoordinateSystem3D& plane) const; - - void ScheduleLoadSliceImage(size_t index, - SliceImageQuality requestedQuality); - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/ParallelSlices.cpp --- a/Framework/Toolbox/ParallelSlices.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Toolbox/ParallelSlices.cpp Wed May 22 16:13:46 2019 +0200 @@ -22,6 +22,7 @@ #include "ParallelSlices.h" #include "GeometryToolbox.h" +#include "../Volumes/ImageBuffer3D.h" #include #include @@ -30,7 +31,7 @@ { ParallelSlices::ParallelSlices() { - LinearAlgebra::AssignVector(normal_, 0, 0, 1); + Clear(); } @@ -48,7 +49,7 @@ } - ParallelSlices::~ParallelSlices() + void ParallelSlices::Clear() { for (size_t i = 0; i < slices_.size(); i++) { @@ -58,6 +59,15 @@ slices_[i] = NULL; } } + + slices_.clear(); + LinearAlgebra::AssignVector(normal_, 0, 0, 1); + } + + + ParallelSlices::~ParallelSlices() + { + Clear(); } @@ -146,4 +156,60 @@ return reversed.release(); } + + + ParallelSlices* ParallelSlices::FromVolumeImage(const VolumeImageGeometry& geometry, + VolumeProjection projection) + { + const Vector dimensions = geometry.GetVoxelDimensions(VolumeProjection_Axial); + const CoordinateSystem3D& axial = geometry.GetAxialGeometry(); + + std::auto_ptr result(new ParallelSlices); + + switch (projection) + { + case VolumeProjection_Axial: + for (unsigned int z = 0; z < geometry.GetDepth(); z++) + { + Vector origin = axial.GetOrigin(); + origin += static_cast(z) * dimensions[2] * axial.GetNormal(); + + result->AddSlice(origin, + axial.GetAxisX(), + axial.GetAxisY()); + } + break; + + case VolumeProjection_Coronal: + for (unsigned int y = 0; y < geometry.GetHeight(); y++) + { + Vector origin = axial.GetOrigin(); + origin += static_cast(y) * dimensions[1] * axial.GetAxisY(); + origin += static_cast(geometry.GetDepth() - 1) * dimensions[2] * axial.GetNormal(); + + result->AddSlice(origin, + axial.GetAxisX(), + -axial.GetNormal()); + } + break; + + case VolumeProjection_Sagittal: + for (unsigned int x = 0; x < geometry.GetWidth(); x++) + { + Vector origin = axial.GetOrigin(); + origin += static_cast(x) * dimensions[0] * axial.GetAxisX(); + origin += static_cast(geometry.GetDepth() - 1) * dimensions[2] * axial.GetNormal(); + + result->AddSlice(origin, + axial.GetAxisY(), + -axial.GetNormal()); + } + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + return result.release(); + } } diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/ParallelSlices.h --- a/Framework/Toolbox/ParallelSlices.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Toolbox/ParallelSlices.h Wed May 22 16:13:46 2019 +0200 @@ -22,6 +22,7 @@ #pragma once #include "CoordinateSystem3D.h" +#include "../Volumes/VolumeImageGeometry.h" namespace OrthancStone { @@ -33,6 +34,8 @@ ParallelSlices& operator= (const ParallelSlices& other); // Forbidden + void Clear(); + public: ParallelSlices(); @@ -63,5 +66,8 @@ const Vector& origin) const; ParallelSlices* Reverse() const; + + static ParallelSlices* FromVolumeImage(const VolumeImageGeometry& geometry, + VolumeProjection projection); }; } diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/ShearWarpProjectiveTransform.cpp --- a/Framework/Toolbox/ShearWarpProjectiveTransform.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Toolbox/ShearWarpProjectiveTransform.cpp Wed May 22 16:13:46 2019 +0200 @@ -343,6 +343,7 @@ float& maxValue, const Matrix& M_view, const ImageBuffer3D& source, + const VolumeImageGeometry& geometry, double pixelSpacing, unsigned int countSlices, ImageInterpolation shearInterpolation, @@ -385,8 +386,8 @@ // Compute the "world" matrix that maps the source volume to the // (0,0,0)->(1,1,1) unit cube - Vector origin = source.GetGeometry().GetCoordinates(0, 0, 0); - Vector ps = source.GetGeometry().GetVoxelDimensions(VolumeProjection_Axial); + Vector origin = geometry.GetCoordinates(0, 0, 0); + Vector ps = geometry.GetVoxelDimensions(VolumeProjection_Axial); Matrix world = LinearAlgebra::Product( GeometryToolbox::CreateScalingMatrix(1.0 / ps[0], 1.0 / ps[1], 1.0 / ps[2]), GeometryToolbox::CreateTranslationMatrix(-origin[0], -origin[1], -origin[2])); @@ -598,6 +599,7 @@ float& maxValue, const Matrix& M_view, const ImageBuffer3D& source, + const VolumeImageGeometry& geometry, bool mip, double pixelSpacing, unsigned int countSlices, @@ -607,13 +609,13 @@ if (mip) { ApplyAxialInternal - (target, maxValue, M_view, source, pixelSpacing, + (target, maxValue, M_view, source, geometry, pixelSpacing, countSlices, shearInterpolation, warpInterpolation); } else { ApplyAxialInternal - (target, maxValue, M_view, source, pixelSpacing, + (target, maxValue, M_view, source, geometry, pixelSpacing, countSlices, shearInterpolation, warpInterpolation); } } @@ -623,6 +625,7 @@ ShearWarpProjectiveTransform::ApplyAxial(float& maxValue, const Matrix& M_view, const ImageBuffer3D& source, + const VolumeImageGeometry& geometry, Orthanc::PixelFormat targetFormat, unsigned int targetWidth, unsigned int targetHeight, @@ -640,7 +643,7 @@ { ApplyAxialInternal2 - (*target, maxValue, M_view, source, mip, pixelSpacing, + (*target, maxValue, M_view, source, geometry, mip, pixelSpacing, countSlices, shearInterpolation, warpInterpolation); } else if (source.GetFormat() == Orthanc::PixelFormat_SignedGrayscale16 && @@ -648,7 +651,7 @@ { ApplyAxialInternal2 - (*target, maxValue, M_view, source, mip, pixelSpacing, + (*target, maxValue, M_view, source, geometry, mip, pixelSpacing, countSlices, shearInterpolation, warpInterpolation); } else diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/ShearWarpProjectiveTransform.h --- a/Framework/Toolbox/ShearWarpProjectiveTransform.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Toolbox/ShearWarpProjectiveTransform.h Wed May 22 16:13:46 2019 +0200 @@ -92,6 +92,7 @@ static Orthanc::ImageAccessor* ApplyAxial(float& maxValue, const Matrix& M_view, // cf. "CalibrateView()" const ImageBuffer3D& source, + const VolumeImageGeometry& geometry, Orthanc::PixelFormat targetFormat, unsigned int targetWidth, unsigned int targetHeight, diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/Slice.cpp --- a/Framework/Toolbox/Slice.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,367 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "Slice.h" - -#include "../StoneEnumerations.h" -#include "GeometryToolbox.h" - -#include -#include -#include - -#include - -namespace Deprecated -{ - static bool ParseDouble(double& target, - const std::string& source) - { - try - { - target = boost::lexical_cast(source); - return true; - } - catch (boost::bad_lexical_cast&) - { - return false; - } - } - - Slice* Slice::Clone() const - { - std::auto_ptr target(new Slice()); - - target->type_ = type_; - target->orthancInstanceId_ = orthancInstanceId_; - target->sopClassUid_ = sopClassUid_; - target->frame_ = frame_; - target->frameCount_ = frameCount_; - target->geometry_ = geometry_; - target->pixelSpacingX_ = pixelSpacingX_; - target->pixelSpacingY_ = pixelSpacingY_; - target->thickness_ = thickness_; - target->width_ = width_; - target->height_ = height_; - target->converter_ = converter_; - if (imageInformation_.get() != NULL) - target->imageInformation_.reset(imageInformation_->Clone()); - - return target.release(); - } - - bool Slice::ComputeRTDoseGeometry(const Orthanc::DicomMap& dataset, - unsigned int frame) - { - // http://dicom.nema.org/medical/Dicom/2016a/output/chtml/part03/sect_C.8.8.3.2.html - - { - std::string increment; - - if (dataset.CopyToString(increment, Orthanc::DICOM_TAG_FRAME_INCREMENT_POINTER, false)) - { - Orthanc::Toolbox::ToUpperCase(increment); - if (increment != "3004,000C") // This is the "Grid Frame Offset Vector" tag - { - LOG(ERROR) << "Bad value for the \"FrameIncrementPointer\" tag"; - return false; - } - } - } - - std::string offsetTag; - - if (!dataset.CopyToString(offsetTag, Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR, false) || - offsetTag.empty()) - { - LOG(ERROR) << "Cannot read the \"GridFrameOffsetVector\" tag, check you are using Orthanc >= 1.3.1"; - return false; - } - - std::vector offsets; - Orthanc::Toolbox::TokenizeString(offsets, offsetTag, '\\'); - - if (frameCount_ <= 1 || - offsets.size() < frameCount_ || - offsets.size() < 2 || - frame >= frameCount_) - { - LOG(ERROR) << "No information about the 3D location of some slice(s) in a RT DOSE"; - return false; - } - - double offset0, offset1, z; - - if (!ParseDouble(offset0, offsets[0]) || - !ParseDouble(offset1, offsets[1]) || - !ParseDouble(z, offsets[frame])) - { - LOG(ERROR) << "Invalid syntax"; - return false; - } - - if (!OrthancStone::LinearAlgebra::IsCloseToZero(offset0)) - { - LOG(ERROR) << "Invalid syntax"; - return false; - } - - geometry_ = OrthancStone::CoordinateSystem3D(geometry_.GetOrigin() + z * geometry_.GetNormal(), - //+ 650 * geometry_.GetAxisX(), - geometry_.GetAxisX(), - geometry_.GetAxisY()); - - thickness_ = offset1 - offset0; - if (thickness_ < 0) - { - thickness_ = -thickness_; - } - - return true; - } - - - bool Slice::ParseOrthancFrame(const Orthanc::DicomMap& dataset, - const std::string& instanceId, - unsigned int frame) - { - orthancInstanceId_ = instanceId; - frame_ = frame; - type_ = Type_OrthancDecodableFrame; - imageInformation_.reset(new Orthanc::DicomImageInformation(dataset)); - - if (!dataset.CopyToString(sopClassUid_, Orthanc::DICOM_TAG_SOP_CLASS_UID, false) || - sopClassUid_.empty()) - { - LOG(ERROR) << "Instance without a SOP class UID"; - return false; - } - - if (!dataset.ParseUnsignedInteger32(frameCount_, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES)) - { - frameCount_ = 1; // Assume instance with one frame - } - - if (frame >= frameCount_) - { - return false; - } - - if (!dataset.ParseUnsignedInteger32(width_, Orthanc::DICOM_TAG_COLUMNS) || - !dataset.ParseUnsignedInteger32(height_, Orthanc::DICOM_TAG_ROWS)) - { - return false; - } - - thickness_ = 100.0 * std::numeric_limits::epsilon(); - - std::string tmp; - if (dataset.CopyToString(tmp, Orthanc::DICOM_TAG_SLICE_THICKNESS, false)) - { - if (!tmp.empty() && - !ParseDouble(thickness_, tmp)) - { - return false; // Syntax error - } - } - - converter_.ReadParameters(dataset); - - OrthancStone::GeometryToolbox::GetPixelSpacing(pixelSpacingX_, pixelSpacingY_, dataset); - - std::string position, orientation; - if (dataset.CopyToString(position, Orthanc::DICOM_TAG_IMAGE_POSITION_PATIENT, false) && - dataset.CopyToString(orientation, Orthanc::DICOM_TAG_IMAGE_ORIENTATION_PATIENT, false)) - { - geometry_ = OrthancStone::CoordinateSystem3D(position, orientation); - - bool ok = true; - - switch (OrthancStone::StringToSopClassUid(sopClassUid_)) - { - case OrthancStone::SopClassUid_RTDose: - type_ = Type_OrthancRawFrame; - ok = ComputeRTDoseGeometry(dataset, frame); - break; - - default: - break; - } - - if (!ok) - { - LOG(ERROR) << "Cannot deduce the 3D location of frame " << frame - << " in instance " << instanceId << ", whose SOP class UID is: " << sopClassUid_; - return false; - } - } - - return true; - } - - - const std::string Slice::GetOrthancInstanceId() const - { - if (type_ == Type_OrthancDecodableFrame || - type_ == Type_OrthancRawFrame) - { - return orthancInstanceId_; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - } - - - unsigned int Slice::GetFrame() const - { - if (type_ == Type_Invalid) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return frame_; - } - - - const OrthancStone::CoordinateSystem3D& Slice::GetGeometry() const - { - if (type_ == Type_Invalid) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return geometry_; - } - - - double Slice::GetThickness() const - { - if (type_ == Type_Invalid) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return thickness_; - } - - - double Slice::GetPixelSpacingX() const - { - if (type_ == Type_Invalid) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return pixelSpacingX_; - } - - - double Slice::GetPixelSpacingY() const - { - if (type_ == Type_Invalid) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return pixelSpacingY_; - } - - - unsigned int Slice::GetWidth() const - { - if (type_ == Type_Invalid) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return width_; - } - - - unsigned int Slice::GetHeight() const - { - if (type_ == Type_Invalid) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return height_; - } - - - const DicomFrameConverter& Slice::GetConverter() const - { - if (type_ == Type_Invalid) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return converter_; - } - - - bool Slice::ContainsPlane(const OrthancStone::CoordinateSystem3D& plane) const - { - if (type_ == Type_Invalid) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - bool opposite; - return (OrthancStone::GeometryToolbox::IsParallelOrOpposite(opposite, - GetGeometry().GetNormal(), - plane.GetNormal()) && - OrthancStone::LinearAlgebra::IsNear(GetGeometry().ProjectAlongNormal(GetGeometry().GetOrigin()), - GetGeometry().ProjectAlongNormal(plane.GetOrigin()), - thickness_ / 2.0)); - } - - - void Slice::GetExtent(std::vector& points) const - { - double sx = GetPixelSpacingX(); - double sy = GetPixelSpacingY(); - double w = static_cast(GetWidth()); - double h = static_cast(GetHeight()); - - points.clear(); - points.push_back(GetGeometry().MapSliceToWorldCoordinates(-0.5 * sx, -0.5 * sy)); - points.push_back(GetGeometry().MapSliceToWorldCoordinates((w - 0.5) * sx, -0.5 * sy)); - points.push_back(GetGeometry().MapSliceToWorldCoordinates(-0.5 * sx, (h - 0.5) * sy)); - points.push_back(GetGeometry().MapSliceToWorldCoordinates((w - 0.5) * sx, (h - 0.5) * sy)); - } - - - const Orthanc::DicomImageInformation& Slice::GetImageInformation() const - { - if (imageInformation_.get() == NULL) - { - // Only available if constructing the "Slice" object with a DICOM map - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else - { - return *imageInformation_; - } - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/Slice.h --- a/Framework/Toolbox/Slice.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,155 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "CoordinateSystem3D.h" -#include "DicomFrameConverter.h" - -#include -#include - -namespace Deprecated -{ - // TODO - Remove this class - class Slice : - public Orthanc::IDynamicObject /* to be used as a payload of SlicesSorter */ - { - private: - enum Type - { - Type_Invalid, - Type_Standalone, - Type_OrthancDecodableFrame, - Type_OrthancRawFrame - // TODO A slice could come from some DICOM file (URL) - }; - - bool ComputeRTDoseGeometry(const Orthanc::DicomMap& dataset, - unsigned int frame); - - Type type_; - std::string orthancInstanceId_; - std::string sopClassUid_; - unsigned int frame_; - unsigned int frameCount_; // TODO : Redundant with "imageInformation_" - OrthancStone::CoordinateSystem3D geometry_; - double pixelSpacingX_; - double pixelSpacingY_; - double thickness_; - unsigned int width_; // TODO : Redundant with "imageInformation_" - unsigned int height_; // TODO : Redundant with "imageInformation_" - DicomFrameConverter converter_; // TODO : Partially redundant with "imageInformation_" - - std::auto_ptr imageInformation_; - - public: - Slice() : - type_(Type_Invalid) - { - } - - - // this constructor is used to reference, i.e, a slice that is being loaded - Slice(const std::string& orthancInstanceId, - unsigned int frame) : - type_(Type_Invalid), - orthancInstanceId_(orthancInstanceId), - frame_(frame) - { - } - - // TODO Is this constructor the best way to go to tackle missing - // layers within SliceViewerWidget? - Slice(const OrthancStone::CoordinateSystem3D& plane, - double thickness) : - type_(Type_Standalone), - frame_(0), - frameCount_(0), - geometry_(plane), - pixelSpacingX_(1), - pixelSpacingY_(1), - thickness_(thickness), - width_(0), - height_(0) - { - } - - Slice(const OrthancStone::CoordinateSystem3D& plane, - double pixelSpacingX, - double pixelSpacingY, - double thickness, - unsigned int width, - unsigned int height, - const DicomFrameConverter& converter) : - type_(Type_Standalone), - frameCount_(1), - geometry_(plane), - pixelSpacingX_(pixelSpacingX), - pixelSpacingY_(pixelSpacingY), - thickness_(thickness), - width_(width), - height_(height), - converter_(converter) - { - } - - bool IsValid() const - { - return type_ != Type_Invalid; - } - - bool ParseOrthancFrame(const Orthanc::DicomMap& dataset, - const std::string& instanceId, - unsigned int frame); - - bool HasOrthancDecoding() const - { - return type_ == Type_OrthancDecodableFrame; - } - - const std::string GetOrthancInstanceId() const; - - unsigned int GetFrame() const; - - const OrthancStone::CoordinateSystem3D& GetGeometry() const; - - double GetThickness() const; - - double GetPixelSpacingX() const; - - double GetPixelSpacingY() const; - - unsigned int GetWidth() const; - - unsigned int GetHeight() const; - - const DicomFrameConverter& GetConverter() const; - - bool ContainsPlane(const OrthancStone::CoordinateSystem3D& plane) const; - - void GetExtent(std::vector& points) const; - - const Orthanc::DicomImageInformation& GetImageInformation() const; - - Slice* Clone() const; - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/ViewportGeometry.cpp --- a/Framework/Toolbox/ViewportGeometry.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,217 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "ViewportGeometry.h" - -#include -#include - -#include - -namespace OrthancStone -{ - void ViewportGeometry::ComputeTransform() - { - // The following lines must be read in reverse order! - cairo_matrix_t tmp; - - // Bring the center of the scene to the center of the view - cairo_matrix_init_translate(&transform_, - panX_ + static_cast(width_) / 2.0, - panY_ + static_cast(height_) / 2.0); - - // Apply the zoom around (0,0) - cairo_matrix_init_scale(&tmp, zoom_, zoom_); - cairo_matrix_multiply(&transform_, &tmp, &transform_); - - // Bring the center of the scene to (0,0) - cairo_matrix_init_translate(&tmp, - -(sceneExtent_.GetX1() + sceneExtent_.GetX2()) / 2.0, - -(sceneExtent_.GetY1() + sceneExtent_.GetY2()) / 2.0); - cairo_matrix_multiply(&transform_, &tmp, &transform_); - } - - - ViewportGeometry::ViewportGeometry() - { - width_ = 0; - height_ = 0; - - zoom_ = 1; - panX_ = 0; - panY_ = 0; - - ComputeTransform(); - } - - - void ViewportGeometry::SetDisplaySize(unsigned int width, - unsigned int height) - { - if (width_ != width || - height_ != height) - { - LOG(INFO) << "New display size: " << width << "x" << height; - - width_ = width; - height_ = height; - - ComputeTransform(); - } - } - - - void ViewportGeometry::SetSceneExtent(const Extent2D& extent) - { - LOG(INFO) << "New scene extent: (" - << extent.GetX1() << "," << extent.GetY1() << ") => (" - << extent.GetX2() << "," << extent.GetY2() << ")"; - - sceneExtent_ = extent; - ComputeTransform(); - } - - - void ViewportGeometry::MapDisplayToScene(double& sceneX /* out */, - double& sceneY /* out */, - double x, - double y) const - { - cairo_matrix_t transform = transform_; - - if (cairo_matrix_invert(&transform) != CAIRO_STATUS_SUCCESS) - { - LOG(ERROR) << "Cannot invert singular matrix"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - sceneX = x; - sceneY = y; - cairo_matrix_transform_point(&transform, &sceneX, &sceneY); - } - - - void ViewportGeometry::MapSceneToDisplay(int& displayX /* out */, - int& displayY /* out */, - double x, - double y) const - { - cairo_matrix_transform_point(&transform_, &x, &y); - - displayX = static_cast(boost::math::iround(x)); - displayY = static_cast(boost::math::iround(y)); - } - - - void ViewportGeometry::MapPixelCenterToScene(std::vector& sceneTouches /* out */, - const std::vector& displayTouches) const - { - double sceneX, sceneY; - sceneTouches.clear(); - for (size_t t = 0; t < displayTouches.size(); t++) - { - MapPixelCenterToScene( - sceneX, - sceneY, - static_cast(displayTouches[t].x), - static_cast(displayTouches[t].y)); - - sceneTouches.push_back(Touch((float)sceneX, (float)sceneY)); - } - } - - void ViewportGeometry::MapPixelCenterToScene(double& sceneX, - double& sceneY, - int x, - int y) const - { - // Take the center of the pixel - MapDisplayToScene(sceneX, sceneY, - static_cast(x) + 0.5, - static_cast(y) + 0.5); - } - - - void ViewportGeometry::FitContent() - { - if (width_ > 0 && - height_ > 0 && - !sceneExtent_.IsEmpty()) - { - double zoomX = static_cast(width_) / (sceneExtent_.GetX2() - sceneExtent_.GetX1()); - double zoomY = static_cast(height_) / (sceneExtent_.GetY2() - sceneExtent_.GetY1()); - zoom_ = zoomX < zoomY ? zoomX : zoomY; - - panX_ = 0; - panY_ = 0; - - ComputeTransform(); - } - } - - - void ViewportGeometry::ApplyTransform(CairoContext& context) const - { - cairo_set_matrix(context.GetObject(), &transform_); - } - - - void ViewportGeometry::GetPan(double& x, - double& y) const - { - x = panX_; - y = panY_; - } - - - void ViewportGeometry::SetPan(double x, - double y) - { - panX_ = x; - panY_ = y; - ComputeTransform(); - } - - - void ViewportGeometry::SetZoom(double zoom) - { - zoom_ = zoom; - ComputeTransform(); - } - - - Matrix ViewportGeometry::GetMatrix() const - { - Matrix m(3, 3); - - m(0, 0) = transform_.xx; - m(0, 1) = transform_.xy; - m(0, 2) = transform_.x0; - m(1, 0) = transform_.yx; - m(1, 1) = transform_.yy; - m(1, 2) = transform_.y0; - m(2, 0) = 0; - m(2, 1) = 0; - m(2, 2) = 1; - - return m; - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/ViewportGeometry.h --- a/Framework/Toolbox/ViewportGeometry.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,110 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../Viewport/CairoContext.h" -#include "Extent2D.h" -#include "LinearAlgebra.h" -#include "../Viewport/IMouseTracker.h" // to include "Touch" definition - -namespace OrthancStone -{ - class ViewportGeometry - { - private: - // Extent of the scene (in world units) - Extent2D sceneExtent_; - - // Size of the display (in pixels) - unsigned int width_; - unsigned int height_; - - // Zoom/pan - double zoom_; - double panX_; // In pixels (display units) - double panY_; - - cairo_matrix_t transform_; // Scene-to-display transformation - - void ComputeTransform(); - - public: - ViewportGeometry(); - - void SetDisplaySize(unsigned int width, - unsigned int height); - - void SetSceneExtent(const Extent2D& extent); - - const Extent2D& GetSceneExtent() const - { - return sceneExtent_; - } - - void MapDisplayToScene(double& sceneX /* out */, - double& sceneY /* out */, - double x, - double y) const; - - void MapPixelCenterToScene(double& sceneX /* out */, - double& sceneY /* out */, - int x, - int y) const; - - void MapPixelCenterToScene(std::vector& sceneTouches /* out */, - const std::vector& displayTouches) const; - - void MapSceneToDisplay(int& displayX /* out */, - int& displayY /* out */, - double x, - double y) const; - - unsigned int GetDisplayWidth() const - { - return width_; - } - - unsigned int GetDisplayHeight() const - { - return height_; - } - - double GetZoom() const - { - return zoom_; - } - - void FitContent(); - - void ApplyTransform(CairoContext& context) const; - - void GetPan(double& x, - double& y) const; - - void SetPan(double x, - double y); - - void SetZoom(double zoom); - - Matrix GetMatrix() const; - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/VolumeImageGeometry.cpp --- a/Framework/Toolbox/VolumeImageGeometry.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,309 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "VolumeImageGeometry.h" - -#include "../Toolbox/GeometryToolbox.h" - -#include - - -namespace OrthancStone -{ - void VolumeImageGeometry::Invalidate() - { - Vector p = (axialGeometry_.GetOrigin() + - static_cast(depth_ - 1) * voxelDimensions_[2] * axialGeometry_.GetNormal()); - - coronalGeometry_ = CoordinateSystem3D(p, - axialGeometry_.GetAxisX(), - -axialGeometry_.GetNormal()); - - sagittalGeometry_ = CoordinateSystem3D(p, - axialGeometry_.GetAxisY(), - axialGeometry_.GetNormal()); - - Vector origin = ( - axialGeometry_.MapSliceToWorldCoordinates(-0.5 * voxelDimensions_[0], - -0.5 * voxelDimensions_[1]) - - 0.5 * voxelDimensions_[2] * axialGeometry_.GetNormal()); - - Vector scaling; - - if (width_ == 0 || - height_ == 0 || - depth_ == 0) - { - LinearAlgebra::AssignVector(scaling, 1, 1, 1); - } - else - { - scaling = ( - axialGeometry_.GetAxisX() * voxelDimensions_[0] * static_cast(width_) + - axialGeometry_.GetAxisY() * voxelDimensions_[1] * static_cast(height_) + - axialGeometry_.GetNormal() * voxelDimensions_[2] * static_cast(depth_)); - } - - transform_ = LinearAlgebra::Product( - GeometryToolbox::CreateTranslationMatrix(origin[0], origin[1], origin[2]), - GeometryToolbox::CreateScalingMatrix(scaling[0], scaling[1], scaling[2])); - - LinearAlgebra::InvertMatrix(transformInverse_, transform_); - } - - - VolumeImageGeometry::VolumeImageGeometry() : - width_(0), - height_(0), - depth_(0) - { - LinearAlgebra::AssignVector(voxelDimensions_, 1, 1, 1); - Invalidate(); - } - - - void VolumeImageGeometry::SetSize(unsigned int width, - unsigned int height, - unsigned int depth) - { - width_ = width; - height_ = height; - depth_ = depth; - Invalidate(); - } - - - void VolumeImageGeometry::SetAxialGeometry(const CoordinateSystem3D& geometry) - { - axialGeometry_ = geometry; - Invalidate(); - } - - - void VolumeImageGeometry::SetVoxelDimensions(double x, - double y, - double z) - { - if (x <= 0 || - y <= 0 || - z <= 0) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - else - { - LinearAlgebra::AssignVector(voxelDimensions_, x, y, z); - Invalidate(); - } - } - - - const CoordinateSystem3D& VolumeImageGeometry::GetProjectionGeometry(VolumeProjection projection) const - { - switch (projection) - { - case VolumeProjection_Axial: - return axialGeometry_; - - case VolumeProjection_Coronal: - return coronalGeometry_; - - case VolumeProjection_Sagittal: - return sagittalGeometry_; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - } - - - Vector VolumeImageGeometry::GetVoxelDimensions(VolumeProjection projection) const - { - switch (projection) - { - case VolumeProjection_Axial: - return voxelDimensions_; - - case VolumeProjection_Coronal: - return LinearAlgebra::CreateVector(voxelDimensions_[0], voxelDimensions_[2], voxelDimensions_[1]); - - case VolumeProjection_Sagittal: - return LinearAlgebra::CreateVector(voxelDimensions_[1], voxelDimensions_[2], voxelDimensions_[0]); - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - } - - - unsigned int VolumeImageGeometry::GetProjectionWidth(VolumeProjection projection) const - { - switch (projection) - { - case VolumeProjection_Axial: - return width_; - - case VolumeProjection_Coronal: - return width_; - - case VolumeProjection_Sagittal: - return height_; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - } - - - unsigned int VolumeImageGeometry::GetProjectionHeight(VolumeProjection projection) const - { - switch (projection) - { - case VolumeProjection_Axial: - return height_; - - case VolumeProjection_Coronal: - return depth_; - - case VolumeProjection_Sagittal: - return depth_; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - } - - - unsigned int VolumeImageGeometry::GetProjectionDepth(VolumeProjection projection) const - { - switch (projection) - { - case VolumeProjection_Axial: - return depth_; - - case VolumeProjection_Coronal: - return height_; - - case VolumeProjection_Sagittal: - return width_; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - } - - - Vector VolumeImageGeometry::GetCoordinates(float x, - float y, - float z) const - { - Vector p = LinearAlgebra::Product(transform_, LinearAlgebra::CreateVector(x, y, z, 1)); - - assert(LinearAlgebra::IsNear(p[3], 1)); // Affine transform, no perspective effect - - // Back to non-homogeneous coordinates - return LinearAlgebra::CreateVector(p[0], p[1], p[2]); - } - - - bool VolumeImageGeometry::DetectProjection(VolumeProjection& projection, - const Vector& planeNormal) const - { - if (GeometryToolbox::IsParallel(planeNormal, axialGeometry_.GetNormal())) - { - projection = VolumeProjection_Axial; - return true; - } - else if (GeometryToolbox::IsParallel(planeNormal, coronalGeometry_.GetNormal())) - { - projection = VolumeProjection_Coronal; - return true; - } - else if (GeometryToolbox::IsParallel(planeNormal, sagittalGeometry_.GetNormal())) - { - projection = VolumeProjection_Sagittal; - return true; - } - else - { - return false; - } - } - - - bool VolumeImageGeometry::DetectSlice(VolumeProjection& projection, - unsigned int& slice, - const CoordinateSystem3D& plane) const - { - if (!DetectProjection(projection, plane.GetNormal())) - { - return false; - } - - // Transforms the coordinates of the origin of the plane, into the - // coordinates of the axial geometry - const Vector& origin = plane.GetOrigin(); - Vector p = LinearAlgebra::Product( - transformInverse_, - LinearAlgebra::CreateVector(origin[0], origin[1], origin[2], 1)); - - assert(LinearAlgebra::IsNear(p[3], 1)); - - double z; - - switch (projection) - { - case VolumeProjection_Axial: - z = p[2]; - break; - - case VolumeProjection_Coronal: - z = p[1]; - break; - - case VolumeProjection_Sagittal: - z = p[0]; - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - const unsigned int projectionDepth = GetProjectionDepth(projection); - - z *= static_cast(projectionDepth); - if (z < 0) - { - return false; - } - - unsigned int d = static_cast(std::floor(z)); - if (d >= projectionDepth) - { - return false; - } - else - { - slice = d; - return true; - } - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Toolbox/VolumeImageGeometry.h --- a/Framework/Toolbox/VolumeImageGeometry.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,122 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../StoneEnumerations.h" -#include "CoordinateSystem3D.h" - -namespace OrthancStone -{ - class VolumeImageGeometry - { - private: - unsigned int width_; - unsigned int height_; - unsigned int depth_; - CoordinateSystem3D axialGeometry_; - CoordinateSystem3D coronalGeometry_; - CoordinateSystem3D sagittalGeometry_; - Vector voxelDimensions_; - Matrix transform_; - Matrix transformInverse_; - - void Invalidate(); - - public: - VolumeImageGeometry(); - - unsigned int GetWidth() const - { - return width_; - } - - unsigned int GetHeight() const - { - return height_; - } - - unsigned int GetDepth() const - { - return depth_; - } - - const CoordinateSystem3D& GetAxialGeometry() const - { - return axialGeometry_; - } - - const CoordinateSystem3D& GetCoronalGeometry() const - { - return coronalGeometry_; - } - - const CoordinateSystem3D& GetSagittalGeometry() const - { - return sagittalGeometry_; - } - - const CoordinateSystem3D& GetProjectionGeometry(VolumeProjection projection) const; - - const Matrix& GetTransform() const - { - return transform_; - } - - const Matrix& GetTransformInverse() const - { - return transformInverse_; - } - - void SetSize(unsigned int width, - unsigned int height, - unsigned int depth); - - // Set the geometry of the first axial slice (i.e. the one whose - // depth == 0) - void SetAxialGeometry(const CoordinateSystem3D& geometry); - - void SetVoxelDimensions(double x, - double y, - double z); - - Vector GetVoxelDimensions(VolumeProjection projection) const; - - unsigned int GetProjectionWidth(VolumeProjection projection) const; - - unsigned int GetProjectionHeight(VolumeProjection projection) const; - - unsigned int GetProjectionDepth(VolumeProjection projection) const; - - // Get the 3D position of a point in the volume, where x, y and z - // lie in the [0;1] range - Vector GetCoordinates(float x, - float y, - float z) const; - - bool DetectProjection(VolumeProjection& projection, - const Vector& planeNormal) const; - - bool DetectSlice(VolumeProjection& projection, - unsigned int& slice, - const CoordinateSystem3D& plane) const; - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Viewport/IMouseTracker.h --- a/Framework/Viewport/IMouseTracker.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "CairoSurface.h" -#include - -namespace OrthancStone -{ - struct Touch - { - float x; - float y; - - Touch(float x, float y) - : x(x), - y(y) - { - } - Touch() - : x(0.0f), - y(0.0f) - { - } - }; - - - // this is tracking a mouse in screen coordinates/pixels unlike - // the IWorldSceneMouseTracker that is tracking a mouse - // in scene coordinates/mm. - class IMouseTracker : public boost::noncopyable - { - public: - virtual ~IMouseTracker() - { - } - - virtual void Render(Orthanc::ImageAccessor& surface) = 0; - - virtual void MouseUp() = 0; - - // Returns "true" iff. the background scene must be repainted - virtual void MouseMove(int x, - int y, - const std::vector& displayTouches) = 0; - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Viewport/IStatusBar.h --- a/Framework/Viewport/IStatusBar.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include -#include - -namespace OrthancStone -{ - class IStatusBar : public boost::noncopyable - { - public: - virtual ~IStatusBar() - { - } - - virtual void ClearMessage() = 0; - - virtual void SetMessage(const std::string& message) = 0; - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Viewport/IViewport.h --- a/Framework/Viewport/IViewport.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,95 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "IStatusBar.h" -#include "../StoneEnumerations.h" -#include "../Messages/IObservable.h" - -#include -#include "../Viewport/IMouseTracker.h" // only to get the "Touch" definition - -namespace OrthancStone -{ - class IWidget; // Forward declaration - - class IViewport : public IObservable - { - public: - ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ViewportChangedMessage, IViewport); - - IViewport(MessageBroker& broker) : - IObservable(broker) - { - } - - virtual ~IViewport() - { - } - - virtual void FitContent() = 0; - - virtual void SetStatusBar(IStatusBar& statusBar) = 0; - - virtual void SetSize(unsigned int width, - unsigned int height) = 0; - - // The function returns "true" iff. a new frame was rendered - virtual bool Render(Orthanc::ImageAccessor& surface) = 0; - - virtual void MouseDown(MouseButton button, - int x, - int y, - KeyboardModifiers modifiers, - const std::vector& touches) = 0; - - virtual void MouseUp() = 0; - - virtual void MouseMove(int x, - int y, - const std::vector& displayTouches) = 0; - - virtual void MouseEnter() = 0; - - virtual void MouseLeave() = 0; - - virtual void MouseWheel(MouseWheelDirection direction, - int x, - int y, - KeyboardModifiers modifiers) = 0; - - virtual void KeyPressed(KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers) = 0; - - virtual bool HasAnimation() = 0; - - virtual void DoAnimation() = 0; - - // Should only be called from IWidget - // TODO Why should this be virtual? - virtual void NotifyContentChanged() - { - BroadcastMessage(ViewportChangedMessage(*this)); - } - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Viewport/WidgetViewport.cpp --- a/Framework/Viewport/WidgetViewport.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,289 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "WidgetViewport.h" - -#include -#include - -namespace OrthancStone -{ - WidgetViewport::WidgetViewport(MessageBroker& broker) : - IViewport(broker), - statusBar_(NULL), - isMouseOver_(false), - lastMouseX_(0), - lastMouseY_(0), - backgroundChanged_(false) - { - } - - - void WidgetViewport::FitContent() - { - if (centralWidget_.get() != NULL) - { - centralWidget_->FitContent(); - } - } - - - void WidgetViewport::SetStatusBar(IStatusBar& statusBar) - { - statusBar_ = &statusBar; - - if (centralWidget_.get() != NULL) - { - centralWidget_->SetStatusBar(statusBar); - } - } - - - IWidget& WidgetViewport::SetCentralWidget(IWidget* widget) - { - if (widget == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - mouseTracker_.reset(NULL); - - centralWidget_.reset(widget); - centralWidget_->SetViewport(*this); - - if (statusBar_ != NULL) - { - centralWidget_->SetStatusBar(*statusBar_); - } - - NotifyBackgroundChanged(); - - return *widget; - } - - - void WidgetViewport::NotifyBackgroundChanged() - { - backgroundChanged_ = true; - NotifyContentChanged(); - } - - - void WidgetViewport::SetSize(unsigned int width, - unsigned int height) - { - background_.SetSize(width, height, false /* no alpha */); - - if (centralWidget_.get() != NULL) - { - centralWidget_->SetSize(width, height); - } - - NotifyBackgroundChanged(); - } - - - bool WidgetViewport::Render(Orthanc::ImageAccessor& surface) - { - if (centralWidget_.get() == NULL) - { - return false; - } - - Orthanc::ImageAccessor background; - background_.GetWriteableAccessor(background); - - if (backgroundChanged_ && - !centralWidget_->Render(background)) - { - return false; - } - - if (background.GetWidth() != surface.GetWidth() || - background.GetHeight() != surface.GetHeight()) - { - return false; - } - - Orthanc::ImageProcessing::Convert(surface, background); - - if (mouseTracker_.get() != NULL) - { - mouseTracker_->Render(surface); - } - else if (isMouseOver_) - { - centralWidget_->RenderMouseOver(surface, lastMouseX_, lastMouseY_); - } - - return true; - } - - void WidgetViewport::TouchStart(const std::vector& displayTouches) - { - MouseDown(MouseButton_Left, (int)displayTouches[0].x, (int)displayTouches[0].y, KeyboardModifiers_None, displayTouches); // one touch is equivalent to a mouse tracker without left button -> set the mouse coordinates to the first touch coordinates - } - - void WidgetViewport::TouchMove(const std::vector& displayTouches) - { - MouseMove((int)displayTouches[0].x, (int)displayTouches[0].y, displayTouches); // one touch is equivalent to a mouse tracker without left button -> set the mouse coordinates to the first touch coordinates - } - - void WidgetViewport::TouchEnd(const std::vector& displayTouches) - { - // note: TouchEnd is not triggered when a single touch gesture ends (it is only triggered when - // going from 2 touches to 1 touch, ...) - MouseUp(); - } - - void WidgetViewport::MouseDown(MouseButton button, - int x, - int y, - KeyboardModifiers modifiers, - const std::vector& displayTouches - ) - { - lastMouseX_ = x; - lastMouseY_ = y; - - if (centralWidget_.get() != NULL) - { - mouseTracker_.reset(centralWidget_->CreateMouseTracker(button, x, y, modifiers, displayTouches)); - } - else - { - mouseTracker_.reset(NULL); - } - - NotifyContentChanged(); - } - - - void WidgetViewport::MouseUp() - { - if (mouseTracker_.get() != NULL) - { - mouseTracker_->MouseUp(); - mouseTracker_.reset(NULL); - NotifyContentChanged(); - } - } - - - void WidgetViewport::MouseMove(int x, - int y, - const std::vector& displayTouches) - { - if (centralWidget_.get() == NULL) - { - return; - } - - lastMouseX_ = x; - lastMouseY_ = y; - - bool repaint = false; - - if (mouseTracker_.get() != NULL) - { - mouseTracker_->MouseMove(x, y, displayTouches); - repaint = true; - } - else - { - repaint = centralWidget_->HasRenderMouseOver(); - } - - if (repaint) - { - // The scene must be repainted, notify the observers - NotifyContentChanged(); - } - } - - - void WidgetViewport::MouseEnter() - { - isMouseOver_ = true; - NotifyContentChanged(); - } - - - void WidgetViewport::MouseLeave() - { - isMouseOver_ = false; - - if (mouseTracker_.get() != NULL) - { - mouseTracker_->MouseUp(); - mouseTracker_.reset(NULL); - } - - NotifyContentChanged(); - } - - - void WidgetViewport::MouseWheel(MouseWheelDirection direction, - int x, - int y, - KeyboardModifiers modifiers) - { - if (centralWidget_.get() != NULL && - mouseTracker_.get() == NULL) - { - centralWidget_->MouseWheel(direction, x, y, modifiers); - } - } - - - void WidgetViewport::KeyPressed(KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers) - { - if (centralWidget_.get() != NULL && - mouseTracker_.get() == NULL) - { - centralWidget_->KeyPressed(key, keyChar, modifiers); - } - } - - - bool WidgetViewport::HasAnimation() - { - if (centralWidget_.get() != NULL) - { - return centralWidget_->HasAnimation(); - } - else - { - return false; - } - } - - - void WidgetViewport::DoAnimation() - { - if (centralWidget_.get() != NULL) - { - centralWidget_->DoAnimation(); - } - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Viewport/WidgetViewport.h --- a/Framework/Viewport/WidgetViewport.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,94 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "IViewport.h" -#include "../Widgets/IWidget.h" - -#include - -namespace OrthancStone -{ - class WidgetViewport : public IViewport - { - private: - std::auto_ptr centralWidget_; - IStatusBar* statusBar_; - std::auto_ptr mouseTracker_; - bool isMouseOver_; - int lastMouseX_; - int lastMouseY_; - CairoSurface background_; - bool backgroundChanged_; - - public: - WidgetViewport(MessageBroker& broker); - - virtual void FitContent(); - - virtual void SetStatusBar(IStatusBar& statusBar); - - IWidget& SetCentralWidget(IWidget* widget); // Takes ownership - - virtual void NotifyBackgroundChanged(); - - virtual void SetSize(unsigned int width, - unsigned int height); - - virtual bool Render(Orthanc::ImageAccessor& surface); - - virtual void MouseDown(MouseButton button, - int x, - int y, - KeyboardModifiers modifiers, - const std::vector& displayTouches); - - virtual void MouseUp(); - - virtual void MouseMove(int x, - int y, - const std::vector& displayTouches); - - virtual void MouseEnter(); - - virtual void MouseLeave(); - - virtual void TouchStart(const std::vector& touches); - - virtual void TouchMove(const std::vector& touches); - - virtual void TouchEnd(const std::vector& touches); - - virtual void MouseWheel(MouseWheelDirection direction, - int x, - int y, - KeyboardModifiers modifiers); - - virtual void KeyPressed(KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers); - - virtual bool HasAnimation(); - - virtual void DoAnimation(); - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Volumes/ISlicedVolume.h --- a/Framework/Volumes/ISlicedVolume.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../Messages/IObservable.h" -#include "../Toolbox/Slice.h" - -namespace OrthancStone -{ - class ISlicedVolume : public IObservable - { - public: - ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentChangedMessage, ISlicedVolume); - ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryErrorMessage, ISlicedVolume); - ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryReadyMessage, ISlicedVolume); - ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, VolumeReadyMessage, ISlicedVolume); - - - class SliceContentChangedMessage : public OriginMessage - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - size_t sliceIndex_; - const Deprecated::Slice& slice_; - - public: - SliceContentChangedMessage(ISlicedVolume& origin, - size_t sliceIndex, - const Deprecated::Slice& slice) : - OriginMessage(origin), - sliceIndex_(sliceIndex), - slice_(slice) - { - } - - size_t GetSliceIndex() const - { - return sliceIndex_; - } - - const Deprecated::Slice& GetSlice() const - { - return slice_; - } - }; - - - ISlicedVolume(MessageBroker& broker) : - IObservable(broker) - { - } - - virtual size_t GetSliceCount() const = 0; - - virtual const Deprecated::Slice& GetSlice(size_t slice) const = 0; - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Volumes/IVolumeLoader.h --- a/Framework/Volumes/IVolumeLoader.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../Messages/IObservable.h" - -namespace OrthancStone -{ - class IVolumeLoader : public IObservable - { - public: - ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryReadyMessage, IVolumeLoader); - ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryErrorMessage, IVolumeLoader); - ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentChangedMessage, IVolumeLoader); - - IVolumeLoader(MessageBroker& broker) : - IObservable(broker) - { - } - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Volumes/ImageBuffer3D.cpp --- a/Framework/Volumes/ImageBuffer3D.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Volumes/ImageBuffer3D.cpp Wed May 22 16:13:46 2019 +0200 @@ -21,8 +21,6 @@ #include "ImageBuffer3D.h" -#include "../Toolbox/GeometryToolbox.h" - #include #include #include @@ -118,8 +116,6 @@ computeRange_(computeRange), hasRange_(false) { - geometry_.SetSize(width, height, depth); - LOG(INFO) << "Created a 3D image of size " << width << "x" << height << "x" << depth << " in " << Orthanc::EnumerationToString(format) << " (" << (GetEstimatedMemorySize() / (1024ll * 1024ll)) << "MB)"; @@ -132,63 +128,6 @@ } - - - ParallelSlices* ImageBuffer3D::GetGeometry(VolumeProjection projection) const - { - const Vector dimensions = geometry_.GetVoxelDimensions(VolumeProjection_Axial); - const CoordinateSystem3D& axial = geometry_.GetAxialGeometry(); - - std::auto_ptr result(new ParallelSlices); - - switch (projection) - { - case VolumeProjection_Axial: - for (unsigned int z = 0; z < depth_; z++) - { - Vector origin = axial.GetOrigin(); - origin += static_cast(z) * dimensions[2] * axial.GetNormal(); - - result->AddSlice(origin, - axial.GetAxisX(), - axial.GetAxisY()); - } - break; - - case VolumeProjection_Coronal: - for (unsigned int y = 0; y < height_; y++) - { - Vector origin = axial.GetOrigin(); - origin += static_cast(y) * dimensions[1] * axial.GetAxisY(); - origin += static_cast(depth_ - 1) * dimensions[2] * axial.GetNormal(); - - result->AddSlice(origin, - axial.GetAxisX(), - -axial.GetNormal()); - } - break; - - case VolumeProjection_Sagittal: - for (unsigned int x = 0; x < width_; x++) - { - Vector origin = axial.GetOrigin(); - origin += static_cast(x) * dimensions[0] * axial.GetAxisX(); - origin += static_cast(depth_ - 1) * dimensions[2] * axial.GetNormal(); - - result->AddSlice(origin, - axial.GetAxisY(), - -axial.GetNormal()); - } - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - return result.release(); - } - - uint64_t ImageBuffer3D::GetEstimatedMemorySize() const { return image_.GetPitch() * image_.GetHeight() * Orthanc::GetBytesPerPixel(format_); @@ -258,35 +197,6 @@ } - bool ImageBuffer3D::FitWindowingToRange(RenderStyle& style, - const Deprecated::DicomFrameConverter& converter) const - { - if (hasRange_) - { - style.windowing_ = ImageWindowing_Custom; - - // casting the narrower type to wider before calling the + operator - // will prevent overflowing (this is why the cast to double is only - // done on the first operand) - style.customWindowCenter_ = static_cast( - converter.Apply((static_cast(minValue_) + maxValue_) / 2.0)); - - style.customWindowWidth_ = static_cast( - converter.Apply(static_cast(maxValue_) - minValue_)); - - if (style.customWindowWidth_ > 1) - { - return true; - } - } - - style.windowing_ = ImageWindowing_Custom; - style.customWindowCenter_ = 128.0; - style.customWindowWidth_ = 256.0; - return false; - } - - ImageBuffer3D::SliceReader::SliceReader(const ImageBuffer3D& that, VolumeProjection projection, unsigned int slice) diff -r a386bbc955dc -r 92c400a09f1b Framework/Volumes/ImageBuffer3D.h --- a/Framework/Volumes/ImageBuffer3D.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Volumes/ImageBuffer3D.h Wed May 22 16:13:46 2019 +0200 @@ -22,10 +22,7 @@ #pragma once #include "../StoneEnumerations.h" -#include "../Layers/RenderStyle.h" -#include "../Toolbox/VolumeImageGeometry.h" -#include "../Toolbox/DicomFrameConverter.h" -#include "../Toolbox/ParallelSlices.h" +#include "../Toolbox/LinearAlgebra.h" #include @@ -34,7 +31,6 @@ class ImageBuffer3D : public boost::noncopyable { private: - VolumeImageGeometry geometry_; // TODO => Move this out of this class Orthanc::Image image_; Orthanc::PixelFormat format_; unsigned int width_; @@ -78,16 +74,6 @@ void Clear(); - VolumeImageGeometry& GetGeometry() - { - return geometry_; - } - - const VolumeImageGeometry& GetGeometry() const - { - return geometry_; - } - const Orthanc::ImageAccessor& GetInternalImage() const { return image_; @@ -113,17 +99,11 @@ return format_; } - // TODO - Remove - ParallelSlices* GetGeometry(VolumeProjection projection) const; - uint64_t GetEstimatedMemorySize() const; bool GetRange(float& minValue, float& maxValue) const; - bool FitWindowingToRange(RenderStyle& style, - const Deprecated::DicomFrameConverter& converter) const; - uint8_t GetVoxelGrayscale8Unchecked(unsigned int x, unsigned int y, unsigned int z) const diff -r a386bbc955dc -r 92c400a09f1b Framework/Volumes/OrientedVolumeBoundingBox.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Volumes/OrientedVolumeBoundingBox.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,268 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "OrientedVolumeBoundingBox.h" + +#include "../Toolbox/GeometryToolbox.h" +#include "ImageBuffer3D.h" + +#include + +#include + +namespace OrthancStone +{ + OrientedVolumeBoundingBox::OrientedVolumeBoundingBox(const VolumeImageGeometry& geometry) + { + unsigned int n = geometry.GetDepth(); + if (n < 1) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize); + } + + Vector dim = geometry.GetVoxelDimensions(VolumeProjection_Axial); + + u_ = geometry.GetAxialGeometry().GetAxisX(); + v_ = geometry.GetAxialGeometry().GetAxisY(); + w_ = geometry.GetAxialGeometry().GetNormal(); + + hu_ = static_cast(geometry.GetWidth() * dim[0] / 2.0); + hv_ = static_cast(geometry.GetHeight() * dim[1] / 2.0); + hw_ = static_cast(geometry.GetDepth() * dim[2] / 2.0); + + c_ = (geometry.GetAxialGeometry().GetOrigin() + + (hu_ - dim[0] / 2.0) * u_ + + (hv_ - dim[1] / 2.0) * v_ + + (hw_ - dim[2] / 2.0) * w_); + } + + + bool OrientedVolumeBoundingBox::HasIntersectionWithPlane(std::vector& points, + const Vector& normal, + double d) const + { + assert(normal.size() == 3); + + double r = (hu_ * fabs(boost::numeric::ublas::inner_prod(normal, u_)) + + hv_ * fabs(boost::numeric::ublas::inner_prod(normal, v_)) + + hw_ * fabs(boost::numeric::ublas::inner_prod(normal, w_))); + + double s = boost::numeric::ublas::inner_prod(normal, c_) + d; + + if (fabs(s) >= r) + { + // No intersection, or intersection is reduced to a single point + return false; + } + else + { + Vector p; + + // Loop over all the 12 edges (segments) of the oriented + // bounding box, and check whether they intersect the plane + + // X-aligned edges + if (GeometryToolbox::IntersectPlaneAndSegment + (p, normal, d, + c_ - u_ * hu_ - v_ * hv_ - w_ * hw_, + c_ + u_ * hu_ - v_ * hv_ - w_ * hw_)) + { + points.push_back(p); + } + + if (GeometryToolbox::IntersectPlaneAndSegment + (p, normal, d, + c_ - u_ * hu_ + v_ * hv_ - w_ * hw_, + c_ + u_ * hu_ + v_ * hv_ - w_ * hw_)) + { + points.push_back(p); + } + + if (GeometryToolbox::IntersectPlaneAndSegment + (p, normal, d, + c_ - u_ * hu_ - v_ * hv_ + w_ * hw_, + c_ + u_ * hu_ - v_ * hv_ + w_ * hw_)) + { + points.push_back(p); + } + + if (GeometryToolbox::IntersectPlaneAndSegment + (p, normal, d, + c_ - u_ * hu_ + v_ * hv_ + w_ * hw_, + c_ + u_ * hu_ + v_ * hv_ + w_ * hw_)) + { + points.push_back(p); + } + + // Y-aligned edges + if (GeometryToolbox::IntersectPlaneAndSegment + (p, normal, d, + c_ - u_ * hu_ - v_ * hv_ - w_ * hw_, + c_ - u_ * hu_ + v_ * hv_ - w_ * hw_)) + { + points.push_back(p); + } + + if (GeometryToolbox::IntersectPlaneAndSegment + (p, normal, d, + c_ + u_ * hu_ - v_ * hv_ - w_ * hw_, + c_ + u_ * hu_ + v_ * hv_ - w_ * hw_)) + { + points.push_back(p); + } + + if (GeometryToolbox::IntersectPlaneAndSegment + (p, normal, d, + c_ - u_ * hu_ - v_ * hv_ + w_ * hw_, + c_ - u_ * hu_ + v_ * hv_ + w_ * hw_)) + { + points.push_back(p); + } + + if (GeometryToolbox::IntersectPlaneAndSegment + (p, normal, d, + c_ + u_ * hu_ - v_ * hv_ + w_ * hw_, + c_ + u_ * hu_ + v_ * hv_ + w_ * hw_)) + { + points.push_back(p); + } + + // Z-aligned edges + if (GeometryToolbox::IntersectPlaneAndSegment + (p, normal, d, + c_ - u_ * hu_ - v_ * hv_ - w_ * hw_, + c_ - u_ * hu_ - v_ * hv_ + w_ * hw_)) + { + points.push_back(p); + } + + if (GeometryToolbox::IntersectPlaneAndSegment + (p, normal, d, + c_ + u_ * hu_ - v_ * hv_ - w_ * hw_, + c_ + u_ * hu_ - v_ * hv_ + w_ * hw_)) + { + points.push_back(p); + } + + if (GeometryToolbox::IntersectPlaneAndSegment + (p, normal, d, + c_ - u_ * hu_ + v_ * hv_ - w_ * hw_, + c_ - u_ * hu_ + v_ * hv_ + w_ * hw_)) + { + points.push_back(p); + } + + if (GeometryToolbox::IntersectPlaneAndSegment + (p, normal, d, + c_ + u_ * hu_ + v_ * hv_ - w_ * hw_, + c_ + u_ * hu_ + v_ * hv_ + w_ * hw_)) + { + points.push_back(p); + } + + return true; + } + } + + + bool OrientedVolumeBoundingBox::HasIntersection(std::vector& points, + const CoordinateSystem3D& plane) const + { + // From the vector equation of a 3D plane (specified by origin + // and normal), to the general equation of a 3D plane (which + // looses information about the origin of the coordinate system) + const Vector& normal = plane.GetNormal(); + const Vector& origin = plane.GetOrigin(); + double d = -(normal[0] * origin[0] + normal[1] * origin[1] + normal[2] * origin[2]); + + return HasIntersectionWithPlane(points, normal, d); + } + + + bool OrientedVolumeBoundingBox::Contains(const Vector& p) const + { + assert(p.size() == 3); + + const Vector q = p - c_; + + return (fabs(boost::numeric::ublas::inner_prod(q, u_)) <= hu_ && + fabs(boost::numeric::ublas::inner_prod(q, v_)) <= hv_ && + fabs(boost::numeric::ublas::inner_prod(q, w_)) <= hw_); + } + + + void OrientedVolumeBoundingBox::FromInternalCoordinates(Vector& target, + double x, + double y, + double z) const + { + target = (c_ + + u_ * 2.0 * hu_ * (x - 0.5) + + v_ * 2.0 * hv_ * (y - 0.5) + + w_ * 2.0 * hw_ * (z - 0.5)); + } + + + void OrientedVolumeBoundingBox::FromInternalCoordinates(Vector& target, + const Vector& source) const + { + assert(source.size() == 3); + FromInternalCoordinates(target, source[0], source[1], source[2]); + } + + + void OrientedVolumeBoundingBox::ToInternalCoordinates(Vector& target, + const Vector& source) const + { + assert(source.size() == 3); + const Vector q = source - c_; + + double x = boost::numeric::ublas::inner_prod(q, u_) / (2.0 * hu_) + 0.5; + double y = boost::numeric::ublas::inner_prod(q, v_) / (2.0 * hv_) + 0.5; + double z = boost::numeric::ublas::inner_prod(q, w_) / (2.0 * hw_) + 0.5; + + LinearAlgebra::AssignVector(target, x, y, z); + } + + + bool OrientedVolumeBoundingBox::ComputeExtent(Extent2D& extent, + const CoordinateSystem3D& plane) const + { + extent.Reset(); + + std::vector points; + if (HasIntersection(points, plane)) + { + for (size_t i = 0; i < points.size(); i++) + { + double x, y; + plane.ProjectPoint(x, y, points[i]); + extent.AddPoint(x, y); + } + + return true; + } + else + { + return false; + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Volumes/OrientedVolumeBoundingBox.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Volumes/OrientedVolumeBoundingBox.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,74 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../Toolbox/CoordinateSystem3D.h" +#include "../Toolbox/Extent2D.h" +#include "../Toolbox/LinearAlgebra.h" +#include "VolumeImageGeometry.h" + +namespace OrthancStone +{ + class OrientedVolumeBoundingBox : public boost::noncopyable + { + private: + Vector c_; // center + Vector u_; // normalized width vector + Vector v_; // normalized height vector + Vector w_; // normalized depth vector + double hu_; // half width + double hv_; // half height + double hw_; // half depth + + public: + OrientedVolumeBoundingBox(const VolumeImageGeometry& geometry); + + const Vector& GetCenter() const + { + return c_; + } + + bool HasIntersectionWithPlane(std::vector& points, + const Vector& normal, + double d) const; + + bool HasIntersection(std::vector& points, + const CoordinateSystem3D& plane) const; + + bool Contains(const Vector& p) const; + + void FromInternalCoordinates(Vector& target, + double x, + double y, + double z) const; + + void FromInternalCoordinates(Vector& target, + const Vector& source) const; + + void ToInternalCoordinates(Vector& target, + const Vector& source) const; + + bool ComputeExtent(Extent2D& extent, + const CoordinateSystem3D& plane) const; + }; +} + diff -r a386bbc955dc -r 92c400a09f1b Framework/Volumes/StructureSetLoader.cpp --- a/Framework/Volumes/StructureSetLoader.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,116 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "StructureSetLoader.h" - -#include "../Toolbox/MessagingToolbox.h" - -#include - -namespace OrthancStone -{ - StructureSetLoader::StructureSetLoader(MessageBroker& broker, - OrthancApiClient& orthanc) : - IVolumeLoader(broker), - IObserver(broker), - orthanc_(orthanc) - { - } - - - void StructureSetLoader::OnReferencedSliceLoaded(const OrthancApiClient::JsonResponseReadyMessage& message) - { - OrthancPlugins::FullOrthancDataset dataset(message.GetJson()); - - Orthanc::DicomMap slice; - MessagingToolbox::ConvertDataset(slice, dataset); - structureSet_->AddReferencedSlice(slice); - - BroadcastMessage(ContentChangedMessage(*this)); - } - - - void StructureSetLoader::OnStructureSetLoaded(const OrthancApiClient::JsonResponseReadyMessage& message) - { - OrthancPlugins::FullOrthancDataset dataset(message.GetJson()); - structureSet_.reset(new DicomStructureSet(dataset)); - - std::set instances; - structureSet_->GetReferencedInstances(instances); - - for (std::set::const_iterator it = instances.begin(); - it != instances.end(); ++it) - { - orthanc_.PostBinaryAsyncExpectJson("/tools/lookup", *it, - new Callable(*this, &StructureSetLoader::OnLookupCompleted)); - } - - BroadcastMessage(GeometryReadyMessage(*this)); - } - - - void StructureSetLoader::OnLookupCompleted(const OrthancApiClient::JsonResponseReadyMessage& message) - { - const Json::Value& lookup = message.GetJson(); - - if (lookup.type() != Json::arrayValue || - lookup.size() != 1 || - !lookup[0].isMember("Type") || - !lookup[0].isMember("Path") || - lookup[0]["Type"].type() != Json::stringValue || - lookup[0]["ID"].type() != Json::stringValue || - lookup[0]["Type"].asString() != "Instance") - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); - } - - const std::string& instance = lookup[0]["ID"].asString(); - orthanc_.GetJsonAsync("/instances/" + instance + "/tags", - new Callable(*this, &StructureSetLoader::OnReferencedSliceLoaded)); - } - - - void StructureSetLoader::ScheduleLoadInstance(const std::string& instance) - { - if (structureSet_.get() != NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else - { - orthanc_.GetJsonAsync("/instances/" + instance + "/tags?ignore-length=3006-0050", - new Callable(*this, &StructureSetLoader::OnStructureSetLoaded)); - } - } - - - DicomStructureSet& StructureSetLoader::GetStructureSet() - { - if (structureSet_.get() == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else - { - return *structureSet_; - } - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Volumes/StructureSetLoader.h --- a/Framework/Volumes/StructureSetLoader.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../Toolbox/DicomStructureSet.h" -#include "../Toolbox/OrthancApiClient.h" -#include "IVolumeLoader.h" - -namespace OrthancStone -{ - class StructureSetLoader : - public IVolumeLoader, - public IObserver - { - private: - OrthancApiClient& orthanc_; - std::auto_ptr structureSet_; - - void OnReferencedSliceLoaded(const OrthancApiClient::JsonResponseReadyMessage& message); - - void OnStructureSetLoaded(const OrthancApiClient::JsonResponseReadyMessage& message); - - void OnLookupCompleted(const OrthancApiClient::JsonResponseReadyMessage& message); - - public: - StructureSetLoader(MessageBroker& broker, - OrthancApiClient& orthanc); - - void ScheduleLoadInstance(const std::string& instance); - - bool HasStructureSet() const - { - return structureSet_.get() != NULL; - } - - DicomStructureSet& GetStructureSet(); - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Volumes/VolumeImageGeometry.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Volumes/VolumeImageGeometry.cpp Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,309 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "VolumeImageGeometry.h" + +#include "../Toolbox/GeometryToolbox.h" + +#include + + +namespace OrthancStone +{ + void VolumeImageGeometry::Invalidate() + { + Vector p = (axialGeometry_.GetOrigin() + + static_cast(depth_ - 1) * voxelDimensions_[2] * axialGeometry_.GetNormal()); + + coronalGeometry_ = CoordinateSystem3D(p, + axialGeometry_.GetAxisX(), + -axialGeometry_.GetNormal()); + + sagittalGeometry_ = CoordinateSystem3D(p, + axialGeometry_.GetAxisY(), + axialGeometry_.GetNormal()); + + Vector origin = ( + axialGeometry_.MapSliceToWorldCoordinates(-0.5 * voxelDimensions_[0], + -0.5 * voxelDimensions_[1]) - + 0.5 * voxelDimensions_[2] * axialGeometry_.GetNormal()); + + Vector scaling; + + if (width_ == 0 || + height_ == 0 || + depth_ == 0) + { + LinearAlgebra::AssignVector(scaling, 1, 1, 1); + } + else + { + scaling = ( + axialGeometry_.GetAxisX() * voxelDimensions_[0] * static_cast(width_) + + axialGeometry_.GetAxisY() * voxelDimensions_[1] * static_cast(height_) + + axialGeometry_.GetNormal() * voxelDimensions_[2] * static_cast(depth_)); + } + + transform_ = LinearAlgebra::Product( + GeometryToolbox::CreateTranslationMatrix(origin[0], origin[1], origin[2]), + GeometryToolbox::CreateScalingMatrix(scaling[0], scaling[1], scaling[2])); + + LinearAlgebra::InvertMatrix(transformInverse_, transform_); + } + + + VolumeImageGeometry::VolumeImageGeometry() : + width_(0), + height_(0), + depth_(0) + { + LinearAlgebra::AssignVector(voxelDimensions_, 1, 1, 1); + Invalidate(); + } + + + void VolumeImageGeometry::SetSize(unsigned int width, + unsigned int height, + unsigned int depth) + { + width_ = width; + height_ = height; + depth_ = depth; + Invalidate(); + } + + + void VolumeImageGeometry::SetAxialGeometry(const CoordinateSystem3D& geometry) + { + axialGeometry_ = geometry; + Invalidate(); + } + + + void VolumeImageGeometry::SetVoxelDimensions(double x, + double y, + double z) + { + if (x <= 0 || + y <= 0 || + z <= 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + LinearAlgebra::AssignVector(voxelDimensions_, x, y, z); + Invalidate(); + } + } + + + const CoordinateSystem3D& VolumeImageGeometry::GetProjectionGeometry(VolumeProjection projection) const + { + switch (projection) + { + case VolumeProjection_Axial: + return axialGeometry_; + + case VolumeProjection_Coronal: + return coronalGeometry_; + + case VolumeProjection_Sagittal: + return sagittalGeometry_; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + + Vector VolumeImageGeometry::GetVoxelDimensions(VolumeProjection projection) const + { + switch (projection) + { + case VolumeProjection_Axial: + return voxelDimensions_; + + case VolumeProjection_Coronal: + return LinearAlgebra::CreateVector(voxelDimensions_[0], voxelDimensions_[2], voxelDimensions_[1]); + + case VolumeProjection_Sagittal: + return LinearAlgebra::CreateVector(voxelDimensions_[1], voxelDimensions_[2], voxelDimensions_[0]); + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + + unsigned int VolumeImageGeometry::GetProjectionWidth(VolumeProjection projection) const + { + switch (projection) + { + case VolumeProjection_Axial: + return width_; + + case VolumeProjection_Coronal: + return width_; + + case VolumeProjection_Sagittal: + return height_; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + + unsigned int VolumeImageGeometry::GetProjectionHeight(VolumeProjection projection) const + { + switch (projection) + { + case VolumeProjection_Axial: + return height_; + + case VolumeProjection_Coronal: + return depth_; + + case VolumeProjection_Sagittal: + return depth_; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + + unsigned int VolumeImageGeometry::GetProjectionDepth(VolumeProjection projection) const + { + switch (projection) + { + case VolumeProjection_Axial: + return depth_; + + case VolumeProjection_Coronal: + return height_; + + case VolumeProjection_Sagittal: + return width_; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + + Vector VolumeImageGeometry::GetCoordinates(float x, + float y, + float z) const + { + Vector p = LinearAlgebra::Product(transform_, LinearAlgebra::CreateVector(x, y, z, 1)); + + assert(LinearAlgebra::IsNear(p[3], 1)); // Affine transform, no perspective effect + + // Back to non-homogeneous coordinates + return LinearAlgebra::CreateVector(p[0], p[1], p[2]); + } + + + bool VolumeImageGeometry::DetectProjection(VolumeProjection& projection, + const Vector& planeNormal) const + { + if (GeometryToolbox::IsParallel(planeNormal, axialGeometry_.GetNormal())) + { + projection = VolumeProjection_Axial; + return true; + } + else if (GeometryToolbox::IsParallel(planeNormal, coronalGeometry_.GetNormal())) + { + projection = VolumeProjection_Coronal; + return true; + } + else if (GeometryToolbox::IsParallel(planeNormal, sagittalGeometry_.GetNormal())) + { + projection = VolumeProjection_Sagittal; + return true; + } + else + { + return false; + } + } + + + bool VolumeImageGeometry::DetectSlice(VolumeProjection& projection, + unsigned int& slice, + const CoordinateSystem3D& plane) const + { + if (!DetectProjection(projection, plane.GetNormal())) + { + return false; + } + + // Transforms the coordinates of the origin of the plane, into the + // coordinates of the axial geometry + const Vector& origin = plane.GetOrigin(); + Vector p = LinearAlgebra::Product( + transformInverse_, + LinearAlgebra::CreateVector(origin[0], origin[1], origin[2], 1)); + + assert(LinearAlgebra::IsNear(p[3], 1)); + + double z; + + switch (projection) + { + case VolumeProjection_Axial: + z = p[2]; + break; + + case VolumeProjection_Coronal: + z = p[1]; + break; + + case VolumeProjection_Sagittal: + z = p[0]; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + const unsigned int projectionDepth = GetProjectionDepth(projection); + + z *= static_cast(projectionDepth); + if (z < 0) + { + return false; + } + + unsigned int d = static_cast(std::floor(z)); + if (d >= projectionDepth) + { + return false; + } + else + { + slice = d; + return true; + } + } +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Volumes/VolumeImageGeometry.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Volumes/VolumeImageGeometry.h Wed May 22 16:13:46 2019 +0200 @@ -0,0 +1,122 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../StoneEnumerations.h" +#include "../Toolbox/CoordinateSystem3D.h" + +namespace OrthancStone +{ + class VolumeImageGeometry + { + private: + unsigned int width_; + unsigned int height_; + unsigned int depth_; + CoordinateSystem3D axialGeometry_; + CoordinateSystem3D coronalGeometry_; + CoordinateSystem3D sagittalGeometry_; + Vector voxelDimensions_; + Matrix transform_; + Matrix transformInverse_; + + void Invalidate(); + + public: + VolumeImageGeometry(); + + unsigned int GetWidth() const + { + return width_; + } + + unsigned int GetHeight() const + { + return height_; + } + + unsigned int GetDepth() const + { + return depth_; + } + + const CoordinateSystem3D& GetAxialGeometry() const + { + return axialGeometry_; + } + + const CoordinateSystem3D& GetCoronalGeometry() const + { + return coronalGeometry_; + } + + const CoordinateSystem3D& GetSagittalGeometry() const + { + return sagittalGeometry_; + } + + const CoordinateSystem3D& GetProjectionGeometry(VolumeProjection projection) const; + + const Matrix& GetTransform() const + { + return transform_; + } + + const Matrix& GetTransformInverse() const + { + return transformInverse_; + } + + void SetSize(unsigned int width, + unsigned int height, + unsigned int depth); + + // Set the geometry of the first axial slice (i.e. the one whose + // depth == 0) + void SetAxialGeometry(const CoordinateSystem3D& geometry); + + void SetVoxelDimensions(double x, + double y, + double z); + + Vector GetVoxelDimensions(VolumeProjection projection) const; + + unsigned int GetProjectionWidth(VolumeProjection projection) const; + + unsigned int GetProjectionHeight(VolumeProjection projection) const; + + unsigned int GetProjectionDepth(VolumeProjection projection) const; + + // Get the 3D position of a point in the volume, where x, y and z + // lie in the [0;1] range + Vector GetCoordinates(float x, + float y, + float z) const; + + bool DetectProjection(VolumeProjection& projection, + const Vector& planeNormal) const; + + bool DetectSlice(VolumeProjection& projection, + unsigned int& slice, + const CoordinateSystem3D& plane) const; + }; +} diff -r a386bbc955dc -r 92c400a09f1b Framework/Volumes/VolumeReslicer.cpp --- a/Framework/Volumes/VolumeReslicer.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Volumes/VolumeReslicer.cpp Wed May 22 16:13:46 2019 +0200 @@ -230,7 +230,7 @@ FastRowIterator(const Orthanc::ImageAccessor& slice, const Extent2D& extent, const CoordinateSystem3D& plane, - const OrientedBoundingBox& box, + const OrientedVolumeBoundingBox& box, unsigned int y) { const double width = static_cast(slice.GetWidth()); @@ -285,7 +285,7 @@ const Orthanc::ImageAccessor& slice_; const Extent2D& extent_; const CoordinateSystem3D& plane_; - const OrientedBoundingBox& box_; + const OrientedVolumeBoundingBox& box_; unsigned int x_; unsigned int y_; @@ -293,7 +293,7 @@ SlowRowIterator(const Orthanc::ImageAccessor& slice, const Extent2D& extent, const CoordinateSystem3D& plane, - const OrientedBoundingBox& box, + const OrientedVolumeBoundingBox& box, unsigned int y) : slice_(slice), extent_(extent), @@ -342,7 +342,7 @@ const Extent2D& extent, const ImageBuffer3D& source, const CoordinateSystem3D& plane, - const OrientedBoundingBox& box, + const OrientedVolumeBoundingBox& box, float scaling, float offset) { @@ -386,7 +386,7 @@ const Extent2D& extent, const ImageBuffer3D& source, const CoordinateSystem3D& plane, - const OrientedBoundingBox& box, + const OrientedVolumeBoundingBox& box, ImageInterpolation interpolation, bool hasLinearFunction, float scaling, @@ -452,7 +452,7 @@ const Extent2D& extent, const ImageBuffer3D& source, const CoordinateSystem3D& plane, - const OrientedBoundingBox& box, + const OrientedVolumeBoundingBox& box, ImageInterpolation interpolation, bool hasLinearFunction, float scaling, @@ -501,7 +501,7 @@ void VolumeReslicer::CheckIterators(const ImageBuffer3D& source, const CoordinateSystem3D& plane, - const OrientedBoundingBox& box) const + const OrientedVolumeBoundingBox& box) const { for (unsigned int y = 0; y < slice_->GetHeight(); y++) { @@ -745,12 +745,13 @@ void VolumeReslicer::Apply(const ImageBuffer3D& source, + const VolumeImageGeometry& geometry, const CoordinateSystem3D& plane) { // Choose the default voxel size as the finest voxel dimension // of the source volumetric image const OrthancStone::Vector dim = - source.GetGeometry().GetVoxelDimensions(OrthancStone::VolumeProjection_Axial); + geometry.GetVoxelDimensions(OrthancStone::VolumeProjection_Axial); double voxelSize = dim[0]; if (dim[1] < voxelSize) @@ -768,11 +769,12 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } - Apply(source, plane, voxelSize); + Apply(source, geometry, plane, voxelSize); } void VolumeReslicer::Apply(const ImageBuffer3D& source, + const VolumeImageGeometry& geometry, const CoordinateSystem3D& plane, double voxelSize) { @@ -783,7 +785,7 @@ // to 6 vertices. We compute the extent of the intersection // polygon, with respect to the coordinate system of the reslicing // plane. - OrientedBoundingBox box(source); + OrientedVolumeBoundingBox box(geometry); if (!box.ComputeExtent(extent_, plane)) { diff -r a386bbc955dc -r 92c400a09f1b Framework/Volumes/VolumeReslicer.h --- a/Framework/Volumes/VolumeReslicer.h Wed May 22 16:01:34 2019 +0200 +++ b/Framework/Volumes/VolumeReslicer.h Wed May 22 16:13:46 2019 +0200 @@ -22,7 +22,7 @@ #pragma once #include "../Toolbox/Extent2D.h" -#include "../Toolbox/OrientedBoundingBox.h" +#include "OrientedVolumeBoundingBox.h" #include "ImageBuffer3D.h" namespace OrthancStone @@ -46,7 +46,7 @@ void CheckIterators(const ImageBuffer3D& source, const CoordinateSystem3D& plane, - const OrientedBoundingBox& box) const; + const OrientedVolumeBoundingBox& box) const; void Reset(); @@ -111,9 +111,11 @@ Orthanc::ImageAccessor* ReleaseOutputSlice(); void Apply(const ImageBuffer3D& source, + const VolumeImageGeometry& geometry, const CoordinateSystem3D& plane); void Apply(const ImageBuffer3D& source, + const VolumeImageGeometry& geometry, const CoordinateSystem3D& plane, double voxelSize); }; diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/CairoWidget.cpp --- a/Framework/Widgets/CairoWidget.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,101 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "CairoWidget.h" - -#include -#include - -namespace OrthancStone -{ - static bool IsAligned(const Orthanc::ImageAccessor& target) - { - // TODO - return true; - } - - CairoWidget::CairoWidget(const std::string& name) : - WidgetBase(name) - { - } - - void CairoWidget::SetSize(unsigned int width, - unsigned int height) - { - surface_.SetSize(width, height, false /* no alpha */); - } - - - bool CairoWidget::Render(Orthanc::ImageAccessor& target) - { - // Don't call the base class here, as - // "ClearBackgroundCairo()" is a faster alternative - - if (IsAligned(target)) - { - CairoSurface surface(target, false /* no alpha */); - CairoContext context(surface); - ClearBackgroundCairo(context); - return RenderCairo(context); - } - else - { - CairoContext context(surface_); - ClearBackgroundCairo(context); - - if (RenderCairo(context)) - { - Orthanc::ImageAccessor surface; - surface_.GetReadOnlyAccessor(surface); - Orthanc::ImageProcessing::Copy(target, surface); - return true; - } - else - { - return false; - } - } - } - - - void CairoWidget::RenderMouseOver(Orthanc::ImageAccessor& target, - int x, - int y) - { - if (IsAligned(target)) - { - CairoSurface surface(target, false /* no alpha */); - CairoContext context(surface); - RenderMouseOverCairo(context, x, y); - } - else - { - Orthanc::ImageAccessor accessor; - surface_.GetWriteableAccessor(accessor); - Orthanc::ImageProcessing::Copy(accessor, target); - - CairoContext context(surface_); - RenderMouseOverCairo(context, x, y); - - Orthanc::ImageProcessing::Copy(target, accessor); - } - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/CairoWidget.h --- a/Framework/Widgets/CairoWidget.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "WidgetBase.h" - -namespace OrthancStone -{ - class CairoWidget : public WidgetBase - { - private: - CairoSurface surface_; - - protected: - virtual bool RenderCairo(CairoContext& context) = 0; - - virtual void RenderMouseOverCairo(CairoContext& context, - int x, - int y) = 0; - - public: - CairoWidget(const std::string& name); - - virtual void SetSize(unsigned int width, - unsigned int height); - - virtual bool Render(Orthanc::ImageAccessor& target); - - virtual void RenderMouseOver(Orthanc::ImageAccessor& target, - int x, - int y); - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/EmptyWidget.cpp --- a/Framework/Widgets/EmptyWidget.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "EmptyWidget.h" - -#include -#include - -namespace OrthancStone -{ - bool EmptyWidget::Render(Orthanc::ImageAccessor& surface) - { - // Note: This call is slow - Orthanc::ImageProcessing::Set(surface, red_, green_, blue_, 255); - return true; - } - - - void EmptyWidget::DoAnimation() - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/EmptyWidget.h --- a/Framework/Widgets/EmptyWidget.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,116 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "IWidget.h" - -namespace OrthancStone -{ - /** - * This is a test widget that simply fills its surface with an - * uniform color. - **/ - class EmptyWidget : public IWidget - { - private: - uint8_t red_; - uint8_t green_; - uint8_t blue_; - - public: - EmptyWidget(uint8_t red, - uint8_t green, - uint8_t blue) : - red_(red), - green_(green), - blue_(blue) - { - } - - virtual void FitContent() - { - } - - virtual void SetParent(IWidget& widget) - { - } - - virtual void SetViewport(WidgetViewport& viewport) - { - } - - virtual void NotifyContentChanged() - { - } - - virtual void SetStatusBar(IStatusBar& statusBar) - { - } - - virtual void SetSize(unsigned int width, - unsigned int height) - { - } - - virtual bool Render(Orthanc::ImageAccessor& surface); - - virtual IMouseTracker* CreateMouseTracker(MouseButton button, - int x, - int y, - KeyboardModifiers modifiers, - const std::vector& touches) - { - return NULL; - } - - virtual void RenderMouseOver(Orthanc::ImageAccessor& target, - int x, - int y) - { - } - - virtual void MouseWheel(MouseWheelDirection direction, - int x, - int y, - KeyboardModifiers modifiers) - { - } - - virtual void KeyPressed(KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers) - { - } - - virtual bool HasAnimation() const - { - return false; - } - - virtual void DoAnimation(); - - virtual bool HasRenderMouseOver() - { - return false; - } - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/IWidget.h --- a/Framework/Widgets/IWidget.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../StoneEnumerations.h" -#include "../Viewport/IMouseTracker.h" -#include "../Viewport/IStatusBar.h" - -namespace OrthancStone -{ - class WidgetViewport; // Forward declaration - - class IWidget : public boost::noncopyable - { - public: - virtual ~IWidget() - { - } - - virtual void FitContent() = 0; - - virtual void SetParent(IWidget& parent) = 0; - - virtual void SetViewport(WidgetViewport& viewport) = 0; - - virtual void SetStatusBar(IStatusBar& statusBar) = 0; - - virtual void SetSize(unsigned int width, - unsigned int height) = 0; - - virtual bool Render(Orthanc::ImageAccessor& surface) = 0; - - virtual IMouseTracker* CreateMouseTracker(MouseButton button, - int x, - int y, - KeyboardModifiers modifiers, - const std::vector& touches) = 0; - - virtual void RenderMouseOver(Orthanc::ImageAccessor& target, - int x, - int y) = 0; - - virtual bool HasRenderMouseOver() = 0; - - virtual void MouseWheel(MouseWheelDirection direction, - int x, - int y, - KeyboardModifiers modifiers) = 0; - - virtual void KeyPressed(KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers) = 0; - - virtual bool HasAnimation() const = 0; - - virtual void DoAnimation() = 0; - - // Subclasses can call this method to signal the display of the - // widget must be refreshed - virtual void NotifyContentChanged() = 0; - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/IWorldSceneInteractor.h --- a/Framework/Widgets/IWorldSceneInteractor.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "IWorldSceneMouseTracker.h" - -#include "../Toolbox/ViewportGeometry.h" -#include "../StoneEnumerations.h" -#include "../Viewport/IStatusBar.h" - -namespace OrthancStone -{ - class WorldSceneWidget; - - class IWorldSceneInteractor : public boost::noncopyable - { - public: - virtual ~IWorldSceneInteractor() - { - } - - virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, - const ViewportGeometry& view, - MouseButton button, - KeyboardModifiers modifiers, - int viewportX, - int viewportY, - double x, - double y, - IStatusBar* statusBar, - const std::vector& touches) = 0; - - virtual void MouseOver(CairoContext& context, - WorldSceneWidget& widget, - const ViewportGeometry& view, - double x, - double y, - IStatusBar* statusBar) = 0; - - virtual void MouseWheel(WorldSceneWidget& widget, - MouseWheelDirection direction, - KeyboardModifiers modifiers, - IStatusBar* statusBar) = 0; - - virtual void KeyPressed(WorldSceneWidget& widget, - KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers, - IStatusBar* statusBar) = 0; - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/IWorldSceneMouseTracker.h --- a/Framework/Widgets/IWorldSceneMouseTracker.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../Viewport/CairoContext.h" -#include "../Viewport/IMouseTracker.h" // only to get the "Touch" definition - -namespace OrthancStone -{ - - // this is tracking a mouse in scene coordinates/mm unlike - // the IMouseTracker that is tracking a mouse - // in screen coordinates/pixels. - class IWorldSceneMouseTracker : public boost::noncopyable - { - public: - virtual ~IWorldSceneMouseTracker() - { - } - - virtual bool HasRender() const = 0; - - virtual void Render(CairoContext& context, - double zoom) = 0; - - virtual void MouseUp() = 0; - - virtual void MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches) = 0; - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/LayoutWidget.cpp --- a/Framework/Widgets/LayoutWidget.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,503 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "LayoutWidget.h" - -#include -#include - -#include - -namespace OrthancStone -{ - class LayoutWidget::LayoutMouseTracker : public IMouseTracker - { - private: - std::auto_ptr tracker_; - int left_; - int top_; - unsigned int width_; - unsigned int height_; - - public: - LayoutMouseTracker(IMouseTracker* tracker, - int left, - int top, - unsigned int width, - unsigned int height) : - tracker_(tracker), - left_(left), - top_(top), - width_(width), - height_(height) - { - if (tracker == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - - virtual void Render(Orthanc::ImageAccessor& surface) - { - Orthanc::ImageAccessor accessor; - surface.GetRegion(accessor, left_, top_, width_, height_); - tracker_->Render(accessor); - } - - virtual void MouseUp() - { - tracker_->MouseUp(); - } - - virtual void MouseMove(int x, - int y, - const std::vector& displayTouches) - { - std::vector relativeTouches; - for (size_t t = 0; t < displayTouches.size(); t++) - { - relativeTouches.push_back(Touch(displayTouches[t].x - left_, displayTouches[t].y - top_)); - } - - tracker_->MouseMove(x - left_, y - top_, relativeTouches); - } - }; - - - class LayoutWidget::ChildWidget : public boost::noncopyable - { - private: - std::auto_ptr widget_; - int left_; - int top_; - unsigned int width_; - unsigned int height_; - - public: - ChildWidget(IWidget* widget) : - widget_(widget) - { - assert(widget != NULL); - SetEmpty(); - } - - void DoAnimation() - { - if (widget_->HasAnimation()) - { - widget_->DoAnimation(); - } - } - - IWidget& GetWidget() const - { - return *widget_; - } - - void SetRectangle(unsigned int left, - unsigned int top, - unsigned int width, - unsigned int height) - { - left_ = left; - top_ = top; - width_ = width; - height_ = height; - - widget_->SetSize(width, height); - } - - void SetEmpty() - { - SetRectangle(0, 0, 0, 0); - } - - bool Contains(int x, - int y) const - { - return (x >= left_ && - y >= top_ && - x < left_ + static_cast(width_) && - y < top_ + static_cast(height_)); - } - - bool Render(Orthanc::ImageAccessor& target) - { - if (width_ == 0 || - height_ == 0) - { - return true; - } - else - { - Orthanc::ImageAccessor accessor; - target.GetRegion(accessor, left_, top_, width_, height_); - return widget_->Render(accessor); - } - } - - IMouseTracker* CreateMouseTracker(MouseButton button, - int x, - int y, - KeyboardModifiers modifiers, - const std::vector& touches) - { - if (Contains(x, y)) - { - IMouseTracker* tracker = widget_->CreateMouseTracker(button, - x - left_, - y - top_, - modifiers, - touches); - if (tracker) - { - return new LayoutMouseTracker(tracker, left_, top_, width_, height_); - } - } - - return NULL; - } - - void RenderMouseOver(Orthanc::ImageAccessor& target, - int x, - int y) - { - if (Contains(x, y)) - { - Orthanc::ImageAccessor accessor; - target.GetRegion(accessor, left_, top_, width_, height_); - - widget_->RenderMouseOver(accessor, x - left_, y - top_); - } - } - - void MouseWheel(MouseWheelDirection direction, - int x, - int y, - KeyboardModifiers modifiers) - { - if (Contains(x, y)) - { - widget_->MouseWheel(direction, x - left_, y - top_, modifiers); - } - } - - bool HasRenderMouseOver() - { - return widget_->HasRenderMouseOver(); - } - }; - - - void LayoutWidget::ComputeChildrenExtents() - { - if (children_.size() == 0) - { - return; - } - - float internal = static_cast(paddingInternal_); - - if (width_ <= paddingLeft_ + paddingRight_ || - height_ <= paddingTop_ + paddingBottom_) - { - for (size_t i = 0; i < children_.size(); i++) - { - children_[i]->SetEmpty(); - } - } - else if (isHorizontal_) - { - unsigned int padding = paddingLeft_ + paddingRight_ + (static_cast(children_.size()) - 1) * paddingInternal_; - float childWidth = ((static_cast(width_) - static_cast(padding)) / - static_cast(children_.size())); - - for (size_t i = 0; i < children_.size(); i++) - { - float left = static_cast(paddingLeft_) + static_cast(i) * (childWidth + internal); - float right = left + childWidth; - - if (left >= right) - { - children_[i]->SetEmpty(); - } - else - { - children_[i]->SetRectangle(static_cast(left), - paddingTop_, - boost::math::iround(right - left), - height_ - paddingTop_ - paddingBottom_); - } - } - } - else - { - unsigned int padding = paddingTop_ + paddingBottom_ + (static_cast(children_.size()) - 1) * paddingInternal_; - float childHeight = ((static_cast(height_) - static_cast(padding)) / - static_cast(children_.size())); - - for (size_t i = 0; i < children_.size(); i++) - { - float top = static_cast(paddingTop_) + static_cast(i) * (childHeight + internal); - float bottom = top + childHeight; - - if (top >= bottom) - { - children_[i]->SetEmpty(); - } - else - { - children_[i]->SetRectangle(paddingTop_, - static_cast(top), - width_ - paddingLeft_ - paddingRight_, - boost::math::iround(bottom - top)); - } - } - } - - NotifyContentChanged(*this); - } - - - LayoutWidget::LayoutWidget(const std::string& name) : - WidgetBase(name), - isHorizontal_(true), - width_(0), - height_(0), - paddingLeft_(0), - paddingTop_(0), - paddingRight_(0), - paddingBottom_(0), - paddingInternal_(0) - { - } - - - LayoutWidget::~LayoutWidget() - { - for (size_t i = 0; i < children_.size(); i++) - { - delete children_[i]; - } - } - - - void LayoutWidget::FitContent() - { - for (size_t i = 0; i < children_.size(); i++) - { - children_[i]->GetWidget().FitContent(); - } - } - - - void LayoutWidget::NotifyContentChanged(const IWidget& widget) - { - // One of the children has changed - WidgetBase::NotifyContentChanged(); - } - - - void LayoutWidget::SetHorizontal() - { - isHorizontal_ = true; - ComputeChildrenExtents(); - } - - - void LayoutWidget::SetVertical() - { - isHorizontal_ = false; - ComputeChildrenExtents(); - } - - - void LayoutWidget::SetPadding(unsigned int left, - unsigned int top, - unsigned int right, - unsigned int bottom, - unsigned int spacing) - { - paddingLeft_ = left; - paddingTop_ = top; - paddingRight_ = right; - paddingBottom_ = bottom; - paddingInternal_ = spacing; - } - - - void LayoutWidget::SetPadding(unsigned int padding) - { - paddingLeft_ = padding; - paddingTop_ = padding; - paddingRight_ = padding; - paddingBottom_ = padding; - paddingInternal_ = padding; - } - - - IWidget& LayoutWidget::AddWidget(IWidget* widget) // Takes ownership - { - if (widget == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - if (GetStatusBar() != NULL) - { - widget->SetStatusBar(*GetStatusBar()); - } - - children_.push_back(new ChildWidget(widget)); - widget->SetParent(*this); - - ComputeChildrenExtents(); - - if (widget->HasAnimation()) - { - hasAnimation_ = true; - } - - return *widget; - } - - - void LayoutWidget::SetStatusBar(IStatusBar& statusBar) - { - WidgetBase::SetStatusBar(statusBar); - - for (size_t i = 0; i < children_.size(); i++) - { - children_[i]->GetWidget().SetStatusBar(statusBar); - } - } - - - void LayoutWidget::SetSize(unsigned int width, - unsigned int height) - { - width_ = width; - height_ = height; - ComputeChildrenExtents(); - } - - - bool LayoutWidget::Render(Orthanc::ImageAccessor& surface) - { - if (!WidgetBase::Render(surface)) - { - return false; - } - - for (size_t i = 0; i < children_.size(); i++) - { - if (!children_[i]->Render(surface)) - { - return false; - } - } - - return true; - } - - - IMouseTracker* LayoutWidget::CreateMouseTracker(MouseButton button, - int x, - int y, - KeyboardModifiers modifiers, - const std::vector& touches) - { - for (size_t i = 0; i < children_.size(); i++) - { - IMouseTracker* tracker = children_[i]->CreateMouseTracker(button, x, y, modifiers, touches); - if (tracker != NULL) - { - return tracker; - } - } - - return NULL; - } - - - void LayoutWidget::RenderMouseOver(Orthanc::ImageAccessor& target, - int x, - int y) - { - for (size_t i = 0; i < children_.size(); i++) - { - children_[i]->RenderMouseOver(target, x, y); - } - } - - - void LayoutWidget::MouseWheel(MouseWheelDirection direction, - int x, - int y, - KeyboardModifiers modifiers) - { - for (size_t i = 0; i < children_.size(); i++) - { - children_[i]->MouseWheel(direction, x, y, modifiers); - } - } - - - void LayoutWidget::KeyPressed(KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers) - { - for (size_t i = 0; i < children_.size(); i++) - { - children_[i]->GetWidget().KeyPressed(key, keyChar, modifiers); - } - } - - - void LayoutWidget::DoAnimation() - { - if (hasAnimation_) - { - for (size_t i = 0; i < children_.size(); i++) - { - children_[i]->DoAnimation(); - } - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - - - bool LayoutWidget::HasRenderMouseOver() - { - for (size_t i = 0; i < children_.size(); i++) - { - if (children_[i]->HasRenderMouseOver()) - { - return true; - } - } - - return false; - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/LayoutWidget.h --- a/Framework/Widgets/LayoutWidget.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,134 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "WidgetBase.h" - -#include -#include - -namespace OrthancStone -{ - class LayoutWidget : public WidgetBase - { - private: - class LayoutMouseTracker; - class ChildWidget; - - std::vector children_; - bool isHorizontal_; - unsigned int width_; - unsigned int height_; - std::auto_ptr mouseTracker_; - unsigned int paddingLeft_; - unsigned int paddingTop_; - unsigned int paddingRight_; - unsigned int paddingBottom_; - unsigned int paddingInternal_; - bool hasAnimation_; - - void ComputeChildrenExtents(); - - public: - LayoutWidget(const std::string& name); - - virtual ~LayoutWidget(); - - virtual void FitContent(); - - virtual void NotifyContentChanged(const IWidget& widget); - - void SetHorizontal(); - - void SetVertical(); - - void SetPadding(unsigned int left, - unsigned int top, - unsigned int right, - unsigned int bottom, - unsigned int spacing); - - void SetPadding(unsigned int padding); - - unsigned int GetPaddingLeft() const - { - return paddingLeft_; - } - - unsigned int GetPaddingTop() const - { - return paddingTop_; - } - - unsigned int GetPaddingRight() const - { - return paddingRight_; - } - - unsigned int GetPaddingBottom() const - { - return paddingBottom_; - } - - unsigned int GetPaddingInternal() const - { - return paddingInternal_; - } - - IWidget& AddWidget(IWidget* widget); // Takes ownership - - virtual void SetStatusBar(IStatusBar& statusBar); - - virtual void SetSize(unsigned int width, - unsigned int height); - - virtual bool Render(Orthanc::ImageAccessor& surface); - - virtual IMouseTracker* CreateMouseTracker(MouseButton button, - int x, - int y, - KeyboardModifiers modifiers, - const std::vector& touches); - - virtual void RenderMouseOver(Orthanc::ImageAccessor& target, - int x, - int y); - - virtual void MouseWheel(MouseWheelDirection direction, - int x, - int y, - KeyboardModifiers modifiers); - - virtual void KeyPressed(KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers); - - virtual bool HasAnimation() const - { - return hasAnimation_; - } - - virtual void DoAnimation(); - - virtual bool HasRenderMouseOver(); - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/PanMouseTracker.cpp --- a/Framework/Widgets/PanMouseTracker.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "PanMouseTracker.h" - -#include -#include - -namespace OrthancStone -{ - PanMouseTracker::PanMouseTracker(WorldSceneWidget& that, - int x, - int y) : - that_(that) - { - that.GetView().GetPan(originalPanX_, originalPanY_); - that.GetView().MapPixelCenterToScene(downX_, downY_, x, y); - } - - - void PanMouseTracker::Render(CairoContext& context, - double zoom) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - - void PanMouseTracker::MouseMove(int displayX, - int displayY, - double x, - double y, - const std::vector& displayTouches, - const std::vector& sceneTouches) - { - ViewportGeometry view = that_.GetView(); - view.SetPan(originalPanX_ + (x - downX_) * view.GetZoom(), - originalPanY_ + (y - downY_) * view.GetZoom()); - that_.SetView(view); - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/PanMouseTracker.h --- a/Framework/Widgets/PanMouseTracker.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "WorldSceneWidget.h" - -namespace OrthancStone -{ - class PanMouseTracker : public IWorldSceneMouseTracker - { - private: - WorldSceneWidget& that_; - double originalPanX_; - double originalPanY_; - double downX_; - double downY_; - - public: - PanMouseTracker(WorldSceneWidget& that, - int x, - int y); - - virtual bool HasRender() const - { - return false; - } - - virtual void MouseUp() - { - } - - virtual void Render(CairoContext& context, - double zoom); - - virtual void MouseMove(int displayX, - int displayY, - double x, - double y, - const std::vector& displayTouches, - const std::vector& sceneTouches); - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/PanZoomMouseTracker.cpp --- a/Framework/Widgets/PanZoomMouseTracker.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,137 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "PanZoomMouseTracker.h" - -#include -#include -#include - -namespace OrthancStone -{ - Touch GetCenter(const std::vector& touches) - { - return Touch((touches[0].x + touches[1].x) / 2.0f, (touches[0].y + touches[1].y) / 2.0f); - } - - double GetDistance(const std::vector& touches) - { - float dx = touches[0].x - touches[1].x; - float dy = touches[0].y - touches[1].y; - return sqrt((double)(dx * dx) + (double)(dy * dy)); - } - - - PanZoomMouseTracker::PanZoomMouseTracker(WorldSceneWidget& that, - const std::vector& startTouches) - : that_(that), - originalZoom_(that.GetView().GetZoom()) - { - that.GetView().GetPan(originalPanX_, originalPanY_); - that.GetView().MapPixelCenterToScene(originalSceneTouches_, startTouches); - - originalDisplayCenter_ = GetCenter(startTouches); - originalSceneCenter_ = GetCenter(originalSceneTouches_); - originalDisplayDistanceBetweenTouches_ = GetDistance(startTouches); - -// printf("original Pan %f %f\n", originalPanX_, originalPanY_); -// printf("original Zoom %f \n", originalZoom_); -// printf("original distance %f \n", (float)originalDisplayDistanceBetweenTouches_); -// printf("original display touches 0 %f %f\n", startTouches[0].x, startTouches[0].y); -// printf("original display touches 1 %f %f\n", startTouches[1].x, startTouches[1].y); -// printf("original Scene center %f %f\n", originalSceneCenter_.x, originalSceneCenter_.y); - - unsigned int height = that.GetView().GetDisplayHeight(); - - if (height <= 3) - { - idle_ = true; - LOG(WARNING) << "image is too small to zoom (current height = " << height << ")"; - } - else - { - idle_ = false; - normalization_ = 1.0 / static_cast(height - 1); - } - - } - - - void PanZoomMouseTracker::Render(CairoContext& context, - double zoom) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - - void PanZoomMouseTracker::MouseMove(int displayX, - int displayY, - double x, - double y, - const std::vector& displayTouches, - const std::vector& sceneTouches) - { - ViewportGeometry view = that_.GetView(); - -// printf("Display touches 0 %f %f\n", displayTouches[0].x, displayTouches[0].y); -// printf("Display touches 1 %f %f\n", displayTouches[1].x, displayTouches[1].y); -// printf("Scene touches 0 %f %f\n", sceneTouches[0].x, sceneTouches[0].y); -// printf("Scene touches 1 %f %f\n", sceneTouches[1].x, sceneTouches[1].y); - -// printf("zoom = %f\n", view.GetZoom()); - Touch currentSceneCenter = GetCenter(sceneTouches); - double panX = originalPanX_ + (currentSceneCenter.x - originalSceneCenter_.x) * view.GetZoom(); - double panY = originalPanY_ + (currentSceneCenter.y - originalSceneCenter_.y) * view.GetZoom(); - - view.SetPan(panX, panY); - - static const double MIN_ZOOM = -4; - static const double MAX_ZOOM = 4; - - if (!idle_) - { - double currentDistanceBetweenTouches = GetDistance(displayTouches); - - double dy = static_cast(currentDistanceBetweenTouches - originalDisplayDistanceBetweenTouches_) * normalization_; // In the range [-1,1] - double z; - - // Linear interpolation from [-1, 1] to [MIN_ZOOM, MAX_ZOOM] - if (dy < -1.0) - { - z = MIN_ZOOM; - } - else if (dy > 1.0) - { - z = MAX_ZOOM; - } - else - { - z = MIN_ZOOM + (MAX_ZOOM - MIN_ZOOM) * (dy + 1.0) / 2.0; - } - - z = pow(2.0, z); - - view.SetZoom(z * originalZoom_); - } - - that_.SetView(view); - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/PanZoomMouseTracker.h --- a/Framework/Widgets/PanZoomMouseTracker.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "WorldSceneWidget.h" - -namespace OrthancStone -{ - class PanZoomMouseTracker : public IWorldSceneMouseTracker - { - private: - WorldSceneWidget& that_; - std::vector originalSceneTouches_; - Touch originalSceneCenter_; - Touch originalDisplayCenter_; - double originalPanX_; - double originalPanY_; - double originalZoom_; - double originalDisplayDistanceBetweenTouches_; - bool idle_; - double normalization_; - - public: - PanZoomMouseTracker(WorldSceneWidget& that, - const std::vector& startTouches); - - virtual bool HasRender() const - { - return false; - } - - virtual void MouseUp() - { - } - - virtual void Render(CairoContext& context, - double zoom); - - virtual void MouseMove(int displayX, - int displayY, - double x, - double y, - const std::vector& displayTouches, - const std::vector& sceneTouches); - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/SliceViewerWidget.cpp --- a/Framework/Widgets/SliceViewerWidget.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,654 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "SliceViewerWidget.h" - -#include "../Layers/SliceOutlineRenderer.h" -#include "../Toolbox/GeometryToolbox.h" -#include "Framework/Layers/FrameRenderer.h" - -#include -#include - -#include - - -static const double THIN_SLICE_THICKNESS = 100.0 * std::numeric_limits::epsilon(); - -namespace OrthancStone -{ - class SliceViewerWidget::Scene : public boost::noncopyable - { - private: - CoordinateSystem3D plane_; - double thickness_; - size_t countMissing_; - std::vector renderers_; - - public: - void DeleteLayer(size_t index) - { - if (index >= renderers_.size()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - assert(countMissing_ <= renderers_.size()); - - if (renderers_[index] != NULL) - { - assert(countMissing_ < renderers_.size()); - delete renderers_[index]; - renderers_[index] = NULL; - countMissing_++; - } - } - - Scene(const CoordinateSystem3D& plane, - double thickness, - size_t countLayers) : - plane_(plane), - thickness_(thickness), - countMissing_(countLayers), - renderers_(countLayers, NULL) - { - if (thickness <= 0) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - } - - ~Scene() - { - for (size_t i = 0; i < renderers_.size(); i++) - { - DeleteLayer(i); - } - } - - void SetLayer(size_t index, - ILayerRenderer* renderer) // Takes ownership - { - if (renderer == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - - DeleteLayer(index); - - renderers_[index] = renderer; - countMissing_--; - } - - const CoordinateSystem3D& GetPlane() const - { - return plane_; - } - - bool HasRenderer(size_t index) - { - return renderers_[index] != NULL; - } - - bool IsComplete() const - { - return countMissing_ == 0; - } - - unsigned int GetCountMissing() const - { - return static_cast(countMissing_); - } - - bool RenderScene(CairoContext& context, - const ViewportGeometry& view, - const CoordinateSystem3D& viewportPlane) - { - bool fullQuality = true; - cairo_t *cr = context.GetObject(); - - for (size_t i = 0; i < renderers_.size(); i++) - { - if (renderers_[i] != NULL) - { - const CoordinateSystem3D& framePlane = renderers_[i]->GetLayerPlane(); - - double x0, y0, x1, y1, x2, y2; - viewportPlane.ProjectPoint(x0, y0, framePlane.GetOrigin()); - viewportPlane.ProjectPoint(x1, y1, framePlane.GetOrigin() + framePlane.GetAxisX()); - viewportPlane.ProjectPoint(x2, y2, framePlane.GetOrigin() + framePlane.GetAxisY()); - - /** - * Now we solve the system of linear equations Ax + b = x', given: - * A [0 ; 0] + b = [x0 ; y0] - * A [1 ; 0] + b = [x1 ; y1] - * A [0 ; 1] + b = [x2 ; y2] - * <=> - * b = [x0 ; y0] - * A [1 ; 0] = [x1 ; y1] - b = [x1 - x0 ; y1 - y0] - * A [0 ; 1] = [x2 ; y2] - b = [x2 - x0 ; y2 - y0] - * <=> - * b = [x0 ; y0] - * [a11 ; a21] = [x1 - x0 ; y1 - y0] - * [a12 ; a22] = [x2 - x0 ; y2 - y0] - **/ - - cairo_matrix_t transform; - cairo_matrix_init(&transform, x1 - x0, y1 - y0, x2 - x0, y2 - y0, x0, y0); - - cairo_save(cr); - cairo_transform(cr, &transform); - - if (!renderers_[i]->RenderLayer(context, view)) - { - cairo_restore(cr); - return false; - } - - cairo_restore(cr); - } - - if (renderers_[i] != NULL && - !renderers_[i]->IsFullQuality()) - { - fullQuality = false; - } - } - - if (!fullQuality) - { - double x, y; - view.MapDisplayToScene(x, y, static_cast(view.GetDisplayWidth()) / 2.0, 10); - - cairo_translate(cr, x, y); - -#if 1 - double s = 5.0 / view.GetZoom(); - cairo_rectangle(cr, -s, -s, 2.0 * s, 2.0 * s); -#else - // TODO Drawing filled circles makes WebAssembly crash! - cairo_arc(cr, 0, 0, 5.0 / view.GetZoom(), 0, 2.0 * boost::math::constants::pi()); -#endif - - cairo_set_line_width(cr, 2.0 / view.GetZoom()); - cairo_set_source_rgb(cr, 1, 1, 1); - cairo_stroke_preserve(cr); - cairo_set_source_rgb(cr, 1, 0, 0); - cairo_fill(cr); - } - - return true; - } - - void SetLayerStyle(size_t index, - const RenderStyle& style) - { - if (renderers_[index] != NULL) - { - renderers_[index]->SetLayerStyle(style); - } - } - - bool ContainsPlane(const CoordinateSystem3D& plane) const - { - bool isOpposite; - if (!GeometryToolbox::IsParallelOrOpposite(isOpposite, - plane.GetNormal(), - plane_.GetNormal())) - { - return false; - } - else - { - double z = (plane_.ProjectAlongNormal(plane.GetOrigin()) - - plane_.ProjectAlongNormal(plane_.GetOrigin())); - - if (z < 0) - { - z = -z; - } - - return z <= thickness_; - } - } - - double GetThickness() const - { - return thickness_; - } - }; - - - bool SliceViewerWidget::LookupLayer(size_t& index /* out */, - const IVolumeSlicer& layer) const - { - LayersIndex::const_iterator found = layersIndex_.find(&layer); - - if (found == layersIndex_.end()) - { - return false; - } - else - { - index = found->second; - assert(index < layers_.size() && - layers_[index] == &layer); - return true; - } - } - - - void SliceViewerWidget::GetLayerExtent(Extent2D& extent, - IVolumeSlicer& source) const - { - extent.Reset(); - - std::vector points; - if (source.GetExtent(points, plane_)) - { - for (size_t i = 0; i < points.size(); i++) - { - double x, y; - plane_.ProjectPoint(x, y, points[i]); - extent.AddPoint(x, y); - } - } - } - - - Extent2D SliceViewerWidget::GetSceneExtent() - { - Extent2D sceneExtent; - - for (size_t i = 0; i < layers_.size(); i++) - { - assert(layers_[i] != NULL); - Extent2D layerExtent; - GetLayerExtent(layerExtent, *layers_[i]); - - sceneExtent.Union(layerExtent); - } - - return sceneExtent; - } - - - bool SliceViewerWidget::RenderScene(CairoContext& context, - const ViewportGeometry& view) - { - if (currentScene_.get() != NULL) - { - return currentScene_->RenderScene(context, view, plane_); - } - else - { - return true; - } - } - - - void SliceViewerWidget::ResetPendingScene() - { - double thickness; - if (pendingScene_.get() == NULL) - { - thickness = 1.0; - } - else - { - thickness = pendingScene_->GetThickness(); - } - - pendingScene_.reset(new Scene(plane_, thickness, layers_.size())); - } - - - void SliceViewerWidget::UpdateLayer(size_t index, - ILayerRenderer* renderer, - const CoordinateSystem3D& plane) - { - LOG(INFO) << "Updating layer " << index; - - std::auto_ptr tmp(renderer); - - if (renderer == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - - if (index >= layers_.size()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - assert(layers_.size() == styles_.size()); - renderer->SetLayerStyle(styles_[index]); - - if (currentScene_.get() != NULL && - currentScene_->ContainsPlane(plane)) - { - currentScene_->SetLayer(index, tmp.release()); - NotifyContentChanged(); - } - else if (pendingScene_.get() != NULL && - pendingScene_->ContainsPlane(plane)) - { - pendingScene_->SetLayer(index, tmp.release()); - - if (currentScene_.get() == NULL || - !currentScene_->IsComplete() || - pendingScene_->IsComplete()) - { - currentScene_ = pendingScene_; - NotifyContentChanged(); - } - } - } - - - SliceViewerWidget::SliceViewerWidget(MessageBroker& broker, - const std::string& name) : - WorldSceneWidget(name), - IObserver(broker), - IObservable(broker), - started_(false) - { - SetBackgroundCleared(true); - } - - - SliceViewerWidget::~SliceViewerWidget() - { - for (size_t i = 0; i < layers_.size(); i++) - { - delete layers_[i]; - } - } - - void SliceViewerWidget::ObserveLayer(IVolumeSlicer& layer) - { - layer.RegisterObserverCallback(new Callable - (*this, &SliceViewerWidget::OnGeometryReady)); - // currently ignore errors layer->RegisterObserverCallback(new Callable(*this, &SliceViewerWidget::...)); - layer.RegisterObserverCallback(new Callable - (*this, &SliceViewerWidget::OnSliceChanged)); - layer.RegisterObserverCallback(new Callable - (*this, &SliceViewerWidget::OnContentChanged)); - layer.RegisterObserverCallback(new Callable - (*this, &SliceViewerWidget::OnLayerReady)); - layer.RegisterObserverCallback(new Callable - (*this, &SliceViewerWidget::OnLayerError)); - } - - - size_t SliceViewerWidget::AddLayer(IVolumeSlicer* layer) // Takes ownership - { - if (layer == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - - size_t index = layers_.size(); - layers_.push_back(layer); - styles_.push_back(RenderStyle()); - layersIndex_[layer] = index; - - ResetPendingScene(); - - ObserveLayer(*layer); - - ResetChangedLayers(); - - return index; - } - - - void SliceViewerWidget::ReplaceLayer(size_t index, IVolumeSlicer* layer) // Takes ownership - { - if (layer == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - - if (index >= layers_.size()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - delete layers_[index]; - layers_[index] = layer; - layersIndex_[layer] = index; - - ResetPendingScene(); - - ObserveLayer(*layer); - - InvalidateLayer(index); - } - - - void SliceViewerWidget::RemoveLayer(size_t index) - { - if (index >= layers_.size()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - IVolumeSlicer* previousLayer = layers_[index]; - layersIndex_.erase(layersIndex_.find(previousLayer)); - layers_.erase(layers_.begin() + index); - changedLayers_.erase(changedLayers_.begin() + index); - styles_.erase(styles_.begin() + index); - - delete layers_[index]; - - currentScene_->DeleteLayer(index); - ResetPendingScene(); - - NotifyContentChanged(); - } - - - const RenderStyle& SliceViewerWidget::GetLayerStyle(size_t layer) const - { - if (layer >= layers_.size()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - assert(layers_.size() == styles_.size()); - return styles_[layer]; - } - - - void SliceViewerWidget::SetLayerStyle(size_t layer, - const RenderStyle& style) - { - if (layer >= layers_.size()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - assert(layers_.size() == styles_.size()); - styles_[layer] = style; - - if (currentScene_.get() != NULL) - { - currentScene_->SetLayerStyle(layer, style); - } - - if (pendingScene_.get() != NULL) - { - pendingScene_->SetLayerStyle(layer, style); - } - - NotifyContentChanged(); - } - - - void SliceViewerWidget::SetSlice(const CoordinateSystem3D& plane) - { - LOG(INFO) << "Setting slice origin: (" << plane.GetOrigin()[0] - << "," << plane.GetOrigin()[1] - << "," << plane.GetOrigin()[2] << ")"; - - Deprecated::Slice displayedSlice(plane_, THIN_SLICE_THICKNESS); - - //if (!displayedSlice.ContainsPlane(slice)) - { - if (currentScene_.get() == NULL || - (pendingScene_.get() != NULL && - pendingScene_->IsComplete())) - { - currentScene_ = pendingScene_; - } - - plane_ = plane; - ResetPendingScene(); - - InvalidateAllLayers(); // TODO Removing this line avoid loading twice the image in WASM - } - - BroadcastMessage(DisplayedSliceMessage(*this, displayedSlice)); - } - - - void SliceViewerWidget::OnGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message) - { - size_t i; - if (LookupLayer(i, message.GetOrigin())) - { - LOG(INFO) << ": Geometry ready for layer " << i << " in " << GetName(); - - changedLayers_[i] = true; - //layers_[i]->ScheduleLayerCreation(plane_); - } - BroadcastMessage(GeometryChangedMessage(*this)); - } - - - void SliceViewerWidget::InvalidateAllLayers() - { - for (size_t i = 0; i < layers_.size(); i++) - { - assert(layers_[i] != NULL); - changedLayers_[i] = true; - - //layers_[i]->ScheduleLayerCreation(plane_); - } - } - - - void SliceViewerWidget::InvalidateLayer(size_t layer) - { - if (layer >= layers_.size()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - assert(layers_[layer] != NULL); - changedLayers_[layer] = true; - - //layers_[layer]->ScheduleLayerCreation(plane_); - } - - - void SliceViewerWidget::OnContentChanged(const IVolumeSlicer::ContentChangedMessage& message) - { - size_t index; - if (LookupLayer(index, message.GetOrigin())) - { - InvalidateLayer(index); - } - - BroadcastMessage(SliceViewerWidget::ContentChangedMessage(*this)); - } - - - void SliceViewerWidget::OnSliceChanged(const IVolumeSlicer::SliceContentChangedMessage& message) - { - if (message.GetSlice().ContainsPlane(plane_)) - { - size_t index; - if (LookupLayer(index, message.GetOrigin())) - { - InvalidateLayer(index); - } - } - - BroadcastMessage(SliceViewerWidget::ContentChangedMessage(*this)); - } - - - void SliceViewerWidget::OnLayerReady(const IVolumeSlicer::LayerReadyMessage& message) - { - size_t index; - if (LookupLayer(index, message.GetOrigin())) - { - LOG(INFO) << "Renderer ready for layer " << index; - UpdateLayer(index, message.CreateRenderer(), message.GetSlice()); - } - - BroadcastMessage(SliceViewerWidget::ContentChangedMessage(*this)); - } - - - void SliceViewerWidget::OnLayerError(const IVolumeSlicer::LayerErrorMessage& message) - { - size_t index; - if (LookupLayer(index, message.GetOrigin())) - { - LOG(ERROR) << "Using error renderer on layer " << index; - - // TODO - //UpdateLayer(index, new SliceOutlineRenderer(slice), slice); - - BroadcastMessage(SliceViewerWidget::ContentChangedMessage(*this)); - } - } - - - void SliceViewerWidget::ResetChangedLayers() - { - changedLayers_.resize(layers_.size()); - - for (size_t i = 0; i < changedLayers_.size(); i++) - { - changedLayers_[i] = false; - } - } - - - void SliceViewerWidget::DoAnimation() - { - assert(changedLayers_.size() <= layers_.size()); - - for (size_t i = 0; i < changedLayers_.size(); i++) - { - if (changedLayers_[i]) - { - layers_[i]->ScheduleLayerCreation(plane_); - } - } - - ResetChangedLayers(); - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/SliceViewerWidget.h --- a/Framework/Widgets/SliceViewerWidget.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,155 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "WorldSceneWidget.h" -#include "../Layers/IVolumeSlicer.h" -#include "../Toolbox/Extent2D.h" -#include "../../Framework/Messages/IObserver.h" - -#include - -namespace OrthancStone -{ - class SliceViewerWidget : - public WorldSceneWidget, - public IObserver, - public IObservable - { - public: - ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryChangedMessage, SliceViewerWidget); - ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentChangedMessage, SliceViewerWidget); - - - // TODO - Use this message in ReferenceLineSource - class DisplayedSliceMessage : public OriginMessage - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - const Deprecated::Slice& slice_; - - public: - DisplayedSliceMessage(SliceViewerWidget& origin, - const Deprecated::Slice& slice) : - OriginMessage(origin), - slice_(slice) - { - } - - const Deprecated::Slice& GetSlice() const - { - return slice_; - } - }; - - private: - SliceViewerWidget(const SliceViewerWidget&); - SliceViewerWidget& operator=(const SliceViewerWidget&); - - class Scene; - - typedef std::map LayersIndex; - - bool started_; - LayersIndex layersIndex_; - std::vector layers_; - std::vector styles_; - CoordinateSystem3D plane_; - std::auto_ptr currentScene_; - std::auto_ptr pendingScene_; - std::vector changedLayers_; - - bool LookupLayer(size_t& index /* out */, - const IVolumeSlicer& layer) const; - - void GetLayerExtent(Extent2D& extent, - IVolumeSlicer& source) const; - - void OnGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message); - - virtual void OnContentChanged(const IVolumeSlicer::ContentChangedMessage& message); - - virtual void OnSliceChanged(const IVolumeSlicer::SliceContentChangedMessage& message); - - virtual void OnLayerReady(const IVolumeSlicer::LayerReadyMessage& message); - - virtual void OnLayerError(const IVolumeSlicer::LayerErrorMessage& message); - - void ObserveLayer(IVolumeSlicer& source); - - void ResetChangedLayers(); - - public: - SliceViewerWidget(MessageBroker& broker, - const std::string& name); - - virtual Extent2D GetSceneExtent(); - - protected: - virtual bool RenderScene(CairoContext& context, - const ViewportGeometry& view); - - void ResetPendingScene(); - - void UpdateLayer(size_t index, - ILayerRenderer* renderer, - const CoordinateSystem3D& plane); - - void InvalidateAllLayers(); - - void InvalidateLayer(size_t layer); - - public: - virtual ~SliceViewerWidget(); - - size_t AddLayer(IVolumeSlicer* layer); // Takes ownership - - void ReplaceLayer(size_t layerIndex, IVolumeSlicer* layer); // Takes ownership - - void RemoveLayer(size_t layerIndex); - - size_t GetLayerCount() const - { - return layers_.size(); - } - - const RenderStyle& GetLayerStyle(size_t layer) const; - - void SetLayerStyle(size_t layer, - const RenderStyle& style); - - void SetSlice(const CoordinateSystem3D& plane); - - const CoordinateSystem3D& GetSlice() const - { - return plane_; - } - - virtual bool HasAnimation() const - { - return true; - } - - virtual void DoAnimation(); - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/TestCairoWidget.cpp --- a/Framework/Widgets/TestCairoWidget.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,126 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "TestCairoWidget.h" - -#include - - -namespace OrthancStone -{ - namespace Samples - { - void TestCairoWidget::DoAnimation() - { - value_ -= 0.01f; - if (value_ < 0) - { - value_ = 1; - } - - NotifyContentChanged(); - } - - - bool TestCairoWidget::RenderCairo(CairoContext& context) - { - cairo_t* cr = context.GetObject(); - - cairo_set_source_rgb (cr, .3, 0, 0); - cairo_paint(cr); - - cairo_set_source_rgb(cr, 0, 1, 0); - cairo_rectangle(cr, width_ / 4, height_ / 4, width_ / 2, height_ / 2); - cairo_set_line_width(cr, 1.0); - cairo_fill(cr); - - cairo_set_source_rgb(cr, 0, 1, value_); - cairo_rectangle(cr, width_ / 2 - 50, height_ / 2 - 50, 100, 100); - cairo_fill(cr); - - return true; - } - - - void TestCairoWidget::RenderMouseOverCairo(CairoContext& context, - int x, - int y) - { - cairo_t* cr = context.GetObject(); - - cairo_set_source_rgb (cr, 1, 0, 0); - cairo_rectangle(cr, x - 5, y - 5, 10, 10); - cairo_set_line_width(cr, 1.0); - cairo_stroke(cr); - - char buf[64]; - sprintf(buf, "(%d,%d)", x, y); - UpdateStatusBar(buf); - } - - - TestCairoWidget::TestCairoWidget(const std::string& name, bool animate) : - CairoWidget(name), - width_(0), - height_(0), - value_(1), - animate_(animate) - { - } - - - void TestCairoWidget::SetSize(unsigned int width, - unsigned int height) - { - CairoWidget::SetSize(width, height); - width_ = width; - height_ = height; - } - - - IMouseTracker* TestCairoWidget::CreateMouseTracker(MouseButton button, - int x, - int y, - KeyboardModifiers modifiers, - const std::vector& touches) - { - UpdateStatusBar("Click"); - return NULL; - } - - - void TestCairoWidget::MouseWheel(MouseWheelDirection direction, - int x, - int y, - KeyboardModifiers modifiers) - { - UpdateStatusBar(direction == MouseWheelDirection_Down ? "Wheel down" : "Wheel up"); - } - - - void TestCairoWidget::KeyPressed(KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers) - { - UpdateStatusBar("Key pressed: \"" + std::string(1, keyChar) + "\""); - } - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/TestCairoWidget.h --- a/Framework/Widgets/TestCairoWidget.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "CairoWidget.h" - -namespace OrthancStone -{ - namespace Samples - { - class TestCairoWidget : public CairoWidget - { - private: - unsigned int width_; - unsigned int height_; - float value_; - bool animate_; - - protected: - virtual bool RenderCairo(CairoContext& context); - - virtual void RenderMouseOverCairo(CairoContext& context, - int x, - int y); - - public: - TestCairoWidget(const std::string& name, bool animate); - - virtual void SetSize(unsigned int width, - unsigned int height); - - virtual IMouseTracker* CreateMouseTracker(MouseButton button, - int x, - int y, - KeyboardModifiers modifiers, - const std::vector& touches); - - virtual void MouseWheel(MouseWheelDirection direction, - int x, - int y, - KeyboardModifiers modifiers); - - virtual void KeyPressed(KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers); - - virtual bool HasAnimation() const - { - return animate_; - } - - virtual void DoAnimation(); - - virtual bool HasRenderMouseOver() - { - return true; - } - }; - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/TestWorldSceneWidget.cpp --- a/Framework/Widgets/TestWorldSceneWidget.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,149 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "TestWorldSceneWidget.h" - -#include - -#include -#include - -namespace OrthancStone -{ - namespace Samples - { - class TestWorldSceneWidget::Interactor : public IWorldSceneInteractor - { - public: - virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, - const ViewportGeometry& view, - MouseButton button, - KeyboardModifiers modifiers, - int viewportX, - int viewportY, - double x, - double y, - IStatusBar* statusBar, - const std::vector& touches) - { - if (statusBar) - { - char buf[64]; - sprintf(buf, "X = %0.2f, Y = %0.2f", x, y); - statusBar->SetMessage(buf); - } - - return NULL; - } - - virtual void MouseOver(CairoContext& context, - WorldSceneWidget& widget, - const ViewportGeometry& view, - double x, - double y, - IStatusBar* statusBar) - { - double S = 0.5; - - if (fabs(x) <= S && - fabs(y) <= S) - { - cairo_t* cr = context.GetObject(); - cairo_set_source_rgb(cr, 1, 0, 0); - cairo_rectangle(cr, -S, -S , 2.0 * S, 2.0 * S); - cairo_set_line_width(cr, 1.0 / view.GetZoom()); - cairo_stroke(cr); - } - } - - virtual void MouseWheel(WorldSceneWidget& widget, - MouseWheelDirection direction, - KeyboardModifiers modifiers, - IStatusBar* statusBar) - { - if (statusBar) - { - statusBar->SetMessage(direction == MouseWheelDirection_Down ? "Wheel down" : "Wheel up"); - } - } - - virtual void KeyPressed(WorldSceneWidget& widget, - KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers, - IStatusBar* statusBar) - { - if (statusBar) - { - statusBar->SetMessage("Key pressed: \"" + std::string(1, keyChar) + "\""); - } - } - }; - - - bool TestWorldSceneWidget::RenderScene(CairoContext& context, - const ViewportGeometry& view) - { - cairo_t* cr = context.GetObject(); - - // Clear background - cairo_set_source_rgb(cr, 0, 0, 0); - cairo_paint(cr); - - float color = static_cast(count_ % 16) / 15.0f; - cairo_set_source_rgb(cr, 0, 1.0f - color, color); - cairo_rectangle(cr, -10, -.5, 20, 1); - cairo_fill(cr); - - return true; - } - - - TestWorldSceneWidget::TestWorldSceneWidget(const std::string& name, bool animate) : - WorldSceneWidget(name), - interactor_(new Interactor), - animate_(animate), - count_(0) - { - SetInteractor(*interactor_); - } - - - Extent2D TestWorldSceneWidget::GetSceneExtent() - { - return Extent2D(-10, -.5, 10, .5); - } - - - void TestWorldSceneWidget::DoAnimation() - { - if (animate_) - { - count_++; - NotifyContentChanged(); - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - } - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/TestWorldSceneWidget.h --- a/Framework/Widgets/TestWorldSceneWidget.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "WorldSceneWidget.h" - -#include - -namespace OrthancStone -{ - namespace Samples - { - class TestWorldSceneWidget : public WorldSceneWidget - { - private: - class Interactor; - - std::auto_ptr interactor_; - bool animate_; - unsigned int count_; - - protected: - virtual bool RenderScene(CairoContext& context, - const ViewportGeometry& view); - - public: - TestWorldSceneWidget(const std::string& name, bool animate); - - virtual Extent2D GetSceneExtent(); - - virtual bool HasAnimation() const - { - return animate_; - } - - virtual void DoAnimation(); - - virtual bool HasRenderMouseOver() - { - return true; - } - }; - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/WidgetBase.cpp --- a/Framework/Widgets/WidgetBase.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,166 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "WidgetBase.h" - -#include -#include -#include - -namespace OrthancStone -{ - void WidgetBase::NotifyContentChanged() - { - if (parent_ != NULL) - { - parent_->NotifyContentChanged(); - } - - if (viewport_ != NULL) - { - viewport_->NotifyBackgroundChanged(); - } - } - - - void WidgetBase::SetParent(IWidget& parent) - { - if (parent_ != NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else - { - parent_ = &parent; - } - } - - - void WidgetBase::ClearBackgroundOrthanc(Orthanc::ImageAccessor& target) const - { - // Clear the background using Orthanc - - if (backgroundCleared_) - { - Orthanc::ImageProcessing::Set(target, - backgroundColor_[0], - backgroundColor_[1], - backgroundColor_[2], - 255 /* alpha */); - } - } - - - void WidgetBase::ClearBackgroundCairo(CairoContext& context) const - { - // Clear the background using Cairo - - if (IsBackgroundCleared()) - { - uint8_t red, green, blue; - GetBackgroundColor(red, green, blue); - - context.SetSourceColor(red, green, blue); - cairo_paint(context.GetObject()); - } - } - - - void WidgetBase::ClearBackgroundCairo(Orthanc::ImageAccessor& target) const - { - CairoSurface surface(target, false /* no alpha */); - CairoContext context(surface); - ClearBackgroundCairo(context); - } - - - void WidgetBase::UpdateStatusBar(const std::string& message) - { - if (statusBar_ != NULL) - { - statusBar_->SetMessage(message); - } - } - - - WidgetBase::WidgetBase(const std::string& name) : - parent_(NULL), - viewport_(NULL), - statusBar_(NULL), - backgroundCleared_(false), - transmitMouseOver_(false), - name_(name) - { - backgroundColor_[0] = 0; - backgroundColor_[1] = 0; - backgroundColor_[2] = 0; - } - - - void WidgetBase::SetViewport(WidgetViewport& viewport) - { - if (viewport_ != NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else - { - viewport_ = &viewport; - } - } - - - void WidgetBase::SetBackgroundColor(uint8_t red, - uint8_t green, - uint8_t blue) - { - backgroundColor_[0] = red; - backgroundColor_[1] = green; - backgroundColor_[2] = blue; - } - - void WidgetBase::GetBackgroundColor(uint8_t& red, - uint8_t& green, - uint8_t& blue) const - { - red = backgroundColor_[0]; - green = backgroundColor_[1]; - blue = backgroundColor_[2]; - } - - - bool WidgetBase::Render(Orthanc::ImageAccessor& surface) - { -#if 0 - ClearBackgroundOrthanc(surface); -#else - ClearBackgroundCairo(surface); // Faster than Orthanc -#endif - - return true; - } - - - void WidgetBase::DoAnimation() - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/WidgetBase.h --- a/Framework/Widgets/WidgetBase.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,117 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "IWidget.h" - -#include "../Viewport/CairoContext.h" -#include "../Viewport/WidgetViewport.h" - -namespace OrthancStone -{ - class WidgetBase : public IWidget - { - private: - IWidget* parent_; - WidgetViewport* viewport_; - IStatusBar* statusBar_; - bool backgroundCleared_; - uint8_t backgroundColor_[3]; - bool transmitMouseOver_; - std::string name_; - - protected: - void ClearBackgroundOrthanc(Orthanc::ImageAccessor& target) const; - - void ClearBackgroundCairo(CairoContext& context) const; - - void ClearBackgroundCairo(Orthanc::ImageAccessor& target) const; - - void UpdateStatusBar(const std::string& message); - - IStatusBar* GetStatusBar() const - { - return statusBar_; - } - - public: - WidgetBase(const std::string& name); - - virtual void FitContent() - { - } - - virtual void SetParent(IWidget& parent); - - virtual void SetViewport(WidgetViewport& viewport); - - void SetBackgroundCleared(bool clear) - { - backgroundCleared_ = clear; - } - - bool IsBackgroundCleared() const - { - return backgroundCleared_; - } - - void SetTransmitMouseOver(bool transmit) - { - transmitMouseOver_ = transmit; - } - - void SetBackgroundColor(uint8_t red, - uint8_t green, - uint8_t blue); - - void GetBackgroundColor(uint8_t& red, - uint8_t& green, - uint8_t& blue) const; - - virtual void SetStatusBar(IStatusBar& statusBar) - { - statusBar_ = &statusBar; - } - - virtual bool Render(Orthanc::ImageAccessor& surface); - - virtual bool HasAnimation() const - { - return false; - } - - virtual void DoAnimation(); - - virtual bool HasRenderMouseOver() - { - return transmitMouseOver_; - } - - virtual void NotifyContentChanged(); - - const std::string& GetName() const - { - return name_; - } - - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/WorldSceneWidget.cpp --- a/Framework/Widgets/WorldSceneWidget.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,231 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "WorldSceneWidget.h" - -#include "PanMouseTracker.h" -#include "ZoomMouseTracker.h" -#include "PanZoomMouseTracker.h" - -#include -#include - -#include -#include -#include - -namespace OrthancStone -{ - // this is an adapter between a IWorldSceneMouseTracker - // that is tracking a mouse in scene coordinates/mm and - // an IMouseTracker that is tracking a mouse - // in screen coordinates/pixels. - class WorldSceneWidget::SceneMouseTracker : public IMouseTracker - { - private: - ViewportGeometry view_; - std::auto_ptr tracker_; - - public: - SceneMouseTracker(const ViewportGeometry& view, - IWorldSceneMouseTracker* tracker) : - view_(view), - tracker_(tracker) - { - if (tracker == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - } - - virtual void Render(Orthanc::ImageAccessor& target) - { - if (tracker_->HasRender()) - { - CairoSurface surface(target, false /* no alpha */); - CairoContext context(surface); - view_.ApplyTransform(context); - tracker_->Render(context, view_.GetZoom()); - } - } - - virtual void MouseUp() - { - tracker_->MouseUp(); - } - - virtual void MouseMove(int x, - int y, - const std::vector& displayTouches) - { - double sceneX, sceneY; - view_.MapPixelCenterToScene(sceneX, sceneY, x, y); - - std::vector sceneTouches; - for (size_t t = 0; t < displayTouches.size(); t++) - { - double sx, sy; - - view_.MapPixelCenterToScene( - sx, sy, (int)displayTouches[t].x, (int)displayTouches[t].y); - - sceneTouches.push_back( - Touch(static_cast(sx), static_cast(sy))); - } - tracker_->MouseMove(x, y, sceneX, sceneY, displayTouches, sceneTouches); - } - }; - - - bool WorldSceneWidget::RenderCairo(CairoContext& context) - { - view_.ApplyTransform(context); - return RenderScene(context, view_); - } - - - void WorldSceneWidget::RenderMouseOverCairo(CairoContext& context, - int x, - int y) - { - ViewportGeometry view = GetView(); - view.ApplyTransform(context); - - double sceneX, sceneY; - view.MapPixelCenterToScene(sceneX, sceneY, x, y); - - if (interactor_) - { - interactor_->MouseOver(context, *this, view, sceneX, sceneY, GetStatusBar()); - } - } - - - void WorldSceneWidget::SetSceneExtent(ViewportGeometry& view) - { - view.SetSceneExtent(GetSceneExtent()); - } - - - void WorldSceneWidget::SetSize(unsigned int width, - unsigned int height) - { - CairoWidget::SetSize(width, height); - view_.SetDisplaySize(width, height); - } - - - void WorldSceneWidget::SetInteractor(IWorldSceneInteractor& interactor) - { - interactor_ = &interactor; - } - - - void WorldSceneWidget::FitContent() - { - SetSceneExtent(view_); - view_.FitContent(); - - NotifyContentChanged(); - } - - - void WorldSceneWidget::SetView(const ViewportGeometry& view) - { - view_ = view; - - NotifyContentChanged(); - } - - - IMouseTracker* WorldSceneWidget::CreateMouseTracker(MouseButton button, - int x, - int y, - KeyboardModifiers modifiers, - const std::vector& touches) - { - double sceneX, sceneY; - view_.MapPixelCenterToScene(sceneX, sceneY, x, y); - - // asks the Widget Interactor to provide a mouse tracker - std::auto_ptr tracker; - - if (interactor_) - { - tracker.reset(interactor_->CreateMouseTracker(*this, view_, button, modifiers, x, y, sceneX, sceneY, GetStatusBar(), touches)); - } - - if (tracker.get() != NULL) - { - return new SceneMouseTracker(view_, tracker.release()); - } - else if (hasDefaultMouseEvents_) - { - printf("has default mouse events\n"); - if (touches.size() == 2) - { - printf("2 touches !\n"); - return new SceneMouseTracker(view_, new PanZoomMouseTracker(*this, touches)); - } - else - { - switch (button) - { - case MouseButton_Middle: - return new SceneMouseTracker(view_, new PanMouseTracker(*this, x, y)); - - case MouseButton_Right: - return new SceneMouseTracker(view_, new ZoomMouseTracker(*this, x, y)); - - default: - return NULL; - } - } - } - else - { - return NULL; - } - } - - - void WorldSceneWidget::MouseWheel(MouseWheelDirection direction, - int x, - int y, - KeyboardModifiers modifiers) - { - if (interactor_) - { - interactor_->MouseWheel(*this, direction, modifiers, GetStatusBar()); - } - } - - - void WorldSceneWidget::KeyPressed(KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers) - { - if (interactor_) - { - interactor_->KeyPressed(*this, key, keyChar, modifiers, GetStatusBar()); - } - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/WorldSceneWidget.h --- a/Framework/Widgets/WorldSceneWidget.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,103 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "CairoWidget.h" -#include "IWorldSceneInteractor.h" - -#include "../Toolbox/ViewportGeometry.h" - -namespace OrthancStone -{ - class WorldSceneWidget : public CairoWidget - { - private: - class SceneMouseTracker; - - ViewportGeometry view_; - IWorldSceneInteractor* interactor_; - bool hasDefaultMouseEvents_; - - protected: - virtual Extent2D GetSceneExtent() = 0; - - virtual bool RenderScene(CairoContext& context, - const ViewportGeometry& view) = 0; - - // From CairoWidget - virtual bool RenderCairo(CairoContext& context); - - // From CairoWidget - virtual void RenderMouseOverCairo(CairoContext& context, - int x, - int y); - - void SetSceneExtent(ViewportGeometry& geometry); - - public: - WorldSceneWidget(const std::string& name) : - CairoWidget(name), - interactor_(NULL), - hasDefaultMouseEvents_(true) - { - } - - void SetDefaultMouseEvents(bool value) - { - hasDefaultMouseEvents_ = value; - } - - bool HasDefaultMouseEvents() const - { - return hasDefaultMouseEvents_; - } - - void SetInteractor(IWorldSceneInteractor& interactor); - - void SetView(const ViewportGeometry& view); - - const ViewportGeometry& GetView() const - { - return view_; - } - - virtual void SetSize(unsigned int width, - unsigned int height); - - virtual void FitContent(); - - virtual IMouseTracker* CreateMouseTracker(MouseButton button, - int x, - int y, - KeyboardModifiers modifiers, - const std::vector& touches); - - virtual void MouseWheel(MouseWheelDirection direction, - int x, - int y, - KeyboardModifiers modifiers); - - virtual void KeyPressed(KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers); - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/ZoomMouseTracker.cpp --- a/Framework/Widgets/ZoomMouseTracker.cpp Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,110 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "ZoomMouseTracker.h" - -#include -#include - -namespace OrthancStone -{ - ZoomMouseTracker::ZoomMouseTracker(WorldSceneWidget& that, - int x, - int y) : - that_(that), - originalZoom_(that.GetView().GetZoom()), - downX_(x), - downY_(y) - { - that.GetView().MapPixelCenterToScene(centerX_, centerY_, x, y); - - unsigned int height = that.GetView().GetDisplayHeight(); - - if (height <= 3) - { - idle_ = true; - LOG(WARNING) << "image is too small to zoom (current height = " << height << ")"; - } - else - { - idle_ = false; - normalization_ = 1.0 / static_cast(height - 1); - } - } - - - void ZoomMouseTracker::Render(CairoContext& context, - double zoom) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - - void ZoomMouseTracker::MouseMove(int displayX, - int displayY, - double x, - double y, - const std::vector& displayTouches, - const std::vector& sceneTouches) - { - static const double MIN_ZOOM = -4; - static const double MAX_ZOOM = 4; - - - if (!idle_) - { - double dy = static_cast(displayY - downY_) * normalization_; // In the range [-1,1] - double z; - - // Linear interpolation from [-1, 1] to [MIN_ZOOM, MAX_ZOOM] - if (dy < -1.0) - { - z = MIN_ZOOM; - } - else if (dy > 1.0) - { - z = MAX_ZOOM; - } - else - { - z = MIN_ZOOM + (MAX_ZOOM - MIN_ZOOM) * (dy + 1.0) / 2.0; - } - - z = pow(2.0, z); - - ViewportGeometry view = that_.GetView(); - - view.SetZoom(z * originalZoom_); - - // Correct the pan so that the original click point is kept at - // the same location on the display - double panX, panY; - view.GetPan(panX, panY); - - int tx, ty; - view.MapSceneToDisplay(tx, ty, centerX_, centerY_); - view.SetPan(panX + static_cast(downX_ - tx), - panY + static_cast(downY_ - ty)); - - that_.SetView(view); - } - } -} diff -r a386bbc955dc -r 92c400a09f1b Framework/Widgets/ZoomMouseTracker.h --- a/Framework/Widgets/ZoomMouseTracker.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "WorldSceneWidget.h" - -namespace OrthancStone -{ - class ZoomMouseTracker : public IWorldSceneMouseTracker - { - private: - WorldSceneWidget& that_; - double originalZoom_; - int downX_; - int downY_; - double centerX_; - double centerY_; - bool idle_; - double normalization_; - - public: - ZoomMouseTracker(WorldSceneWidget& that, - int x, - int y); - - virtual bool HasRender() const - { - return false; - } - - virtual void MouseUp() - { - } - - virtual void Render(CairoContext& context, - double zoom); - - virtual void MouseMove(int displayX, - int displayY, - double x, - double y, - const std::vector& displayTouches, - const std::vector& sceneTouches); - }; -} diff -r a386bbc955dc -r 92c400a09f1b Framework/dev.h --- a/Framework/dev.h Wed May 22 16:01:34 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,958 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "Layers/FrameRenderer.h" -#include "Layers/LineLayerRenderer.h" -#include "Layers/SliceOutlineRenderer.h" -#include "Toolbox/DownloadStack.h" -#include "Toolbox/GeometryToolbox.h" -#include "Toolbox/OrthancSlicesLoader.h" -#include "Volumes/ImageBuffer3D.h" -#include "Volumes/ISlicedVolume.h" -#include "Widgets/SliceViewerWidget.h" - -#include -#include -#include - -#include - - -namespace OrthancStone -{ - // TODO: Handle errors while loading - class OrthancVolumeImage : - public ISlicedVolume, - public IObserver - { - private: - OrthancSlicesLoader loader_; - std::auto_ptr image_; - std::auto_ptr downloadStack_; - bool computeRange_; - size_t pendingSlices_; - - void ScheduleSliceDownload() - { - assert(downloadStack_.get() != NULL); - - unsigned int slice; - if (downloadStack_->Pop(slice)) - { - loader_.ScheduleLoadSliceImage(slice, SliceImageQuality_Jpeg90); - } - } - - - static bool IsCompatible(const Deprecated::Slice& a, - const Deprecated::Slice& b) - { - if (!GeometryToolbox::IsParallel(a.GetGeometry().GetNormal(), - b.GetGeometry().GetNormal())) - { - LOG(ERROR) << "A slice in the volume image is not parallel to the others."; - return false; - } - - if (a.GetConverter().GetExpectedPixelFormat() != b.GetConverter().GetExpectedPixelFormat()) - { - LOG(ERROR) << "The pixel format changes across the slices of the volume image."; - return false; - } - - if (a.GetWidth() != b.GetWidth() || - a.GetHeight() != b.GetHeight()) - { - LOG(ERROR) << "The slices dimensions (width/height) are varying throughout the volume image"; - return false; - } - - if (!LinearAlgebra::IsNear(a.GetPixelSpacingX(), b.GetPixelSpacingX()) || - !LinearAlgebra::IsNear(a.GetPixelSpacingY(), b.GetPixelSpacingY())) - { - LOG(ERROR) << "The pixel spacing of the slices change across the volume image"; - return false; - } - - return true; - } - - - static double GetDistance(const Deprecated::Slice& a, - const Deprecated::Slice& b) - { - return fabs(a.GetGeometry().ProjectAlongNormal(a.GetGeometry().GetOrigin()) - - a.GetGeometry().ProjectAlongNormal(b.GetGeometry().GetOrigin())); - } - - - void OnSliceGeometryReady(const OrthancSlicesLoader::SliceGeometryReadyMessage& message) - { - assert(&message.GetOrigin() == &loader_); - - if (loader_.GetSlicesCount() == 0) - { - LOG(ERROR) << "Empty volume image"; - BroadcastMessage(ISlicedVolume::GeometryErrorMessage(*this)); - return; - } - - for (size_t i = 1; i < loader_.GetSlicesCount(); i++) - { - if (!IsCompatible(loader_.GetSlice(0), loader_.GetSlice(i))) - { - BroadcastMessage(ISlicedVolume::GeometryErrorMessage(*this)); - return; - } - } - - double spacingZ; - - if (loader_.GetSlicesCount() > 1) - { - spacingZ = GetDistance(loader_.GetSlice(0), loader_.GetSlice(1)); - } - else - { - // This is a volume with one single slice: Choose a dummy - // z-dimension for voxels - spacingZ = 1; - } - - for (size_t i = 1; i < loader_.GetSlicesCount(); i++) - { - if (!LinearAlgebra::IsNear(spacingZ, GetDistance(loader_.GetSlice(i - 1), loader_.GetSlice(i)), - 0.001 /* this is expressed in mm */)) - { - LOG(ERROR) << "The distance between successive slices is not constant in a volume image"; - BroadcastMessage(ISlicedVolume::GeometryErrorMessage(*this)); - return; - } - } - - unsigned int width = loader_.GetSlice(0).GetWidth(); - unsigned int height = loader_.GetSlice(0).GetHeight(); - Orthanc::PixelFormat format = loader_.GetSlice(0).GetConverter().GetExpectedPixelFormat(); - LOG(INFO) << "Creating a volume image of size " << width << "x" << height - << "x" << loader_.GetSlicesCount() << " in " << Orthanc::EnumerationToString(format); - - image_.reset(new ImageBuffer3D(format, width, height, static_cast(loader_.GetSlicesCount()), computeRange_)); - image_->GetGeometry().SetAxialGeometry(loader_.GetSlice(0).GetGeometry()); - image_->GetGeometry().SetVoxelDimensions(loader_.GetSlice(0).GetPixelSpacingX(), - loader_.GetSlice(0).GetPixelSpacingY(), spacingZ); - image_->Clear(); - - downloadStack_.reset(new DownloadStack(static_cast(loader_.GetSlicesCount()))); - pendingSlices_ = loader_.GetSlicesCount(); - - for (unsigned int i = 0; i < 4; i++) // Limit to 4 simultaneous downloads - { - ScheduleSliceDownload(); - } - - // TODO Check the DicomFrameConverter are constant - - BroadcastMessage(ISlicedVolume::GeometryReadyMessage(*this)); - } - - - void OnSliceGeometryError(const OrthancSlicesLoader::SliceGeometryErrorMessage& message) - { - assert(&message.GetOrigin() == &loader_); - - LOG(ERROR) << "Unable to download a volume image"; - BroadcastMessage(ISlicedVolume::GeometryErrorMessage(*this)); - } - - - void OnSliceImageReady(const OrthancSlicesLoader::SliceImageReadyMessage& message) - { - assert(&message.GetOrigin() == &loader_); - - { - ImageBuffer3D::SliceWriter writer(*image_, VolumeProjection_Axial, message.GetSliceIndex()); - Orthanc::ImageProcessing::Copy(writer.GetAccessor(), message.GetImage()); - } - - BroadcastMessage(ISlicedVolume::SliceContentChangedMessage - (*this, message.GetSliceIndex(), message.GetSlice())); - - if (pendingSlices_ == 1) - { - BroadcastMessage(ISlicedVolume::VolumeReadyMessage(*this)); - pendingSlices_ = 0; - } - else if (pendingSlices_ > 1) - { - pendingSlices_ -= 1; - } - - ScheduleSliceDownload(); - } - - - void OnSliceImageError(const OrthancSlicesLoader::SliceImageErrorMessage& message) - { - assert(&message.GetOrigin() == &loader_); - - LOG(ERROR) << "Cannot download slice " << message.GetSliceIndex() << " in a volume image"; - ScheduleSliceDownload(); - } - - - public: - OrthancVolumeImage(MessageBroker& broker, - OrthancApiClient& orthanc, - bool computeRange) : - ISlicedVolume(broker), - IObserver(broker), - loader_(broker, orthanc), - computeRange_(computeRange), - pendingSlices_(0) - { - loader_.RegisterObserverCallback( - new Callable - (*this, &OrthancVolumeImage::OnSliceGeometryReady)); - - loader_.RegisterObserverCallback( - new Callable - (*this, &OrthancVolumeImage::OnSliceGeometryError)); - - loader_.RegisterObserverCallback( - new Callable - (*this, &OrthancVolumeImage::OnSliceImageReady)); - - loader_.RegisterObserverCallback( - new Callable - (*this, &OrthancVolumeImage::OnSliceImageError)); - } - - void ScheduleLoadSeries(const std::string& seriesId) - { - loader_.ScheduleLoadSeries(seriesId); - } - - void ScheduleLoadInstance(const std::string& instanceId) - { - loader_.ScheduleLoadInstance(instanceId); - } - - void ScheduleLoadFrame(const std::string& instanceId, - unsigned int frame) - { - loader_.ScheduleLoadFrame(instanceId, frame); - } - - virtual size_t GetSlicesCount() const - { - return loader_.GetSlicesCount(); - } - - virtual const Deprecated::Slice& GetSlice(size_t index) const - { - return loader_.GetSlice(index); - } - - ImageBuffer3D& GetImage() const - { - if (image_.get() == NULL) - { - // The geometry is not ready yet - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else - { - return *image_; - } - } - - bool FitWindowingToRange(RenderStyle& style, - const Deprecated::DicomFrameConverter& converter) const - { - if (image_.get() == NULL) - { - return false; - } - else - { - return image_->FitWindowingToRange(style, converter); - } - } - }; - - - class OLD_VolumeImageGeometry - { - private: - unsigned int width_; - unsigned int height_; - size_t depth_; - double pixelSpacingX_; - double pixelSpacingY_; - double sliceThickness_; - CoordinateSystem3D reference_; - Deprecated::DicomFrameConverter converter_; - - double ComputeAxialThickness(const OrthancVolumeImage& volume) const - { - double thickness; - - size_t n = volume.GetSlicesCount(); - if (n > 1) - { - const Deprecated::Slice& a = volume.GetSlice(0); - const Deprecated::Slice& b = volume.GetSlice(n - 1); - thickness = ((reference_.ProjectAlongNormal(b.GetGeometry().GetOrigin()) - - reference_.ProjectAlongNormal(a.GetGeometry().GetOrigin())) / - (static_cast(n) - 1.0)); - } - else - { - thickness = volume.GetSlice(0).GetThickness(); - } - - if (thickness <= 0) - { - // The slices should have been sorted with increasing Z - // (along the normal) by the OrthancSlicesLoader - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - else - { - return thickness; - } - } - - void SetupAxial(const OrthancVolumeImage& volume) - { - const Deprecated::Slice& axial = volume.GetSlice(0); - - width_ = axial.GetWidth(); - height_ = axial.GetHeight(); - depth_ = volume.GetSlicesCount(); - - pixelSpacingX_ = axial.GetPixelSpacingX(); - pixelSpacingY_ = axial.GetPixelSpacingY(); - sliceThickness_ = ComputeAxialThickness(volume); - - reference_ = axial.GetGeometry(); - } - - void SetupCoronal(const OrthancVolumeImage& volume) - { - const Deprecated::Slice& axial = volume.GetSlice(0); - double axialThickness = ComputeAxialThickness(volume); - - width_ = axial.GetWidth(); - height_ = static_cast(volume.GetSlicesCount()); - depth_ = axial.GetHeight(); - - pixelSpacingX_ = axial.GetPixelSpacingX(); - pixelSpacingY_ = axialThickness; - sliceThickness_ = axial.GetPixelSpacingY(); - - Vector origin = axial.GetGeometry().GetOrigin(); - origin += (static_cast(volume.GetSlicesCount() - 1) * - axialThickness * axial.GetGeometry().GetNormal()); - - reference_ = CoordinateSystem3D(origin, - axial.GetGeometry().GetAxisX(), - - axial.GetGeometry().GetNormal()); - } - - void SetupSagittal(const OrthancVolumeImage& volume) - { - const Deprecated::Slice& axial = volume.GetSlice(0); - double axialThickness = ComputeAxialThickness(volume); - - width_ = axial.GetHeight(); - height_ = static_cast(volume.GetSlicesCount()); - depth_ = axial.GetWidth(); - - pixelSpacingX_ = axial.GetPixelSpacingY(); - pixelSpacingY_ = axialThickness; - sliceThickness_ = axial.GetPixelSpacingX(); - - Vector origin = axial.GetGeometry().GetOrigin(); - origin += (static_cast(volume.GetSlicesCount() - 1) * - axialThickness * axial.GetGeometry().GetNormal()); - - reference_ = CoordinateSystem3D(origin, - axial.GetGeometry().GetAxisY(), - axial.GetGeometry().GetNormal()); - } - - public: - OLD_VolumeImageGeometry(const OrthancVolumeImage& volume, - VolumeProjection projection) - { - if (volume.GetSlicesCount() == 0) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - converter_ = volume.GetSlice(0).GetConverter(); - - switch (projection) - { - case VolumeProjection_Axial: - SetupAxial(volume); - break; - - case VolumeProjection_Coronal: - SetupCoronal(volume); - break; - - case VolumeProjection_Sagittal: - SetupSagittal(volume); - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - } - - size_t GetSlicesCount() const - { - return depth_; - } - - const Vector& GetNormal() const - { - return reference_.GetNormal(); - } - - bool LookupSlice(size_t& index, - const CoordinateSystem3D& slice) const - { - bool opposite; - if (!GeometryToolbox::IsParallelOrOpposite(opposite, - reference_.GetNormal(), - slice.GetNormal())) - { - return false; - } - - double z = (reference_.ProjectAlongNormal(slice.GetOrigin()) - - reference_.ProjectAlongNormal(reference_.GetOrigin())) / sliceThickness_; - - int s = static_cast(boost::math::iround(z)); - - if (s < 0 || - s >= static_cast(depth_)) - { - return false; - } - else - { - index = static_cast(s); - return true; - } - } - - Deprecated::Slice* GetSlice(size_t slice) const - { - if (slice >= depth_) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - else - { - CoordinateSystem3D origin(reference_.GetOrigin() + - static_cast(slice) * sliceThickness_ * reference_.GetNormal(), - reference_.GetAxisX(), - reference_.GetAxisY()); - - return new Deprecated::Slice(origin, pixelSpacingX_, pixelSpacingY_, sliceThickness_, - width_, height_, converter_); - } - } - }; - - - - class VolumeImageMPRSlicer : - public IVolumeSlicer, - public IObserver - { - private: - class RendererFactory : public LayerReadyMessage::IRendererFactory - { - private: - const Orthanc::ImageAccessor& frame_; - const Deprecated::Slice& slice_; - bool isFullQuality_; - - public: - RendererFactory(const Orthanc::ImageAccessor& frame, - const Deprecated::Slice& slice, - bool isFullQuality) : - frame_(frame), - slice_(slice), - isFullQuality_(isFullQuality) - { - } - - virtual ILayerRenderer* CreateRenderer() const - { - return FrameRenderer::CreateRenderer(frame_, slice_, isFullQuality_); - } - }; - - - OrthancVolumeImage& volume_; - std::auto_ptr axialGeometry_; - std::auto_ptr coronalGeometry_; - std::auto_ptr sagittalGeometry_; - - - bool IsGeometryReady() const - { - return axialGeometry_.get() != NULL; - } - - void OnGeometryReady(const ISlicedVolume::GeometryReadyMessage& message) - { - assert(&message.GetOrigin() == &volume_); - - // These 3 values are only used to speed up the IVolumeSlicer - axialGeometry_.reset(new OLD_VolumeImageGeometry(volume_, VolumeProjection_Axial)); - coronalGeometry_.reset(new OLD_VolumeImageGeometry(volume_, VolumeProjection_Coronal)); - sagittalGeometry_.reset(new OLD_VolumeImageGeometry(volume_, VolumeProjection_Sagittal)); - - BroadcastMessage(IVolumeSlicer::GeometryReadyMessage(*this)); - } - - void OnGeometryError(const ISlicedVolume::GeometryErrorMessage& message) - { - assert(&message.GetOrigin() == &volume_); - - BroadcastMessage(IVolumeSlicer::GeometryErrorMessage(*this)); - } - - void OnContentChanged(const ISlicedVolume::ContentChangedMessage& message) - { - assert(&message.GetOrigin() == &volume_); - - BroadcastMessage(IVolumeSlicer::ContentChangedMessage(*this)); - } - - void OnSliceContentChanged(const ISlicedVolume::SliceContentChangedMessage& message) - { - assert(&message.GetOrigin() == &volume_); - - //IVolumeSlicer::OnSliceContentChange(slice); - - // TODO Improve this? - BroadcastMessage(IVolumeSlicer::ContentChangedMessage(*this)); - } - - const OLD_VolumeImageGeometry& GetProjectionGeometry(VolumeProjection projection) - { - if (!IsGeometryReady()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - switch (projection) - { - case VolumeProjection_Axial: - return *axialGeometry_; - - case VolumeProjection_Sagittal: - return *sagittalGeometry_; - - case VolumeProjection_Coronal: - return *coronalGeometry_; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - - - bool DetectProjection(VolumeProjection& projection, - const CoordinateSystem3D& viewportSlice) - { - bool isOpposite; // Ignored - - if (GeometryToolbox::IsParallelOrOpposite(isOpposite, - viewportSlice.GetNormal(), - axialGeometry_->GetNormal())) - { - projection = VolumeProjection_Axial; - return true; - } - else if (GeometryToolbox::IsParallelOrOpposite(isOpposite, - viewportSlice.GetNormal(), - sagittalGeometry_->GetNormal())) - { - projection = VolumeProjection_Sagittal; - return true; - } - else if (GeometryToolbox::IsParallelOrOpposite(isOpposite, - viewportSlice.GetNormal(), - coronalGeometry_->GetNormal())) - { - projection = VolumeProjection_Coronal; - return true; - } - else - { - return false; - } - } - - - public: - VolumeImageMPRSlicer(MessageBroker& broker, - OrthancVolumeImage& volume) : - IVolumeSlicer(broker), - IObserver(broker), - volume_(volume) - { - volume_.RegisterObserverCallback( - new Callable - (*this, &VolumeImageMPRSlicer::OnGeometryReady)); - - volume_.RegisterObserverCallback( - new Callable - (*this, &VolumeImageMPRSlicer::OnGeometryError)); - - volume_.RegisterObserverCallback( - new Callable - (*this, &VolumeImageMPRSlicer::OnContentChanged)); - - volume_.RegisterObserverCallback( - new Callable - (*this, &VolumeImageMPRSlicer::OnSliceContentChanged)); - } - - virtual bool GetExtent(std::vector& points, - const CoordinateSystem3D& viewportSlice) ORTHANC_OVERRIDE - { - VolumeProjection projection; - - if (!IsGeometryReady() || - !DetectProjection(projection, viewportSlice)) - { - return false; - } - else - { - // As the slices of the volumic image are arranged in a box, - // we only consider one single reference slice (the one with index 0). - std::auto_ptr slice(GetProjectionGeometry(projection).GetSlice(0)); - slice->GetExtent(points); - - return true; - } - } - - virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice) ORTHANC_OVERRIDE - { - VolumeProjection projection; - - if (IsGeometryReady() && - DetectProjection(projection, viewportSlice)) - { - const OLD_VolumeImageGeometry& geometry = GetProjectionGeometry(projection); - - size_t closest; - - if (geometry.LookupSlice(closest, viewportSlice)) - { - bool isFullQuality = true; // TODO - - std::auto_ptr frame; - - { - ImageBuffer3D::SliceReader reader(volume_.GetImage(), projection, static_cast(closest)); - - // TODO Transfer ownership if non-axial, to avoid memcpy - frame.reset(Orthanc::Image::Clone(reader.GetAccessor())); - } - - std::auto_ptr slice(geometry.GetSlice(closest)); - - RendererFactory factory(*frame, *slice, isFullQuality); - - BroadcastMessage(IVolumeSlicer::LayerReadyMessage(*this, factory, slice->GetGeometry())); - return; - } - } - - // Error - CoordinateSystem3D slice; - BroadcastMessage(IVolumeSlicer::LayerErrorMessage(*this, slice)); - } - }; - - - class VolumeImageInteractor : - public IWorldSceneInteractor, - public IObserver - { - private: - SliceViewerWidget& widget_; - VolumeProjection projection_; - std::auto_ptr slices_; - size_t slice_; - - protected: - void OnGeometryReady(const ISlicedVolume::GeometryReadyMessage& message) - { - if (slices_.get() == NULL) - { - const OrthancVolumeImage& image = - dynamic_cast(message.GetOrigin()); - - slices_.reset(new OLD_VolumeImageGeometry(image, projection_)); - SetSlice(slices_->GetSlicesCount() / 2); - - widget_.FitContent(); - } - } - - virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, - const ViewportGeometry& view, - MouseButton button, - KeyboardModifiers modifiers, - int viewportX, - int viewportY, - double x, - double y, - IStatusBar* statusBar, - const std::vector& touches) ORTHANC_OVERRIDE - { - return NULL; - } - - virtual void MouseOver(CairoContext& context, - WorldSceneWidget& widget, - const ViewportGeometry& view, - double x, - double y, - IStatusBar* statusBar) ORTHANC_OVERRIDE - { - } - - virtual void MouseWheel(WorldSceneWidget& widget, - MouseWheelDirection direction, - KeyboardModifiers modifiers, - IStatusBar* statusBar) ORTHANC_OVERRIDE - { - int scale = (modifiers & KeyboardModifiers_Control ? 10 : 1); - - switch (direction) - { - case MouseWheelDirection_Up: - OffsetSlice(-scale); - break; - - case MouseWheelDirection_Down: - OffsetSlice(scale); - break; - - default: - break; - } - } - - virtual void KeyPressed(WorldSceneWidget& widget, - KeyboardKeys key, - char keyChar, - KeyboardModifiers modifiers, - IStatusBar* statusBar) ORTHANC_OVERRIDE - { - switch (keyChar) - { - case 's': - widget.FitContent(); - break; - - default: - break; - } - } - - public: - VolumeImageInteractor(MessageBroker& broker, - OrthancVolumeImage& volume, - SliceViewerWidget& widget, - VolumeProjection projection) : - IObserver(broker), - widget_(widget), - projection_(projection) - { - widget.SetInteractor(*this); - - volume.RegisterObserverCallback( - new Callable - (*this, &VolumeImageInteractor::OnGeometryReady)); - } - - bool IsGeometryReady() const - { - return slices_.get() != NULL; - } - - size_t GetSlicesCount() const - { - if (slices_.get() == NULL) - { - return 0; - } - else - { - return slices_->GetSlicesCount(); - } - } - - void OffsetSlice(int offset) - { - if (slices_.get() != NULL) - { - int slice = static_cast(slice_) + offset; - - if (slice < 0) - { - slice = 0; - } - - if (slice >= static_cast(slices_->GetSlicesCount())) - { - slice = static_cast(slices_->GetSlicesCount()) - 1; - } - - if (slice != static_cast(slice_)) - { - SetSlice(slice); - } - } - } - - void SetSlice(size_t slice) - { - if (slices_.get() != NULL) - { - slice_ = slice; - - std::auto_ptr tmp(slices_->GetSlice(slice_)); - widget_.SetSlice(tmp->GetGeometry()); - } - } - }; - - - - class ReferenceLineSource : public IVolumeSlicer - { - private: - class RendererFactory : public LayerReadyMessage::IRendererFactory - { - private: - double x1_; - double y1_; - double x2_; - double y2_; - const CoordinateSystem3D& slice_; - - public: - RendererFactory(double x1, - double y1, - double x2, - double y2, - const CoordinateSystem3D& slice) : - x1_(x1), - y1_(y1), - x2_(x2), - y2_(y2), - slice_(slice) - { - } - - virtual ILayerRenderer* CreateRenderer() const - { - return new LineLayerRenderer(x1_, y1_, x2_, y2_, slice_); - } - }; - - SliceViewerWidget& otherPlane_; - - public: - ReferenceLineSource(MessageBroker& broker, - SliceViewerWidget& otherPlane) : - IVolumeSlicer(broker), - otherPlane_(otherPlane) - { - BroadcastMessage(IVolumeSlicer::GeometryReadyMessage(*this)); - } - - virtual bool GetExtent(std::vector& points, - const CoordinateSystem3D& viewportSlice) - { - return false; - } - - virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice) - { - Deprecated::Slice reference(viewportSlice, 0.001); - - Vector p, d; - - const CoordinateSystem3D& slice = otherPlane_.GetSlice(); - - // Compute the line of intersection between the two slices - if (!GeometryToolbox::IntersectTwoPlanes(p, d, - slice.GetOrigin(), slice.GetNormal(), - viewportSlice.GetOrigin(), viewportSlice.GetNormal())) - { - // The two slice are parallel, don't try and display the intersection - BroadcastMessage(IVolumeSlicer::LayerErrorMessage(*this, reference.GetGeometry())); - } - else - { - double x1, y1, x2, y2; - viewportSlice.ProjectPoint(x1, y1, p); - viewportSlice.ProjectPoint(x2, y2, p + 1000.0 * d); - - const Extent2D extent = otherPlane_.GetSceneExtent(); - - if (GeometryToolbox::ClipLineToRectangle(x1, y1, x2, y2, - x1, y1, x2, y2, - extent.GetX1(), extent.GetY1(), - extent.GetX2(), extent.GetY2())) - { - RendererFactory factory(x1, y1, x2, y2, slice); - BroadcastMessage(IVolumeSlicer::LayerReadyMessage(*this, factory, reference.GetGeometry())); - } - else - { - // Error: Parallel slices - BroadcastMessage(IVolumeSlicer::LayerErrorMessage(*this, reference.GetGeometry())); - } - } - } - }; -} diff -r a386bbc955dc -r 92c400a09f1b Platforms/Generic/DelayedCallCommand.cpp --- a/Platforms/Generic/DelayedCallCommand.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Generic/DelayedCallCommand.cpp Wed May 22 16:13:46 2019 +0200 @@ -24,13 +24,13 @@ #include -namespace OrthancStone +namespace Deprecated { - DelayedCallCommand::DelayedCallCommand(MessageBroker& broker, - MessageHandler* callback, // takes ownership + DelayedCallCommand::DelayedCallCommand(OrthancStone::MessageBroker& broker, + OrthancStone::MessageHandler* callback, // takes ownership unsigned int timeoutInMs, Orthanc::IDynamicObject* payload /* takes ownership */, - NativeStoneApplicationContext& context + OrthancStone::NativeStoneApplicationContext& context ) : IObservable(broker), callback_(callback), @@ -55,7 +55,7 @@ // We want to make sure that, i.e, the UpdateThread is not // triggered while we are updating the "model" with the result of // an OracleCommand - NativeStoneApplicationContext::GlobalMutexLocker lock(context_); + OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_); if (callback_.get() != NULL) { diff -r a386bbc955dc -r 92c400a09f1b Platforms/Generic/DelayedCallCommand.h --- a/Platforms/Generic/DelayedCallCommand.h Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Generic/DelayedCallCommand.h Wed May 22 16:13:46 2019 +0200 @@ -23,30 +23,30 @@ #include "IOracleCommand.h" -#include "../../Framework/Toolbox/IDelayedCallExecutor.h" +#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h" #include "../../Framework/Messages/IObservable.h" #include "../../Framework/Messages/ICallable.h" #include "../../Applications/Generic/NativeStoneApplicationContext.h" #include -namespace OrthancStone +namespace Deprecated { - class DelayedCallCommand : public IOracleCommand, IObservable + class DelayedCallCommand : public IOracleCommand, OrthancStone::IObservable { protected: - std::auto_ptr > callback_; + std::auto_ptr > callback_; std::auto_ptr payload_; - NativeStoneApplicationContext& context_; + OrthancStone::NativeStoneApplicationContext& context_; boost::posix_time::ptime expirationTimePoint_; unsigned int timeoutInMs_; public: - DelayedCallCommand(MessageBroker& broker, - MessageHandler* callback, // takes ownership + DelayedCallCommand(OrthancStone::MessageBroker& broker, + OrthancStone::MessageHandler* callback, // takes ownership unsigned int timeoutInMs, Orthanc::IDynamicObject* payload /* takes ownership */, - NativeStoneApplicationContext& context + OrthancStone::NativeStoneApplicationContext& context ); virtual void Execute(); diff -r a386bbc955dc -r 92c400a09f1b Platforms/Generic/IOracleCommand.h --- a/Platforms/Generic/IOracleCommand.h Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Generic/IOracleCommand.h Wed May 22 16:13:46 2019 +0200 @@ -23,7 +23,7 @@ #include -namespace OrthancStone +namespace Deprecated { class IOracleCommand : public Orthanc::IDynamicObject { diff -r a386bbc955dc -r 92c400a09f1b Platforms/Generic/Oracle.cpp --- a/Platforms/Generic/Oracle.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Generic/Oracle.cpp Wed May 22 16:13:46 2019 +0200 @@ -29,7 +29,7 @@ #include #include -namespace OrthancStone +namespace Deprecated { class Oracle::PImpl { diff -r a386bbc955dc -r 92c400a09f1b Platforms/Generic/Oracle.h --- a/Platforms/Generic/Oracle.h Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Generic/Oracle.h Wed May 22 16:13:46 2019 +0200 @@ -25,7 +25,7 @@ #include -namespace OrthancStone +namespace Deprecated { class Oracle : public boost::noncopyable { diff -r a386bbc955dc -r 92c400a09f1b Platforms/Generic/OracleDelayedCallExecutor.h --- a/Platforms/Generic/OracleDelayedCallExecutor.h Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Generic/OracleDelayedCallExecutor.h Wed May 22 16:13:46 2019 +0200 @@ -21,31 +21,31 @@ #pragma once -#include "../../Framework/Toolbox/IDelayedCallExecutor.h" +#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h" #include "Oracle.h" #include "../../Applications/Generic/NativeStoneApplicationContext.h" #include "DelayedCallCommand.h" -namespace OrthancStone +namespace Deprecated { // The OracleTimeout executes callbacks after a delay. class OracleDelayedCallExecutor : public IDelayedCallExecutor { private: Oracle& oracle_; - NativeStoneApplicationContext& context_; + OrthancStone::NativeStoneApplicationContext& context_; public: - OracleDelayedCallExecutor(MessageBroker& broker, + OracleDelayedCallExecutor(OrthancStone::MessageBroker& broker, Oracle& oracle, - NativeStoneApplicationContext& context) : + OrthancStone::NativeStoneApplicationContext& context) : IDelayedCallExecutor(broker), oracle_(oracle), context_(context) { } - virtual void Schedule(MessageHandler* callback, + virtual void Schedule(OrthancStone::MessageHandler* callback, unsigned int timeoutInMs = 1000) { oracle_.Submit(new DelayedCallCommand(broker_, callback, timeoutInMs, NULL, context_)); diff -r a386bbc955dc -r 92c400a09f1b Platforms/Generic/OracleWebService.cpp --- a/Platforms/Generic/OracleWebService.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Generic/OracleWebService.cpp Wed May 22 16:13:46 2019 +0200 @@ -20,26 +20,26 @@ #include "OracleWebService.h" -#include "../../Framework/Toolbox/IWebService.h" +#include "../../Framework/Deprecated/Toolbox/IWebService.h" -namespace OrthancStone +namespace Deprecated { - class OracleWebService::WebServiceCachedGetCommand : public IOracleCommand, IObservable + class OracleWebService::WebServiceCachedGetCommand : public IOracleCommand, OrthancStone::IObservable { protected: - std::auto_ptr > successCallback_; + std::auto_ptr > successCallback_; std::auto_ptr payload_; boost::shared_ptr cachedMessage_; - NativeStoneApplicationContext& context_; + OrthancStone::NativeStoneApplicationContext& context_; public: - WebServiceCachedGetCommand(MessageBroker& broker, - MessageHandler* successCallback, // takes ownership + WebServiceCachedGetCommand(OrthancStone::MessageBroker& broker, + OrthancStone::MessageHandler* successCallback, // takes ownership boost::shared_ptr cachedMessage, Orthanc::IDynamicObject* payload /* takes ownership */, - NativeStoneApplicationContext& context + OrthancStone::NativeStoneApplicationContext& context ) : IObservable(broker), successCallback_(successCallback), @@ -59,7 +59,7 @@ // We want to make sure that, i.e, the UpdateThread is not // triggered while we are updating the "model" with the result of // a WebServiceCommand - NativeStoneApplicationContext::GlobalMutexLocker lock(context_); + OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_); IWebService::HttpRequestSuccessMessage successMessage(cachedMessage_->GetUri(), cachedMessage_->GetAnswer(), @@ -73,7 +73,7 @@ void OracleWebService::NotifyHttpSuccessLater(boost::shared_ptr cachedMessage, Orthanc::IDynamicObject* payload, // takes ownership - MessageHandler* successCallback) + OrthancStone::MessageHandler* successCallback) { oracle_.Submit(new WebServiceCachedGetCommand(GetBroker(), successCallback, cachedMessage, payload, context_)); } diff -r a386bbc955dc -r 92c400a09f1b Platforms/Generic/OracleWebService.h --- a/Platforms/Generic/OracleWebService.h Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Generic/OracleWebService.h Wed May 22 16:13:46 2019 +0200 @@ -21,14 +21,14 @@ #pragma once -#include "../../Framework/Toolbox/BaseWebService.h" +#include "../../Framework/Deprecated/Toolbox/BaseWebService.h" #include "Oracle.h" #include "WebServiceGetCommand.h" #include "WebServicePostCommand.h" #include "WebServiceDeleteCommand.h" #include "../../Applications/Generic/NativeStoneApplicationContext.h" -namespace OrthancStone +namespace Deprecated { // The OracleWebService performs HTTP requests in a native environment. // It uses a thread pool to handle multiple HTTP requests in a same time. @@ -37,16 +37,16 @@ { private: Oracle& oracle_; - NativeStoneApplicationContext& context_; + OrthancStone::NativeStoneApplicationContext& context_; Orthanc::WebServiceParameters parameters_; class WebServiceCachedGetCommand; public: - OracleWebService(MessageBroker& broker, + OracleWebService(OrthancStone::MessageBroker& broker, Oracle& oracle, const Orthanc::WebServiceParameters& parameters, - NativeStoneApplicationContext& context) : + OrthancStone::NativeStoneApplicationContext& context) : BaseWebService(broker), oracle_(oracle), context_(context), @@ -58,8 +58,8 @@ const HttpHeaders& headers, const std::string& body, Orthanc::IDynamicObject* payload, // takes ownership - MessageHandler* successCallback, // takes ownership - MessageHandler* failureCallback = NULL, // takes ownership + OrthancStone::MessageHandler* successCallback, // takes ownership + OrthancStone::MessageHandler* failureCallback = NULL, // takes ownership unsigned int timeoutInSeconds = 60) { oracle_.Submit(new WebServicePostCommand(GetBroker(), successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, body, payload, context_)); @@ -68,8 +68,8 @@ virtual void DeleteAsync(const std::string& uri, const HttpHeaders& headers, Orthanc::IDynamicObject* payload, - MessageHandler* successCallback, - MessageHandler* failureCallback = NULL, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback = NULL, unsigned int timeoutInSeconds = 60) { oracle_.Submit(new WebServiceDeleteCommand(GetBroker(), successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, payload, context_)); @@ -79,8 +79,8 @@ virtual void GetAsyncInternal(const std::string& uri, const HttpHeaders& headers, Orthanc::IDynamicObject* payload, // takes ownership - MessageHandler* successCallback, // takes ownership - MessageHandler* failureCallback = NULL,// takes ownership + OrthancStone::MessageHandler* successCallback, // takes ownership + OrthancStone::MessageHandler* failureCallback = NULL,// takes ownership unsigned int timeoutInSeconds = 60) { oracle_.Submit(new WebServiceGetCommand(GetBroker(), successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, payload, context_)); @@ -88,7 +88,7 @@ virtual void NotifyHttpSuccessLater(boost::shared_ptr cachedHttpMessage, Orthanc::IDynamicObject* payload, // takes ownership - MessageHandler* successCallback); + OrthancStone::MessageHandler* successCallback); }; } diff -r a386bbc955dc -r 92c400a09f1b Platforms/Generic/WebServiceCommandBase.cpp --- a/Platforms/Generic/WebServiceCommandBase.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Generic/WebServiceCommandBase.cpp Wed May 22 16:13:46 2019 +0200 @@ -23,17 +23,17 @@ #include -namespace OrthancStone +namespace Deprecated { - WebServiceCommandBase::WebServiceCommandBase(MessageBroker& broker, - MessageHandler* successCallback, - MessageHandler* failureCallback, + WebServiceCommandBase::WebServiceCommandBase(OrthancStone::MessageBroker& broker, + OrthancStone::MessageHandler* successCallback, + OrthancStone::MessageHandler* failureCallback, const Orthanc::WebServiceParameters& parameters, const std::string& url, const IWebService::HttpHeaders& headers, unsigned int timeoutInSeconds, Orthanc::IDynamicObject* payload /* takes ownership */, - NativeStoneApplicationContext& context) : + OrthancStone::NativeStoneApplicationContext& context) : IObservable(broker), successCallback_(successCallback), failureCallback_(failureCallback), @@ -52,7 +52,7 @@ // We want to make sure that, i.e, the UpdateThread is not // triggered while we are updating the "model" with the result of // a WebServiceCommand - NativeStoneApplicationContext::GlobalMutexLocker lock(context_); + OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_); if (success_ && successCallback_.get() != NULL) { diff -r a386bbc955dc -r 92c400a09f1b Platforms/Generic/WebServiceCommandBase.h --- a/Platforms/Generic/WebServiceCommandBase.h Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Generic/WebServiceCommandBase.h Wed May 22 16:13:46 2019 +0200 @@ -23,7 +23,7 @@ #include "IOracleCommand.h" -#include "../../Framework/Toolbox/IWebService.h" +#include "../../Framework/Deprecated/Toolbox/IWebService.h" #include "../../Framework/Messages/IObservable.h" #include "../../Framework/Messages/ICallable.h" #include "../../Applications/Generic/NativeStoneApplicationContext.h" @@ -32,13 +32,13 @@ #include -namespace OrthancStone +namespace Deprecated { - class WebServiceCommandBase : public IOracleCommand, IObservable + class WebServiceCommandBase : public IOracleCommand, OrthancStone::IObservable { protected: - std::auto_ptr > successCallback_; - std::auto_ptr > failureCallback_; + std::auto_ptr > successCallback_; + std::auto_ptr > failureCallback_; Orthanc::WebServiceParameters parameters_; std::string url_; IWebService::HttpHeaders headers_; @@ -46,19 +46,19 @@ bool success_; std::string answer_; IWebService::HttpHeaders answerHeaders_; - NativeStoneApplicationContext& context_; + OrthancStone::NativeStoneApplicationContext& context_; unsigned int timeoutInSeconds_; public: - WebServiceCommandBase(MessageBroker& broker, - MessageHandler* successCallback, // takes ownership - MessageHandler* failureCallback, // takes ownership + WebServiceCommandBase(OrthancStone::MessageBroker& broker, + OrthancStone::MessageHandler* successCallback, // takes ownership + OrthancStone::MessageHandler* failureCallback, // takes ownership const Orthanc::WebServiceParameters& parameters, const std::string& url, const IWebService::HttpHeaders& headers, unsigned int timeoutInSeconds, Orthanc::IDynamicObject* payload /* takes ownership */, - NativeStoneApplicationContext& context + OrthancStone::NativeStoneApplicationContext& context ); virtual void Execute() = 0; diff -r a386bbc955dc -r 92c400a09f1b Platforms/Generic/WebServiceDeleteCommand.cpp --- a/Platforms/Generic/WebServiceDeleteCommand.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Generic/WebServiceDeleteCommand.cpp Wed May 22 16:13:46 2019 +0200 @@ -23,17 +23,17 @@ #include -namespace OrthancStone +namespace Deprecated { - WebServiceDeleteCommand::WebServiceDeleteCommand(MessageBroker& broker, - MessageHandler* successCallback, // takes ownership - MessageHandler* failureCallback, // takes ownership + WebServiceDeleteCommand::WebServiceDeleteCommand(OrthancStone::MessageBroker& broker, + OrthancStone::MessageHandler* successCallback, // takes ownership + OrthancStone::MessageHandler* failureCallback, // takes ownership const Orthanc::WebServiceParameters& parameters, const std::string& url, - const IWebService::HttpHeaders& headers, + const Deprecated::IWebService::HttpHeaders& headers, unsigned int timeoutInSeconds, Orthanc::IDynamicObject* payload /* takes ownership */, - NativeStoneApplicationContext& context) : + OrthancStone::NativeStoneApplicationContext& context) : WebServiceCommandBase(broker, successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context) { } @@ -45,7 +45,7 @@ client.SetTimeout(timeoutInSeconds_); client.SetMethod(Orthanc::HttpMethod_Delete); - for (IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ ) + for (Deprecated::IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ ) { client.AddHeader(it->first, it->second); } diff -r a386bbc955dc -r 92c400a09f1b Platforms/Generic/WebServiceDeleteCommand.h --- a/Platforms/Generic/WebServiceDeleteCommand.h Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Generic/WebServiceDeleteCommand.h Wed May 22 16:13:46 2019 +0200 @@ -23,20 +23,20 @@ #include "WebServiceCommandBase.h" -namespace OrthancStone +namespace Deprecated { class WebServiceDeleteCommand : public WebServiceCommandBase { public: - WebServiceDeleteCommand(MessageBroker& broker, - MessageHandler* successCallback, // takes ownership - MessageHandler* failureCallback, // takes ownership + WebServiceDeleteCommand(OrthancStone::MessageBroker& broker, + OrthancStone::MessageHandler* successCallback, // takes ownership + OrthancStone::MessageHandler* failureCallback, // takes ownership const Orthanc::WebServiceParameters& parameters, const std::string& url, const IWebService::HttpHeaders& headers, unsigned int timeoutInSeconds, Orthanc::IDynamicObject* payload /* takes ownership */, - NativeStoneApplicationContext& context); + OrthancStone::NativeStoneApplicationContext& context); virtual void Execute(); }; diff -r a386bbc955dc -r 92c400a09f1b Platforms/Generic/WebServiceGetCommand.cpp --- a/Platforms/Generic/WebServiceGetCommand.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Generic/WebServiceGetCommand.cpp Wed May 22 16:13:46 2019 +0200 @@ -23,18 +23,18 @@ #include -namespace OrthancStone +namespace Deprecated { - WebServiceGetCommand::WebServiceGetCommand(MessageBroker& broker, - MessageHandler* successCallback, // takes ownership - MessageHandler* failureCallback, // takes ownership + WebServiceGetCommand::WebServiceGetCommand(OrthancStone::MessageBroker& broker, + OrthancStone::MessageHandler* successCallback, // takes ownership + OrthancStone::MessageHandler* failureCallback, // takes ownership const Orthanc::WebServiceParameters& parameters, const std::string& url, const IWebService::HttpHeaders& headers, unsigned int timeoutInSeconds, Orthanc::IDynamicObject* payload /* takes ownership */, - NativeStoneApplicationContext& context) : + OrthancStone::NativeStoneApplicationContext& context) : WebServiceCommandBase(broker, successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context) { } diff -r a386bbc955dc -r 92c400a09f1b Platforms/Generic/WebServiceGetCommand.h --- a/Platforms/Generic/WebServiceGetCommand.h Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Generic/WebServiceGetCommand.h Wed May 22 16:13:46 2019 +0200 @@ -23,20 +23,20 @@ #include "WebServiceCommandBase.h" -namespace OrthancStone +namespace Deprecated { class WebServiceGetCommand : public WebServiceCommandBase { public: - WebServiceGetCommand(MessageBroker& broker, - MessageHandler* successCallback, // takes ownership - MessageHandler* failureCallback, // takes ownership + WebServiceGetCommand(OrthancStone::MessageBroker& broker, + OrthancStone::MessageHandler* successCallback, // takes ownership + OrthancStone::MessageHandler* failureCallback, // takes ownership const Orthanc::WebServiceParameters& parameters, const std::string& url, const IWebService::HttpHeaders& headers, unsigned int timeoutInSeconds, Orthanc::IDynamicObject* payload /* takes ownership */, - NativeStoneApplicationContext& context); + OrthancStone::NativeStoneApplicationContext& context); virtual void Execute(); }; diff -r a386bbc955dc -r 92c400a09f1b Platforms/Generic/WebServicePostCommand.cpp --- a/Platforms/Generic/WebServicePostCommand.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Generic/WebServicePostCommand.cpp Wed May 22 16:13:46 2019 +0200 @@ -23,18 +23,18 @@ #include -namespace OrthancStone +namespace Deprecated { - WebServicePostCommand::WebServicePostCommand(MessageBroker& broker, - MessageHandler* successCallback, // takes ownership - MessageHandler* failureCallback, // takes ownership + WebServicePostCommand::WebServicePostCommand(OrthancStone::MessageBroker& broker, + OrthancStone::MessageHandler* successCallback, // takes ownership + OrthancStone::MessageHandler* failureCallback, // takes ownership const Orthanc::WebServiceParameters& parameters, const std::string& url, - const IWebService::HttpHeaders& headers, + const Deprecated::IWebService::HttpHeaders& headers, unsigned int timeoutInSeconds, const std::string& body, Orthanc::IDynamicObject* payload /* takes ownership */, - NativeStoneApplicationContext& context) : + OrthancStone::NativeStoneApplicationContext& context) : WebServiceCommandBase(broker, successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context), body_(body) { @@ -48,7 +48,7 @@ client.SetMethod(Orthanc::HttpMethod_Post); client.GetBody().swap(body_); - for (IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ ) + for (Deprecated::IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ ) { client.AddHeader(it->first, it->second); } diff -r a386bbc955dc -r 92c400a09f1b Platforms/Generic/WebServicePostCommand.h --- a/Platforms/Generic/WebServicePostCommand.h Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Generic/WebServicePostCommand.h Wed May 22 16:13:46 2019 +0200 @@ -23,7 +23,7 @@ #include "WebServiceCommandBase.h" -namespace OrthancStone +namespace Deprecated { class WebServicePostCommand : public WebServiceCommandBase { @@ -31,16 +31,16 @@ std::string body_; public: - WebServicePostCommand(MessageBroker& broker, - MessageHandler* successCallback, // takes ownership - MessageHandler* failureCallback, // takes ownership + WebServicePostCommand(OrthancStone::MessageBroker& broker, + OrthancStone::MessageHandler* successCallback, // takes ownership + OrthancStone::MessageHandler* failureCallback, // takes ownership const Orthanc::WebServiceParameters& parameters, const std::string& url, const IWebService::HttpHeaders& headers, unsigned int timeoutInSeconds, const std::string& body, Orthanc::IDynamicObject* payload /* takes ownership */, - NativeStoneApplicationContext& context); + OrthancStone::NativeStoneApplicationContext& context); virtual void Execute(); }; diff -r a386bbc955dc -r 92c400a09f1b Platforms/Wasm/Defaults.cpp --- a/Platforms/Wasm/Defaults.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Wasm/Defaults.cpp Wed May 22 16:13:46 2019 +0200 @@ -2,9 +2,8 @@ #include "WasmWebService.h" #include "WasmDelayedCallExecutor.h" -#include -#include "Framework/Widgets/TestCairoWidget.h" -#include +#include "../../Framework/Deprecated/Widgets/TestCairoWidget.h" +#include #include #include #include @@ -26,16 +25,16 @@ static OrthancStone::ViewportContentChangedObserver viewportContentChangedObserver_(broker); static OrthancStone::StatusBar statusBar_; -static std::list> viewports_; +static std::list> viewports_; -std::shared_ptr FindViewportSharedPtr(ViewportHandle viewport) { +std::shared_ptr FindViewportSharedPtr(ViewportHandle viewport) { for (const auto& v : viewports_) { if (v.get() == viewport) { return v; } } assert(false); - return std::shared_ptr(); + return std::shared_ptr(); } #ifdef __cplusplus @@ -47,7 +46,7 @@ // when WASM needs a C++ viewport ViewportHandle EMSCRIPTEN_KEEPALIVE CreateCppViewport() { - std::shared_ptr viewport(new OrthancStone::WidgetViewport(broker)); + std::shared_ptr viewport(new Deprecated::WidgetViewport(broker)); printf("viewport %x\n", (int)viewport.get()); viewports_.push_back(viewport); @@ -57,7 +56,7 @@ viewport->SetStatusBar(statusBar_); viewport->RegisterObserverCallback( - new Callable + new Callable (viewportContentChangedObserver_, &ViewportContentChangedObserver::OnViewportChanged)); return viewport.get(); @@ -65,7 +64,7 @@ // when WASM does not need a viewport anymore, it should release it void EMSCRIPTEN_KEEPALIVE ReleaseCppViewport(ViewportHandle viewport) { - viewports_.remove_if([viewport](const std::shared_ptr& v) { return v.get() == viewport;}); + viewports_.remove_if([viewport](const std::shared_ptr& v) { return v.get() == viewport;}); printf("There are now %lu viewports in C++\n", viewports_.size()); } @@ -76,8 +75,8 @@ application.reset(CreateUserApplication(broker)); applicationWasmAdapter.reset(CreateWasmApplicationAdapter(broker, application.get())); - WasmWebService::SetBroker(broker); - WasmDelayedCallExecutor::SetBroker(broker); + Deprecated::WasmWebService::SetBroker(broker); + Deprecated::WasmDelayedCallExecutor::SetBroker(broker); startupParametersBuilder.Clear(); } @@ -104,8 +103,8 @@ context.reset(new OrthancStone::StoneApplicationContext(broker)); context->SetOrthancBaseUrl(baseUri); printf("Base URL to Orthanc API: [%s]\n", baseUri); - context->SetWebService(OrthancStone::WasmWebService::GetInstance()); - context->SetDelayedCallExecutor(OrthancStone::WasmDelayedCallExecutor::GetInstance()); + context->SetWebService(Deprecated::WasmWebService::GetInstance()); + context->SetDelayedCallExecutor(Deprecated::WasmDelayedCallExecutor::GetInstance()); application->Initialize(context.get(), statusBar_, parameters); application->InitializeWasm(); @@ -208,7 +207,7 @@ return; // Unknown button } - viewport->MouseDown(button, x, y, OrthancStone::KeyboardModifiers_None, std::vector()); + viewport->MouseDown(button, x, y, OrthancStone::KeyboardModifiers_None, std::vector()); } @@ -239,10 +238,10 @@ int x, int y) { - viewport->MouseMove(x, y, std::vector()); + viewport->MouseMove(x, y, std::vector()); } - void GetTouchVector(std::vector& output, + void GetTouchVector(std::vector& output, int touchCount, float x0, float y0, @@ -254,15 +253,15 @@ // TODO: it might be nice to try to pass all the x0,y0 coordinates as arrays but that's not so easy to pass array between JS and C++ if (touchCount > 0) { - output.push_back(OrthancStone::Touch(x0, y0)); + output.push_back(Deprecated::Touch(x0, y0)); } if (touchCount > 1) { - output.push_back(OrthancStone::Touch(x1, y1)); + output.push_back(Deprecated::Touch(x1, y1)); } if (touchCount > 2) { - output.push_back(OrthancStone::Touch(x2, y2)); + output.push_back(Deprecated::Touch(x2, y2)); } } @@ -278,7 +277,7 @@ { printf("touch start with %d touches\n", touchCount); - std::vector touches; + std::vector touches; GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2); viewport->TouchStart(touches); } @@ -294,7 +293,7 @@ { printf("touch move with %d touches\n", touchCount); - std::vector touches; + std::vector touches; GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2); viewport->TouchMove(touches); } @@ -310,7 +309,7 @@ { printf("touch end with %d touches remaining\n", touchCount); - std::vector touches; + std::vector touches; GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2); viewport->TouchEnd(touches); } diff -r a386bbc955dc -r 92c400a09f1b Platforms/Wasm/Defaults.h --- a/Platforms/Wasm/Defaults.h Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Wasm/Defaults.h Wed May 22 16:13:46 2019 +0200 @@ -2,13 +2,12 @@ #include -#include -#include -#include +#include "../../Framework/Deprecated/Viewport/WidgetViewport.h" +#include "../../Framework/Deprecated/Widgets/LayoutWidget.h" #include #include -typedef OrthancStone::WidgetViewport* ViewportHandle; // the objects exchanged between JS and C++ +typedef Deprecated::WidgetViewport* ViewportHandle; // the objects exchanged between JS and C++ #ifdef __cplusplus extern "C" { @@ -57,7 +56,7 @@ isScheduled_ = false; } - void OnViewportChanged(const IViewport::ViewportChangedMessage& message) + void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message) { if (!isScheduled_) { @@ -68,7 +67,7 @@ }; // default status bar to log messages on the console/stdout - class StatusBar : public OrthancStone::IStatusBar + class StatusBar : public Deprecated::IStatusBar { public: virtual void ClearMessage() diff -r a386bbc955dc -r 92c400a09f1b Platforms/Wasm/WasmDelayedCallExecutor.cpp --- a/Platforms/Wasm/WasmDelayedCallExecutor.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Wasm/WasmDelayedCallExecutor.cpp Wed May 22 16:13:46 2019 +0200 @@ -21,8 +21,8 @@ } else { - reinterpret_cast*>(callable)-> - Apply(OrthancStone::IDelayedCallExecutor::TimeoutMessage()); // uri, reinterpret_cast(payload))); + reinterpret_cast*>(callable)-> + Apply(Deprecated::IDelayedCallExecutor::TimeoutMessage()); // uri, reinterpret_cast(payload))); } } @@ -33,12 +33,12 @@ -namespace OrthancStone +namespace Deprecated { - MessageBroker* WasmDelayedCallExecutor::broker_ = NULL; + OrthancStone::MessageBroker* WasmDelayedCallExecutor::broker_ = NULL; - void WasmDelayedCallExecutor::Schedule(MessageHandler* callback, + void WasmDelayedCallExecutor::Schedule(OrthancStone::MessageHandler* callback, unsigned int timeoutInMs) { WasmDelayedCallExecutor_Schedule(callback, timeoutInMs); diff -r a386bbc955dc -r 92c400a09f1b Platforms/Wasm/WasmDelayedCallExecutor.h --- a/Platforms/Wasm/WasmDelayedCallExecutor.h Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Wasm/WasmDelayedCallExecutor.h Wed May 22 16:13:46 2019 +0200 @@ -1,17 +1,17 @@ #pragma once -#include +#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h" #include -namespace OrthancStone +namespace Deprecated { class WasmDelayedCallExecutor : public IDelayedCallExecutor { private: - static MessageBroker* broker_; + static OrthancStone::MessageBroker* broker_; // Private constructor => Singleton design pattern - WasmDelayedCallExecutor(MessageBroker& broker) : + WasmDelayedCallExecutor(OrthancStone::MessageBroker& broker) : IDelayedCallExecutor(broker) { } @@ -28,12 +28,12 @@ return instance; } - static void SetBroker(MessageBroker& broker) + static void SetBroker(OrthancStone::MessageBroker& broker) { broker_ = &broker; } - virtual void Schedule(MessageHandler* callback, + virtual void Schedule(OrthancStone::MessageHandler* callback, unsigned int timeoutInMs = 1000); }; diff -r a386bbc955dc -r 92c400a09f1b Platforms/Wasm/WasmViewport.cpp --- a/Platforms/Wasm/WasmViewport.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Wasm/WasmViewport.cpp Wed May 22 16:13:46 2019 +0200 @@ -3,11 +3,11 @@ #include #include -std::vector> wasmViewports; +std::vector> wasmViewports; -void AttachWidgetToWasmViewport(const char* htmlCanvasId, OrthancStone::IWidget* centralWidget) { - std::shared_ptr viewport(CreateWasmViewportFromCpp(htmlCanvasId)); +void AttachWidgetToWasmViewport(const char* htmlCanvasId, Deprecated::IWidget* centralWidget) { + std::shared_ptr viewport(CreateWasmViewportFromCpp(htmlCanvasId)); viewport->SetCentralWidget(centralWidget); wasmViewports.push_back(viewport); -} \ No newline at end of file +} diff -r a386bbc955dc -r 92c400a09f1b Platforms/Wasm/WasmViewport.h --- a/Platforms/Wasm/WasmViewport.h Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Wasm/WasmViewport.h Wed May 22 16:13:46 2019 +0200 @@ -1,6 +1,6 @@ #pragma once -#include +#include "../../Framework/Deprecated/Viewport/WidgetViewport.h" #include @@ -9,10 +9,10 @@ #endif // JS methods accessible from C++ - extern OrthancStone::WidgetViewport* CreateWasmViewportFromCpp(const char* htmlCanvasId); + extern Deprecated::WidgetViewport* CreateWasmViewportFromCpp(const char* htmlCanvasId); #ifdef __cplusplus } #endif -extern void AttachWidgetToWasmViewport(const char* htmlCanvasId, OrthancStone::IWidget* centralWidget); +extern void AttachWidgetToWasmViewport(const char* htmlCanvasId, Deprecated::IWidget* centralWidget); diff -r a386bbc955dc -r 92c400a09f1b Platforms/Wasm/WasmWebService.cpp --- a/Platforms/Wasm/WasmWebService.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Wasm/WasmWebService.cpp Wed May 22 16:13:46 2019 +0200 @@ -6,9 +6,9 @@ struct CachedSuccessNotification { - boost::shared_ptr cachedMessage; + boost::shared_ptr cachedMessage; std::auto_ptr payload; - OrthancStone::MessageHandler* successCallback; + OrthancStone::MessageHandler* successCallback; }; @@ -47,8 +47,8 @@ { if (failureCallable != NULL) { - reinterpret_cast*>(failureCallable)-> - Apply(OrthancStone::IWebService::HttpRequestErrorMessage(uri, reinterpret_cast(payload))); + reinterpret_cast*>(failureCallable)-> + Apply(Deprecated::IWebService::HttpRequestErrorMessage(uri, reinterpret_cast(payload))); } } @@ -57,7 +57,7 @@ // notification has been allocated in C++ and passed to JS. It must be deleted by this method std::auto_ptr notification(reinterpret_cast(notification_)); - notification->successCallback->Apply(OrthancStone::IWebService::HttpRequestSuccessMessage( + notification->successCallback->Apply(Deprecated::IWebService::HttpRequestSuccessMessage( notification->cachedMessage->GetUri(), notification->cachedMessage->GetAnswer(), notification->cachedMessage->GetAnswerSize(), @@ -75,13 +75,13 @@ { if (successCallable != NULL) { - OrthancStone::IWebService::HttpHeaders headers; + Deprecated::IWebService::HttpHeaders headers; // TODO - Parse "answerHeaders" //printf("TODO: parse headers [%s]\n", answerHeaders); - reinterpret_cast*>(successCallable)-> - Apply(OrthancStone::IWebService::HttpRequestSuccessMessage(uri, body, bodySize, headers, + reinterpret_cast*>(successCallable)-> + Apply(Deprecated::IWebService::HttpRequestSuccessMessage(uri, body, bodySize, headers, reinterpret_cast(payload))); } } @@ -92,9 +92,9 @@ -namespace OrthancStone +namespace Deprecated { - MessageBroker* WasmWebService::broker_ = NULL; + OrthancStone::MessageBroker* WasmWebService::broker_ = NULL; void ToJsonString(std::string& output, const IWebService::HttpHeaders& headers) { @@ -116,8 +116,8 @@ const HttpHeaders& headers, const std::string& body, Orthanc::IDynamicObject* payload, - MessageHandler* successCallable, - MessageHandler* failureCallable, + OrthancStone::MessageHandler* successCallable, + OrthancStone::MessageHandler* failureCallable, unsigned int timeoutInSeconds) { std::string headersInJsonString; @@ -129,8 +129,8 @@ void WasmWebService::DeleteAsync(const std::string& relativeUri, const HttpHeaders& headers, Orthanc::IDynamicObject* payload, - MessageHandler* successCallable, - MessageHandler* failureCallable, + OrthancStone::MessageHandler* successCallable, + OrthancStone::MessageHandler* failureCallable, unsigned int timeoutInSeconds) { std::string headersInJsonString; @@ -142,8 +142,8 @@ void WasmWebService::GetAsyncInternal(const std::string &relativeUri, const HttpHeaders &headers, Orthanc::IDynamicObject *payload, - MessageHandler *successCallable, - MessageHandler *failureCallable, + OrthancStone::MessageHandler *successCallable, + OrthancStone::MessageHandler *failureCallable, unsigned int timeoutInSeconds) { std::string headersInJsonString; @@ -153,8 +153,8 @@ } void WasmWebService::NotifyHttpSuccessLater(boost::shared_ptr cachedMessage, - Orthanc::IDynamicObject* payload, // takes ownership - MessageHandler* successCallback) + Orthanc::IDynamicObject* payload, // takes ownership + OrthancStone::MessageHandler* successCallback) { CachedSuccessNotification* notification = new CachedSuccessNotification(); // allocated on the heap, it will be passed to JS and deleted when coming back to C++ notification->cachedMessage = cachedMessage; diff -r a386bbc955dc -r 92c400a09f1b Platforms/Wasm/WasmWebService.h --- a/Platforms/Wasm/WasmWebService.h Wed May 22 16:01:34 2019 +0200 +++ b/Platforms/Wasm/WasmWebService.h Wed May 22 16:13:46 2019 +0200 @@ -1,17 +1,17 @@ #pragma once -#include +#include "../../Framework/Deprecated/Toolbox/BaseWebService.h" #include -namespace OrthancStone +namespace Deprecated { class WasmWebService : public BaseWebService { private: - static MessageBroker *broker_; + static OrthancStone::MessageBroker *broker_; // Private constructor => Singleton design pattern - WasmWebService(MessageBroker &broker) : BaseWebService(broker) + WasmWebService(OrthancStone::MessageBroker &broker) : BaseWebService(broker) { } @@ -27,7 +27,7 @@ return instance; } - static void SetBroker(MessageBroker &broker) + static void SetBroker(OrthancStone::MessageBroker &broker) { broker_ = &broker; } @@ -36,27 +36,27 @@ const HttpHeaders &headers, const std::string &body, Orthanc::IDynamicObject *payload, - MessageHandler *successCallable, - MessageHandler *failureCallable = NULL, + OrthancStone::MessageHandler *successCallable, + OrthancStone::MessageHandler *failureCallable = NULL, unsigned int timeoutInSeconds = 60); virtual void DeleteAsync(const std::string &uri, const HttpHeaders &headers, Orthanc::IDynamicObject *payload, - MessageHandler *successCallable, - MessageHandler *failureCallable = NULL, + OrthancStone::MessageHandler *successCallable, + OrthancStone::MessageHandler *failureCallable = NULL, unsigned int timeoutInSeconds = 60); protected: virtual void GetAsyncInternal(const std::string &uri, const HttpHeaders &headers, Orthanc::IDynamicObject *payload, - MessageHandler *successCallable, - MessageHandler *failureCallable = NULL, + OrthancStone::MessageHandler *successCallable, + OrthancStone::MessageHandler *failureCallable = NULL, unsigned int timeoutInSeconds = 60); virtual void NotifyHttpSuccessLater(boost::shared_ptr cachedHttpMessage, Orthanc::IDynamicObject *payload, // takes ownership - MessageHandler *successCallback); + OrthancStone::MessageHandler *successCallback); }; -} // namespace OrthancStone +} // namespace Deprecated diff -r a386bbc955dc -r 92c400a09f1b Resources/CMake/OrthancStoneConfiguration.cmake --- a/Resources/CMake/OrthancStoneConfiguration.cmake Wed May 22 16:01:34 2019 +0200 +++ b/Resources/CMake/OrthancStoneConfiguration.cmake Wed May 22 16:13:46 2019 +0200 @@ -32,7 +32,6 @@ include(${ORTHANC_ROOT}/Resources/CMake/OrthancFrameworkConfiguration.cmake) include_directories(${ORTHANC_ROOT}) -include_directories(${ORTHANC_ROOT}/Core/Images) # hack for the numerous #include "../Enumerations.h" in Orthanc to work ##################################################################### @@ -110,7 +109,7 @@ message("SDL is enabled") include(${CMAKE_CURRENT_LIST_DIR}/SdlConfiguration.cmake) add_definitions( - -DORTHANC_ENABLE_NATIVE=1 + -DORTHANC_ENABLE_THREADS=1 -DORTHANC_ENABLE_QT=0 -DORTHANC_ENABLE_SDL=1 ) @@ -118,7 +117,7 @@ message("QT is enabled") include(${CMAKE_CURRENT_LIST_DIR}/QtConfiguration.cmake) add_definitions( - -DORTHANC_ENABLE_NATIVE=1 + -DORTHANC_ENABLE_THREADS=1 -DORTHANC_ENABLE_QT=1 -DORTHANC_ENABLE_SDL=0 ) @@ -128,7 +127,7 @@ add_definitions( -DORTHANC_ENABLE_SDL=0 -DORTHANC_ENABLE_QT=0 - -DORTHANC_ENABLE_NATIVE=0 + -DORTHANC_ENABLE_THREADS=0 ) endif() @@ -305,6 +304,58 @@ DEPENDS "${ORTHANC_STONE_ROOT}/Platforms/Wasm/default-library.js") endif() + +if (ENABLE_STONE_DEPRECATED) + list(APPEND ORTHANC_STONE_SOURCES + ${ORTHANC_ROOT}/Plugins/Samples/Common/DicomDatasetReader.cpp + ${ORTHANC_ROOT}/Plugins/Samples/Common/DicomPath.cpp + ${ORTHANC_ROOT}/Plugins/Samples/Common/FullOrthancDataset.cpp + ${ORTHANC_ROOT}/Plugins/Samples/Common/IOrthancConnection.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/CircleMeasureTracker.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/ColorFrameRenderer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/DicomSeriesVolumeSlicer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/DicomStructureSetSlicer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/FrameRenderer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/GrayscaleFrameRenderer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/IVolumeSlicer.h + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/LineLayerRenderer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/LineMeasureTracker.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/RenderStyle.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/SliceOutlineRenderer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/SmartLoader.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/BaseWebService.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/DicomFrameConverter.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/DownloadStack.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/IDelayedCallExecutor.h + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/IWebService.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/OrthancApiClient.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/OrthancSlicesLoader.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/Slice.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/ViewportGeometry.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/IMouseTracker.h + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/IStatusBar.h + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/IViewport.h + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/WidgetViewport.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Volumes/StructureSetLoader.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/CairoWidget.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/EmptyWidget.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/IWidget.h + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/IWorldSceneInteractor.h + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/IWorldSceneMouseTracker.h + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/LayoutWidget.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/PanMouseTracker.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/PanZoomMouseTracker.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/SliceViewerWidget.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/TestCairoWidget.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/TestWorldSceneWidget.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/WidgetBase.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/WorldSceneWidget.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/ZoomMouseTracker.cpp + ${ORTHANC_STONE_ROOT}/Framework/Deprecated/dev.h + ) +endif() + + list(APPEND ORTHANC_STONE_SOURCES #${ORTHANC_STONE_ROOT}/Framework/Layers/SeriesFrameRendererFactory.cpp #${ORTHANC_STONE_ROOT}/Framework/Layers/SingleFrameRendererFactory.cpp @@ -362,7 +413,7 @@ ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/OneGesturePointerTracker.h ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/PointerTypes.h ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/ViewportController.cpp - ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/ViewportController.h + ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/ViewportController.h ${ORTHANC_STONE_ROOT}/Framework/Fonts/FontRenderer.cpp ${ORTHANC_STONE_ROOT}/Framework/Fonts/Glyph.cpp @@ -370,19 +421,20 @@ ${ORTHANC_STONE_ROOT}/Framework/Fonts/GlyphBitmapAlphabet.cpp ${ORTHANC_STONE_ROOT}/Framework/Fonts/GlyphTextureAlphabet.cpp ${ORTHANC_STONE_ROOT}/Framework/Fonts/TextBoundingBox.cpp - ${ORTHANC_STONE_ROOT}/Framework/Layers/CircleMeasureTracker.cpp - ${ORTHANC_STONE_ROOT}/Framework/Layers/ColorFrameRenderer.cpp - ${ORTHANC_STONE_ROOT}/Framework/Layers/DicomSeriesVolumeSlicer.cpp - ${ORTHANC_STONE_ROOT}/Framework/Layers/DicomStructureSetSlicer.cpp - ${ORTHANC_STONE_ROOT}/Framework/Layers/FrameRenderer.cpp - ${ORTHANC_STONE_ROOT}/Framework/Layers/GrayscaleFrameRenderer.cpp - ${ORTHANC_STONE_ROOT}/Framework/Layers/IVolumeSlicer.h - ${ORTHANC_STONE_ROOT}/Framework/Layers/LineLayerRenderer.cpp - ${ORTHANC_STONE_ROOT}/Framework/Layers/LineMeasureTracker.cpp - ${ORTHANC_STONE_ROOT}/Framework/Layers/RenderStyle.cpp - ${ORTHANC_STONE_ROOT}/Framework/Layers/SliceOutlineRenderer.cpp ${ORTHANC_STONE_ROOT}/Framework/Loaders/BasicFetchingItemsSorter.cpp ${ORTHANC_STONE_ROOT}/Framework/Loaders/BasicFetchingStrategy.cpp + ${ORTHANC_STONE_ROOT}/Framework/Messages/ICallable.h + ${ORTHANC_STONE_ROOT}/Framework/Messages/IMessage.h + ${ORTHANC_STONE_ROOT}/Framework/Messages/IObservable.cpp + ${ORTHANC_STONE_ROOT}/Framework/Messages/IObserver.h + ${ORTHANC_STONE_ROOT}/Framework/Messages/MessageBroker.h + ${ORTHANC_STONE_ROOT}/Framework/Messages/MessageForwarder.cpp + ${ORTHANC_STONE_ROOT}/Framework/Messages/Promise.h + ${ORTHANC_STONE_ROOT}/Framework/Oracle/GetOrthancImageCommand.cpp + ${ORTHANC_STONE_ROOT}/Framework/Oracle/GetOrthancWebViewerJpegCommand.cpp + ${ORTHANC_STONE_ROOT}/Framework/Oracle/OracleCommandWithPayload.cpp + ${ORTHANC_STONE_ROOT}/Framework/Oracle/OrthancRestApiCommand.cpp + ${ORTHANC_STONE_ROOT}/Framework/Oracle/ThreadedOracle.cpp ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyAlphaLayer.cpp ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyDicomLayer.cpp ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyLayer.cpp @@ -399,75 +451,32 @@ ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyTextLayer.cpp ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyWidget.cpp ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyWindowingTracker.cpp - ${ORTHANC_STONE_ROOT}/Framework/SmartLoader.cpp ${ORTHANC_STONE_ROOT}/Framework/StoneEnumerations.cpp ${ORTHANC_STONE_ROOT}/Framework/StoneException.h ${ORTHANC_STONE_ROOT}/Framework/StoneInitialization.cpp ${ORTHANC_STONE_ROOT}/Framework/Toolbox/AffineTransform2D.cpp - ${ORTHANC_STONE_ROOT}/Framework/Toolbox/BaseWebService.cpp ${ORTHANC_STONE_ROOT}/Framework/Toolbox/CoordinateSystem3D.cpp - ${ORTHANC_STONE_ROOT}/Framework/Toolbox/DicomFrameConverter.cpp + ${ORTHANC_STONE_ROOT}/Framework/Toolbox/DicomInstanceParameters.cpp ${ORTHANC_STONE_ROOT}/Framework/Toolbox/DicomStructureSet.cpp - ${ORTHANC_STONE_ROOT}/Framework/Toolbox/DownloadStack.cpp ${ORTHANC_STONE_ROOT}/Framework/Toolbox/DynamicBitmap.cpp ${ORTHANC_STONE_ROOT}/Framework/Toolbox/Extent2D.cpp ${ORTHANC_STONE_ROOT}/Framework/Toolbox/FiniteProjectiveCamera.cpp ${ORTHANC_STONE_ROOT}/Framework/Toolbox/GeometryToolbox.cpp - ${ORTHANC_STONE_ROOT}/Framework/Toolbox/IDelayedCallExecutor.h - ${ORTHANC_STONE_ROOT}/Framework/Toolbox/IWebService.cpp ${ORTHANC_STONE_ROOT}/Framework/Toolbox/ImageGeometry.cpp ${ORTHANC_STONE_ROOT}/Framework/Toolbox/LinearAlgebra.cpp ${ORTHANC_STONE_ROOT}/Framework/Toolbox/MessagingToolbox.cpp - ${ORTHANC_STONE_ROOT}/Framework/Toolbox/OrientedBoundingBox.cpp - ${ORTHANC_STONE_ROOT}/Framework/Toolbox/OrthancApiClient.cpp - ${ORTHANC_STONE_ROOT}/Framework/Toolbox/OrthancSlicesLoader.cpp ${ORTHANC_STONE_ROOT}/Framework/Toolbox/ParallelSlices.cpp ${ORTHANC_STONE_ROOT}/Framework/Toolbox/ParallelSlicesCursor.cpp ${ORTHANC_STONE_ROOT}/Framework/Toolbox/ShearWarpProjectiveTransform.cpp - ${ORTHANC_STONE_ROOT}/Framework/Toolbox/Slice.cpp ${ORTHANC_STONE_ROOT}/Framework/Toolbox/SlicesSorter.cpp ${ORTHANC_STONE_ROOT}/Framework/Toolbox/UndoRedoStack.cpp - ${ORTHANC_STONE_ROOT}/Framework/Toolbox/ViewportGeometry.cpp - ${ORTHANC_STONE_ROOT}/Framework/Toolbox/VolumeImageGeometry.cpp ${ORTHANC_STONE_ROOT}/Framework/Viewport/CairoContext.cpp ${ORTHANC_STONE_ROOT}/Framework/Viewport/CairoSurface.cpp - ${ORTHANC_STONE_ROOT}/Framework/Viewport/IMouseTracker.h - ${ORTHANC_STONE_ROOT}/Framework/Viewport/IStatusBar.h - ${ORTHANC_STONE_ROOT}/Framework/Viewport/IViewport.h - ${ORTHANC_STONE_ROOT}/Framework/Viewport/WidgetViewport.cpp ${ORTHANC_STONE_ROOT}/Framework/Volumes/ImageBuffer3D.cpp - ${ORTHANC_STONE_ROOT}/Framework/Volumes/StructureSetLoader.cpp + ${ORTHANC_STONE_ROOT}/Framework/Volumes/OrientedVolumeBoundingBox.cpp + ${ORTHANC_STONE_ROOT}/Framework/Volumes/VolumeImageGeometry.cpp ${ORTHANC_STONE_ROOT}/Framework/Volumes/VolumeReslicer.cpp - ${ORTHANC_STONE_ROOT}/Framework/Widgets/CairoWidget.cpp - ${ORTHANC_STONE_ROOT}/Framework/Widgets/EmptyWidget.cpp - ${ORTHANC_STONE_ROOT}/Framework/Widgets/IWidget.h - ${ORTHANC_STONE_ROOT}/Framework/Widgets/IWorldSceneInteractor.h - ${ORTHANC_STONE_ROOT}/Framework/Widgets/IWorldSceneMouseTracker.h - ${ORTHANC_STONE_ROOT}/Framework/Widgets/LayoutWidget.cpp - ${ORTHANC_STONE_ROOT}/Framework/Widgets/PanMouseTracker.cpp - ${ORTHANC_STONE_ROOT}/Framework/Widgets/PanZoomMouseTracker.cpp - ${ORTHANC_STONE_ROOT}/Framework/Widgets/SliceViewerWidget.cpp - ${ORTHANC_STONE_ROOT}/Framework/Widgets/TestCairoWidget.cpp - ${ORTHANC_STONE_ROOT}/Framework/Widgets/TestWorldSceneWidget.cpp - ${ORTHANC_STONE_ROOT}/Framework/Widgets/WidgetBase.cpp - ${ORTHANC_STONE_ROOT}/Framework/Widgets/WorldSceneWidget.cpp - ${ORTHANC_STONE_ROOT}/Framework/Widgets/ZoomMouseTracker.cpp - ${ORTHANC_STONE_ROOT}/Framework/dev.h - - ${ORTHANC_STONE_ROOT}/Framework/Messages/ICallable.h - ${ORTHANC_STONE_ROOT}/Framework/Messages/IMessage.h - ${ORTHANC_STONE_ROOT}/Framework/Messages/IObservable.cpp - ${ORTHANC_STONE_ROOT}/Framework/Messages/IObserver.h - ${ORTHANC_STONE_ROOT}/Framework/Messages/MessageBroker.h - ${ORTHANC_STONE_ROOT}/Framework/Messages/MessageForwarder.cpp - ${ORTHANC_STONE_ROOT}/Framework/Messages/Promise.h - - ${ORTHANC_ROOT}/Plugins/Samples/Common/DicomDatasetReader.cpp - ${ORTHANC_ROOT}/Plugins/Samples/Common/DicomPath.cpp - ${ORTHANC_ROOT}/Plugins/Samples/Common/FullOrthancDataset.cpp - ${ORTHANC_ROOT}/Plugins/Samples/Common/IOrthancConnection.cpp - ${PLATFORM_SOURCES} ${APPLICATIONS_SOURCES} ${ORTHANC_CORE_SOURCES} @@ -515,8 +524,6 @@ endif() -include_directories(${ORTHANC_STONE_ROOT}) - ## ## TEST - Automatically add all ".h" headers to the list of sources diff -r a386bbc955dc -r 92c400a09f1b Resources/CMake/OrthancStoneParameters.cmake --- a/Resources/CMake/OrthancStoneParameters.cmake Wed May 22 16:01:34 2019 +0200 +++ b/Resources/CMake/OrthancStoneParameters.cmake Wed May 22 16:13:46 2019 +0200 @@ -54,3 +54,4 @@ set(ENABLE_OPENGL ON CACHE INTERNAL "Enable support of OpenGL") set(ENABLE_WASM OFF CACHE INTERNAL "Enable support of WebAssembly") +set(ENABLE_STONE_DEPRECATED OFF CACHE INTERNAL "Enable backward compatibility with deprecated Stone classes") diff -r a386bbc955dc -r 92c400a09f1b Resources/CodeGeneration/README.md --- a/Resources/CodeGeneration/README.md Wed May 22 16:01:34 2019 +0200 +++ b/Resources/CodeGeneration/README.md Wed May 22 16:13:46 2019 +0200 @@ -10,11 +10,11 @@ `testCppHandler` contains a C++ project that produces an executable slurping a set of text files representing messages defined against -the `test_data/test1.yaml' schema and dumping them to `cout`. +the `test_data/testTestStoneCodeGen.yaml' schema and dumping them to `cout`. 'testWasmIntegrated` contains a small Web app demonstrating the interaction between TypeScript and C++ in WASM. source ~/apps/emsdk/emsdk_env.sh -Install Python and the following packages `pip install pyyaml jinja2` +Install Python and the following packages `pip install pyyaml yamlloader jinja2` diff -r a386bbc955dc -r 92c400a09f1b Resources/CodeGeneration/stonegentool.py --- a/Resources/CodeGeneration/stonegentool.py Wed May 22 16:01:34 2019 +0200 +++ b/Resources/CodeGeneration/stonegentool.py Wed May 22 16:13:46 2019 +0200 @@ -7,6 +7,7 @@ from io import StringIO import time import datetime +import yamlloader """ 1 2 3 4 5 6 7 @@ -186,7 +187,6 @@ RegisterTemplateFunction(template,NeedsCppConstruction) RegisterTemplateFunction(template, DefaultValueToTs) RegisterTemplateFunction(template, DefaultValueToCpp) - RegisterTemplateFunction(template, sorted) return template def MakeTemplateFromFile(templateFileName): @@ -533,7 +533,7 @@ if not (nextCh == ' ' or nextCh == '\n'): lineNumber = schemaText.count("\n",0,i) + 1 raise RuntimeError("Error at line " + str(lineNumber) + " in the schema: colons must be followed by a space or a newline!") - schema = yaml.load(schemaText, Loader = yaml.SafeLoader) + schema = yaml.load(schemaText, Loader = yamlloader.ordereddict.SafeLoader) return schema def GetTemplatingDictFromSchemaFilename(fn): diff -r a386bbc955dc -r 92c400a09f1b Resources/CodeGeneration/stonegentool_test.py --- a/Resources/CodeGeneration/stonegentool_test.py Wed May 22 16:01:34 2019 +0200 +++ b/Resources/CodeGeneration/stonegentool_test.py Wed May 22 16:13:46 2019 +0200 @@ -87,13 +87,13 @@ self.assertEqual(b4,["int","vector"]) def test_ParseSchema(self): - fn = os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml') + fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml') obj = LoadSchema(fn) # we're happy if it does not crash :) CheckSchemaSchema(obj) def test_ComputeRequiredDeclarationOrder(self): - fn = os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml') + fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml') obj = LoadSchema(fn) genOrder: str = ComputeRequiredDeclarationOrder(obj) self.assertEqual(5,len(genOrder)) @@ -110,7 +110,7 @@ def test_genEnums(self): self.maxDiff = None - fn = os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml') + fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml') obj = LoadSchema(fn) genOrder: str = ComputeRequiredDeclarationOrder(obj) processedSchema = ProcessSchema(obj, genOrder) @@ -127,13 +127,15 @@ self.assertTrue('someBs' in structs['C']['fields']) self.assertTrue('CrispType' in enums) self.assertTrue('Message1' in structs) - self.assertEqual('int32', structs['Message1']['fields']['a'].type) - self.assertEqual('string', structs['Message1']['fields']['b'].type) - self.assertEqual('EnumMonth0', structs['Message1']['fields']['c'].type) - self.assertEqual('bool', structs['Message1']['fields']['d'].type) + self.assertEqual('int32', structs['Message1']['fields']['memberInt32'].type) + self.assertEqual('string', structs['Message1']['fields']['memberString'].type) + self.assertEqual('EnumMonth0', structs['Message1']['fields']['memberEnumMonth'].type) + self.assertEqual('bool', structs['Message1']['fields']['memberBool'].type) + self.assertEqual('float32', structs['Message1']['fields']['memberFloat32'].type) + self.assertEqual('float64', structs['Message1']['fields']['memberFloat64'].type) def test_GenerateTypeScriptEnums(self): - fn = os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml') + fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml') tdico = GetTemplatingDictFromSchemaFilename(fn) template = Template(""" // end of generic methods {% for enum in enums%} export enum {{enum['name']}} { @@ -167,7 +169,7 @@ self.assertEqual(renderedCodeRef,renderedCode) def test_GenerateCplusplusEnums(self): - fn = os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml') + fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml') tdico = GetTemplatingDictFromSchemaFilename(fn) template = Template(""" // end of generic methods {% for enum in enums%} enum {{enum['name']}} { @@ -201,45 +203,9 @@ self.assertEqual(renderedCodeRef,renderedCode) def test_generateTsStructType(self): - fn = os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml') + fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml') tdico = GetTemplatingDictFromSchemaFilename(fn) - ref = """ export class Message1 { - a: number; - b: string; - c: EnumMonth0; - d: boolean; - public StoneSerialize(): string { - let container: object = {}; - container['type'] = 'VsolStuff.Message1'; - container['value'] = this; - return JSON.stringify(container); - } - }; - export class Message2 { - toto: string; - tata: Message1[]; - tutu: string[]; - titi: Map; - lulu: Map; - - constructor() - { - this.tata = new Array(); - this.tutu = new Array(); - this.titi = new Map(); - this.lulu = new Map(); - } - - public StoneSerialize(): string { - let container: object = {}; - container['type'] = 'VsolStuff.Message2'; - container['value'] = this; - return JSON.stringify(container); - } - }; - -""" # template = MakeTemplate(""" // end of generic methods # {% for struct in struct%} export class {{struct['name']}} { # {% for key in struct['fields']%} {{key}}:{{struct['fields'][key]}}, @@ -290,7 +256,7 @@ public StoneSerialize(): string { let container: object = {}; - container['type'] = 'VsolMessages.A'; + container['type'] = 'TestStoneCodeGen.A'; container['value'] = this; return JSON.stringify(container); } @@ -307,7 +273,7 @@ public StoneSerialize(): string { let container: object = {}; - container['type'] = 'VsolMessages.B'; + container['type'] = 'TestStoneCodeGen.B'; container['value'] = this; return JSON.stringify(container); } @@ -324,53 +290,63 @@ public StoneSerialize(): string { let container: object = {}; - container['type'] = 'VsolMessages.C'; + container['type'] = 'TestStoneCodeGen.C'; container['value'] = this; return JSON.stringify(container); } }; export class Message1 { - a:number; - b:string; - c:EnumMonth0; - d:boolean; + memberInt32:number; + memberString:string; + memberEnumMonth:EnumMonth0; + memberBool:boolean; + memberFloat32:number; + memberFloat64:number; constructor() { - this.a = new number(); - this.b = new string(); - this.c = new EnumMonth0(); - this.d = new boolean(); + this.memberInt32 = new number(); + this.memberString = new string(); + this.memberEnumMonth = new EnumMonth0(); + this.memberBool = new boolean(); + this.memberFloat32 = new number(); + this.memberFloat64 = new number(); } public StoneSerialize(): string { let container: object = {}; - container['type'] = 'VsolMessages.Message1'; + container['type'] = 'TestStoneCodeGen.Message1'; container['value'] = this; return JSON.stringify(container); } }; export class Message2 { - toto:string; - tata:Array; - tutu:Array; - titi:Map; - lulu:Map; - movieType:MovieType; + memberString:string; + memberStringWithDefault:string; + memberVectorOfMessage1:Array; + memberVectorOfString:Array; + memberMapStringString:Map; + memberMapStringStruct:Map; + memberMapEnumFloat:Map; + memberEnumMovieType:MovieType; + memberJson:Object; constructor() { - this.toto = new string(); - this.tata = new Array(); - this.tutu = new Array(); - this.titi = new Map(); - this.lulu = new Map(); - this.movieType = new MovieType(); + this.memberString = new string(); + this.memberStringWithDefault = new string(); + this.memberVectorOfMessage1 = new Array(); + this.memberVectorOfString = new Array(); + this.memberMapStringString = new Map(); + this.memberMapStringStruct = new Map(); + this.memberMapEnumFloat = new Map(); + this.memberEnumMovieType = new MovieType(); + this.memberJson = new Object(); } public StoneSerialize(): string { let container: object = {}; - container['type'] = 'VsolMessages.Message2'; + container['type'] = 'TestStoneCodeGen.Message2'; container['value'] = this; return JSON.stringify(container); } @@ -383,7 +359,7 @@ def test_generateWholeTsFile(self): schemaFile = \ - os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml') + os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml') tdico = GetTemplatingDictFromSchemaFilename(schemaFile) tsTemplateFile = \ os.path.join(os.path.dirname(__file__), 'template.in.ts.j2') diff -r a386bbc955dc -r 92c400a09f1b Resources/CodeGeneration/template.in.h.j2 --- a/Resources/CodeGeneration/template.in.h.j2 Wed May 22 16:01:34 2019 +0200 +++ b/Resources/CodeGeneration/template.in.h.j2 Wed May 22 16:13:46 2019 +0200 @@ -109,6 +109,21 @@ } /** Throws in case of problem */ + inline void _StoneDeserializeValue(float& destValue, const Json::Value& jsonValue) + { + if (!jsonValue.isNull()) + { + destValue = jsonValue.asFloat(); + } + } + + inline Json::Value _StoneSerializeValue(float value) + { + Json::Value result(value); + return result; + } + + /** Throws in case of problem */ inline void _StoneDeserializeValue(bool& destValue, const Json::Value& jsonValue) { if (!jsonValue.isNull()) @@ -167,10 +182,21 @@ return out; } + inline std::string ToString(const std::string& str) + { + return str; + } + + inline void FromString(std::string& value, std::string strValue) + { + value = strValue; + } + + /** Throws in case of problem */ - template + template void _StoneDeserializeValue( - std::map& destValue, const Json::Value& jsonValue) + std::map& destValue, const Json::Value& jsonValue) { if (!jsonValue.isNull()) { @@ -180,10 +206,12 @@ itr != jsonValue.end(); itr++) { - std::string key; - _StoneDeserializeValue(key, itr.key()); + std::string strKey; + _StoneDeserializeValue(strKey, itr.key()); + TK key; + FromString(key, strKey); // if you have a compile error here, it means that your type is not suitable to be the key of a map (or you should overwrite the FromString/ToString in template.in.h.j2) - T innerDestValue; + TV innerDestValue; _StoneDeserializeValue(innerDestValue, *itr); destValue[key] = innerDestValue; @@ -191,29 +219,30 @@ } } - template - Json::Value _StoneSerializeValue(const std::map& value) + template + Json::Value _StoneSerializeValue(const std::map& value) { Json::Value result(Json::objectValue); - for (typename std::map::const_iterator it = value.cbegin(); + for (typename std::map::const_iterator it = value.cbegin(); it != value.cend(); ++it) { // it->first it->second - result[it->first] = _StoneSerializeValue(it->second); + result[ToString(it->first)] = _StoneSerializeValue(it->second); } return result; } - template - std::ostream& StoneDumpValue(std::ostream& out, const std::map& value, size_t indent) + template + std::ostream& StoneDumpValue(std::ostream& out, const std::map& value, size_t indent) { out << MakeIndent(indent) << "{\n"; - for (typename std::map::const_iterator it = value.cbegin(); + for (typename std::map::const_iterator it = value.cbegin(); it != value.cend(); ++it) { out << MakeIndent(indent+2) << "\"" << it->first << "\" : "; StoneDumpValue(out, it->second, indent+2); + out << ", \n"; } out << MakeIndent(indent) << "}\n"; return out; @@ -224,7 +253,7 @@ void _StoneDeserializeValue( std::vector& destValue, const Json::Value& jsonValue) { - if (!jsonValue.isNull()) + if (!jsonValue.isNull() && jsonValue.isArray()) { destValue.clear(); destValue.reserve(jsonValue.size()); @@ -255,6 +284,7 @@ for (size_t i = 0; i < value.size(); ++i) { StoneDumpValue(out, value[i], indent+2); + out << ", \n"; } out << MakeIndent(indent) << "]\n"; return out; @@ -265,7 +295,7 @@ void _StoneDeserializeValue( std::set& destValue, const Json::Value& jsonValue) { - if (!jsonValue.isNull()) + if (!jsonValue.isNull() && jsonValue.isArray()) { destValue.clear(); for (Json::Value::ArrayIndex i = 0; i != jsonValue.size(); i++) @@ -295,6 +325,7 @@ for (typename std::set::const_iterator it = value.begin(); it != value.end(); ++it) { StoneDumpValue(out, *it, indent+2); + out << ", \n"; } out << MakeIndent(indent) << "]\n"; return out; @@ -381,7 +412,7 @@ { {% for key in enum['fields']%} if( value == {{enum['name']}}_{{key}}) { - out << MakeIndent(indent) << "{{key}}" << std::endl; + out << MakeIndent(indent) << "{{key}}"; } {%endfor%} return out; } @@ -394,9 +425,9 @@ struct {{struct['name']}} { -{% if struct %}{% if struct['fields'] %}{% for key in sorted(struct['fields']) %} {{CanonToCpp(struct['fields'][key]['type'])}} {{key}}; +{% if struct %}{% if struct['fields'] %}{% for key in struct['fields'] %} {{CanonToCpp(struct['fields'][key]['type'])}} {{key}}; {% endfor %}{% endif %}{% endif %} - {{struct['name']}}({% if struct %}{% if struct['fields'] %}{% for key in sorted(struct['fields']) %}{{CanonToCpp(struct['fields'][key]['type'])}} {{key}} = {% if struct['fields'][key]['defaultValue'] %}{{DefaultValueToCpp(rootName,enums,struct['fields'][key])}} {%else%} {{CanonToCpp(struct['fields'][key]['type'])}}() {%endif%} {{ ", " if not loop.last }}{% endfor %}{% endif %}{% endif %}) + {{struct['name']}}({% if struct %}{% if struct['fields'] %}{% for key in struct['fields'] %}{{CanonToCpp(struct['fields'][key]['type'])}} {{key}} = {% if struct['fields'][key]['defaultValue'] %}{{DefaultValueToCpp(rootName,enums,struct['fields'][key])}} {%else%} {{CanonToCpp(struct['fields'][key]['type'])}}() {%endif%} {{ ", " if not loop.last }}{% endfor %}{% endif %}{% endif %}) { {% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%} this->{{key}} = {{key}}; {% endfor %}{% endif %}{% endif %} } @@ -421,11 +452,11 @@ inline std::ostream& StoneDumpValue(std::ostream& out, const {{struct['name']}}& value, size_t indent = 0) { out << MakeIndent(indent) << "{\n"; -{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%} out << MakeIndent(indent) << "{{key}}:\n"; +{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%} out << MakeIndent(indent+2) << "{{key}}: "; StoneDumpValue(out, value.{{key}},indent+2); - out << "\n"; + out << ", \n"; {% endfor %}{% endif %}{% endif %} - out << MakeIndent(indent) << "}\n"; + out << MakeIndent(indent) << "}"; return out; } diff -r a386bbc955dc -r 92c400a09f1b Resources/CodeGeneration/testCppHandler/CMakeLists.txt --- a/Resources/CodeGeneration/testCppHandler/CMakeLists.txt Wed May 22 16:01:34 2019 +0200 +++ b/Resources/CodeGeneration/testCppHandler/CMakeLists.txt Wed May 22 16:13:46 2019 +0200 @@ -3,13 +3,13 @@ project(testCppHandler) set(testCppHandler_Codegen_Deps - ${CMAKE_CURRENT_LIST_DIR}/../test_data/test1.yaml + ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml ${CMAKE_CURRENT_LIST_DIR}/../template.in.h.j2 ) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/VsolMessages_generated.hpp - COMMAND python ${CMAKE_CURRENT_LIST_DIR}/../stonegentool.py -o ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_LIST_DIR}/../test_data/test1.yaml + COMMAND python ${CMAKE_CURRENT_LIST_DIR}/../stonegentool.py -o ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml DEPENDS ${testCppHandler_Codegen_Deps} ) diff -r a386bbc955dc -r 92c400a09f1b Resources/CodeGeneration/testCppHandler/README.md --- a/Resources/CodeGeneration/testCppHandler/README.md Wed May 22 16:01:34 2019 +0200 +++ b/Resources/CodeGeneration/testCppHandler/README.md Wed May 22 16:13:46 2019 +0200 @@ -25,8 +25,10 @@ - `cmake -G "Visual Studio 15 2017 Win64" ..` (modify for your current Visual Studio version) - `cmake --build . --config Debug` or - `cmake --build . --config Release` +How to execute the test +======================= +- `cd test_data && testCppHandler --pattern=*.json` - diff -r a386bbc955dc -r 92c400a09f1b Resources/CodeGeneration/testCppHandler/main.cpp --- a/Resources/CodeGeneration/testCppHandler/main.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Resources/CodeGeneration/testCppHandler/main.cpp Wed May 22 16:13:46 2019 +0200 @@ -8,7 +8,7 @@ #include using namespace boost::program_options; -#include "VsolMessages_generated.hpp" +#include "TestStoneCodeGen_generated.hpp" /** Transforms `str` by replacing occurrences of `oldStr` with `newStr`, using @@ -39,32 +39,32 @@ return string(bytes.data(), fileSize); } -class MyHandler : public VsolMessages::IHandler +class MyHandler : public TestStoneCodeGen::IHandler { public: - virtual bool Handle(const VsolMessages::A& value) override + virtual bool Handle(const TestStoneCodeGen::A& value) override { - VsolMessages::StoneDumpValue(cout, value); + TestStoneCodeGen::StoneDumpValue(cout, value); return true; } - virtual bool Handle(const VsolMessages::B& value) override + virtual bool Handle(const TestStoneCodeGen::B& value) override { - VsolMessages::StoneDumpValue(cout, value); + TestStoneCodeGen::StoneDumpValue(cout, value); return true; } - virtual bool Handle(const VsolMessages::C& value) override + virtual bool Handle(const TestStoneCodeGen::C& value) override { - VsolMessages::StoneDumpValue(cout, value); + TestStoneCodeGen::StoneDumpValue(cout, value); return true; } - virtual bool Handle(const VsolMessages::Message1& value) override + virtual bool Handle(const TestStoneCodeGen::Message1& value) override { - VsolMessages::StoneDumpValue(cout, value); + TestStoneCodeGen::StoneDumpValue(cout, value); return true; } - virtual bool Handle(const VsolMessages::Message2& value) override + virtual bool Handle(const TestStoneCodeGen::Message2& value) override { - VsolMessages::StoneDumpValue(cout, value); + TestStoneCodeGen::StoneDumpValue(cout, value); return true; } }; @@ -77,7 +77,7 @@ cout << "+--------------------------------------------+\n"; MyHandler handler; auto contents = SlurpFile(filePath.path().string()); - VsolMessages::StoneDispatchToHandler(contents, &handler); + TestStoneCodeGen::StoneDispatchToHandler(contents, &handler); } int main(int argc, char** argv) diff -r a386bbc955dc -r 92c400a09f1b Resources/CodeGeneration/testCppHandler/test_data/test_Message2.json --- a/Resources/CodeGeneration/testCppHandler/test_data/test_Message2.json Wed May 22 16:01:34 2019 +0200 +++ b/Resources/CodeGeneration/testCppHandler/test_data/test_Message2.json Wed May 22 16:13:46 2019 +0200 @@ -1,40 +1,47 @@ { - "type": "VsolMessages.Message2", + "type": "TestStoneCodeGen.Message2", "value": { - "tata": [ + "memberVectorOfMessage1": [ { - "a": 42, - "b": "Benjamin", - "c": 0, - "d": false + "memberInt32": 42, + "memberString": "Benjamin", + "memberEnumMonth": "January", + "memberBool": false, + "memberFloat32": 0.1, + "memberFloat64": -0.2 }, { - "a": 43, - "b": "Sandrine", - "c": 2 + "memberInt32": 43, + "memberString": "Sandrine", + "memberEnumMonth": "March" } ], - "tutu": [ + "memberVectorOfString": [ "Mercadet", "Poisson" ], - "titi": { + "memberMapStringString": { "44": "key 44", "45": "key 45" }, - "lulu": { + "memberMapStringStruct": { "54": { - "a": 43, - "b": "Sandrine", - "c": 2 + "memberInt32": 43, + "memberString": "Sandrine", + "memberEnumMonth": "March" }, "55": { - "a": 42, - "b": "Benjamin", - "c": 0, - "d": false + "memberInt32": 42, + "memberString": "Benjamin", + "memberEnumMonth": "January", + "memberBool": false } }, - "toto": "Prout zizi" + "memberString": "Prout zizi", + "memberMapEnumFloat" : { + "SaltAndPepper" : 0.1, + "CreamAndChives" : -0.2 + }, + "memberJson" : {"custom-key": "custom-value"} } } \ No newline at end of file diff -r a386bbc955dc -r 92c400a09f1b Resources/CodeGeneration/testWasmIntegrated/CMakeLists.txt --- a/Resources/CodeGeneration/testWasmIntegrated/CMakeLists.txt Wed May 22 16:01:34 2019 +0200 +++ b/Resources/CodeGeneration/testWasmIntegrated/CMakeLists.txt Wed May 22 16:13:46 2019 +0200 @@ -21,14 +21,14 @@ set(jsoncppRootDir ${CMAKE_CURRENT_LIST_DIR}/jsoncpp-1.8.4) add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/testWasmIntegratedCpp_generated.hpp ${CMAKE_CURRENT_BINARY_DIR}/testWasmIntegratedCpp_generated.ts - COMMAND python3 ${CMAKE_CURRENT_LIST_DIR}/../stonegentool.py -o ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_LIST_DIR}/testWasmIntegratedCpp_api.yaml - DEPENDS ${testCppHandler_Codegen_Deps} ${CMAKE_CURRENT_LIST_DIR}/testWasmIntegratedCpp_api.yaml + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/TestStoneCodeGen_generated.hpp ${CMAKE_CURRENT_BINARY_DIR}/TestStoneCodeGen_generated.ts + COMMAND python3 ${CMAKE_CURRENT_LIST_DIR}/../stonegentool.py -o ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml + DEPENDS ${testCppHandler_Codegen_Deps} ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml ) add_executable(testWasmIntegratedCpp main.cpp - ${CMAKE_CURRENT_BINARY_DIR}/testWasmIntegratedCpp_generated.hpp + ${CMAKE_CURRENT_BINARY_DIR}/TestStoneCodeGen_generated.hpp ${jsoncppRootDir}/jsoncpp.cpp ${testCppHandler_Codegen_Deps}) diff -r a386bbc955dc -r 92c400a09f1b Resources/CodeGeneration/testWasmIntegrated/DefaultLibrary.js --- a/Resources/CodeGeneration/testWasmIntegrated/DefaultLibrary.js Wed May 22 16:01:34 2019 +0200 +++ b/Resources/CodeGeneration/testWasmIntegrated/DefaultLibrary.js Wed May 22 16:13:46 2019 +0200 @@ -9,7 +9,7 @@ }, SendMessageFromCppJS: function(statusUpdateMessage) { var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage); - SendMessageFromCpp(statusUpdateMessage_); + window.SendMessageFromCpp(statusUpdateMessage_); } }); diff -r a386bbc955dc -r 92c400a09f1b Resources/CodeGeneration/testWasmIntegrated/build-web.sh --- a/Resources/CodeGeneration/testWasmIntegrated/build-web.sh Wed May 22 16:01:34 2019 +0200 +++ b/Resources/CodeGeneration/testWasmIntegrated/build-web.sh Wed May 22 16:13:46 2019 +0200 @@ -4,7 +4,7 @@ mkdir -p build-final # compile TS to JS -tsc --module commonjs --sourceMap -t ES2015 --outDir "build-tsc/" build-wasm/testWasmIntegratedCpp_generated.ts testWasmIntegrated.ts +tsc --module commonjs --sourceMap -t ES2015 --outDir "build-tsc/" build-wasm/TestStoneCodeGen_generated.ts testWasmIntegrated.ts # bundle JS files to final build dir browserify "build-tsc/build-wasm/testWasmIntegratedCpp_generated.js" "build-tsc/testWasmIntegrated.js" -o "build-final/testWasmIntegratedApp.js" @@ -22,8 +22,10 @@ # copy WASM binary to output dir cp build-wasm/testWasmIntegratedCpp.wasm build-final/ -echo "...Serving files at http://127.0.0.1:8080/" -echo "Please open build_final/testWasmIntegrated.html" +cp ../test_data/testTestStoneCodeGen.yaml build-final/ +cp ../testCppHandler/test_data/test_Message2.json build-final/cppHandler_test_Message2.json + +echo "...Serving files at http://127.0.0.1:8080/build-final/testWasmIntegrated.html" sudo python3 serve.py diff -r a386bbc955dc -r 92c400a09f1b Resources/CodeGeneration/testWasmIntegrated/main.cpp --- a/Resources/CodeGeneration/testWasmIntegrated/main.cpp Wed May 22 16:01:34 2019 +0200 +++ b/Resources/CodeGeneration/testWasmIntegrated/main.cpp Wed May 22 16:13:46 2019 +0200 @@ -1,7 +1,7 @@ #include #include #include -#include "testWasmIntegratedCpp_generated.hpp" +#include "TestStoneCodeGen_generated.hpp" using std::stringstream; @@ -55,35 +55,72 @@ #define HANDLE_MESSAGE(Type,value) \ stringstream ss; \ ss << "Received an instance of:\n" #Type "\n. Here's the dump:\n"; \ - testWasmIntegratedCpp::StoneDumpValue(ss, value, 0); \ + TestStoneCodeGen::StoneDumpValue(ss, value, 0); \ SendFreeTextFromCppJS(ss.str().c_str()); \ return true; -class MyHandler : public testWasmIntegratedCpp::IHandler +#define ECHO_MESSAGE(Type,value) \ + stringstream ss; \ + ss << "Received an instance of:\n" #Type "\n. Here's the dump:\n"; \ + TestStoneCodeGen::StoneDumpValue(ss, value, 0); \ + SendFreeTextFromCppJS(ss.str().c_str()); \ + std::string serializedInCpp = StoneSerialize(value); \ + SendMessageFromCppJS(serializedInCpp.c_str()); \ + return true; + +class MyHandler : public TestStoneCodeGen::IHandler { public: - virtual bool Handle(const testWasmIntegratedCpp::A& value) override + virtual bool Handle(const TestStoneCodeGen::A& value) override { - HANDLE_MESSAGE(testWasmIntegratedCpp::A,value) + HANDLE_MESSAGE(TestStoneCodeGen::A,value) + } + virtual bool Handle(const TestStoneCodeGen::B& value) override + { + HANDLE_MESSAGE(TestStoneCodeGen::B,value) } - virtual bool Handle(const testWasmIntegratedCpp::B& value) override + + virtual bool Handle(const TestStoneCodeGen::Message1& value) override { - HANDLE_MESSAGE(testWasmIntegratedCpp::B,value) + HANDLE_MESSAGE(TestStoneCodeGen::Message1,value) + } + + virtual bool Handle(const TestStoneCodeGen::Message2& value) override + { + HANDLE_MESSAGE(TestStoneCodeGen::Message2,value) } - virtual bool Handle(const testWasmIntegratedCpp::Message1& value) override + virtual bool Handle(const TestStoneCodeGen::C& value) override { - HANDLE_MESSAGE(testWasmIntegratedCpp::Message1,value) + HANDLE_MESSAGE(TestStoneCodeGen::C,value) + } +}; + +class MyEchoHandler : public TestStoneCodeGen::IHandler +{ + public: + virtual bool Handle(const TestStoneCodeGen::A& value) override + { + ECHO_MESSAGE(TestStoneCodeGen::A,value) + } + virtual bool Handle(const TestStoneCodeGen::B& value) override + { + ECHO_MESSAGE(TestStoneCodeGen::B,value) } - virtual bool Handle(const testWasmIntegratedCpp::Message2& value) override + virtual bool Handle(const TestStoneCodeGen::Message1& value) override { - HANDLE_MESSAGE(testWasmIntegratedCpp::Message2,value) + ECHO_MESSAGE(TestStoneCodeGen::Message1,value) } - virtual bool Handle(const testWasmIntegratedCpp::C& value) override + virtual bool Handle(const TestStoneCodeGen::Message2& value) override { - HANDLE_MESSAGE(testWasmIntegratedCpp::C,value) + ECHO_MESSAGE(TestStoneCodeGen::Message2,value) + } + + virtual bool Handle(const TestStoneCodeGen::C& value) override + { + ECHO_MESSAGE(TestStoneCodeGen::C,value) } }; @@ -92,10 +129,29 @@ MyHandler handler; try { - bool handled = testWasmIntegratedCpp::StoneDispatchToHandler(message,&handler); + bool handled = TestStoneCodeGen::StoneDispatchToHandler(message,&handler); if(!handled) { - SendFreeTextFromCppJS("This message is valid JSON, but was not recognized!"); + SendFreeTextFromCppJS("This message is valid JSON, but was not handled!"); + } + } + catch(std::exception& e) + { + stringstream ss; + ss << "Error while parsing message: " << e.what() << "\n"; + SendFreeTextFromCppJS(ss.str().c_str()); + } +} + +extern "C" void EMSCRIPTEN_KEEPALIVE SendMessageToCppForEcho(const char* message) +{ + MyEchoHandler echoHandler; + try + { + bool handled = TestStoneCodeGen::StoneDispatchToHandler(message,&echoHandler); + if(!handled) + { + SendFreeTextFromCppJS("This message is valid JSON, but was not handled by the echo handler!"); } } catch(std::exception& e) diff -r a386bbc955dc -r 92c400a09f1b Resources/CodeGeneration/testWasmIntegrated/styles.css --- a/Resources/CodeGeneration/testWasmIntegrated/styles.css Wed May 22 16:01:34 2019 +0200 +++ b/Resources/CodeGeneration/testWasmIntegrated/styles.css Wed May 22 16:13:46 2019 +0200 @@ -9,7 +9,7 @@ "Test1 Test2 Test3 Test4 . . ." ". . . . . . ." "Test5 Test6 Test7 Test8 . . ." - ". . . . . . ." + "TestTsCppTs . . . . . ." ". . . . . . ." ; height: 480px; @@ -59,6 +59,8 @@ .TestWasm-Test8 { grid-area: Test8; } +.TestWasm-ts-cpp-ts { grid-area: TestTsCppTs; } + .TestWasm-button { width:80px; } diff -r a386bbc955dc -r 92c400a09f1b Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.html --- a/Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.html Wed May 22 16:01:34 2019 +0200 +++ b/Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.html Wed May 22 16:13:46 2019 +0200 @@ -31,7 +31,7 @@
- +
@@ -54,6 +54,9 @@
+
+ +