# HG changeset patch # User Sebastien Jodogne # Date 1541780799 -3600 # Node ID 20f149669c1f872830a2d5ccb7374c4fb2498067 # Parent a8b5cf760473b889e1a91547fc0108410b8e970a renamed LayerWidget as SliceViewerWidget diff -r a8b5cf760473 -r 20f149669c1f Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp --- a/Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp Fri Nov 09 17:11:35 2018 +0100 +++ b/Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp Fri Nov 09 17:26:39 2018 +0100 @@ -38,12 +38,12 @@ { if (application_.GetCurrentTool() == SimpleViewerApplication::Tools_LineMeasure) { - return new LineMeasureTracker(statusBar, dynamic_cast(widget).GetSlice(), + return new LineMeasureTracker(statusBar, dynamic_cast(widget).GetSlice(), x, y, 255, 0, 0, application_.GetFont()); } else if (application_.GetCurrentTool() == SimpleViewerApplication::Tools_CircleMeasure) { - return new CircleMeasureTracker(statusBar, dynamic_cast(widget).GetSlice(), + return new CircleMeasureTracker(statusBar, dynamic_cast(widget).GetSlice(), x, y, 255, 0, 0, application_.GetFont()); } else if (application_.GetCurrentTool() == SimpleViewerApplication::Tools_Crop) @@ -76,7 +76,7 @@ { 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)", diff -r a8b5cf760473 -r 20f149669c1f Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp --- a/Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp Fri Nov 09 17:11:35 2018 +0100 +++ b/Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp Fri Nov 09 17:26:39 2018 +0100 @@ -54,7 +54,7 @@ thumbnailsLayout_->SetBackgroundColor(50, 50, 50); thumbnailsLayout_->SetVertical(); - mainWidget_ = new LayerWidget(IObserver::broker_, "main-viewport"); + mainWidget_ = new SliceViewerWidget(IObserver::broker_, "main-viewport"); //mainWidget_->RegisterObserver(*this); // hierarchy @@ -152,10 +152,13 @@ void SimpleViewerApplication::LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId) { LOG(INFO) << "Loading thumbnail for series " << seriesId; - LayerWidget* thumbnailWidget = new LayerWidget(IObserver::broker_, "thumbnail-series-" + seriesId); + SliceViewerWidget* thumbnailWidget = + new SliceViewerWidget(IObserver::broker_, "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_); } @@ -165,7 +168,7 @@ orthancApiClient_->GetJsonAsync("/studies/" + studyId, new Callable(*this, &SimpleViewerApplication::OnStudyReceived)); } - void SimpleViewerApplication::OnWidgetGeometryChanged(const LayerWidget::GeometryChangedMessage& message) + void SimpleViewerApplication::OnWidgetGeometryChanged(const SliceViewerWidget::GeometryChangedMessage& message) { message.GetOrigin().FitContent(); } diff -r a8b5cf760473 -r 20f149669c1f Applications/Samples/SimpleViewer/SimpleViewerApplication.h --- a/Applications/Samples/SimpleViewer/SimpleViewerApplication.h Fri Nov 09 17:11:35 2018 +0100 +++ b/Applications/Samples/SimpleViewer/SimpleViewerApplication.h Fri Nov 09 17:26:39 2018 +0100 @@ -26,7 +26,7 @@ #include "Framework/Layers/OrthancFrameLayerSource.h" #include "Framework/Layers/CircleMeasureTracker.h" #include "Framework/Layers/LineMeasureTracker.h" -#include "Framework/Widgets/LayerWidget.h" +#include "Framework/Widgets/SliceViewerWidget.h" #include "Framework/Widgets/LayoutWidget.h" #include "Framework/Messages/IObserver.h" #include "Framework/SmartLoader.h" @@ -88,26 +88,26 @@ }; private: - Tools currentTool_; + Tools currentTool_; std::auto_ptr mainWidgetInteractor_; std::auto_ptr thumbnailInteractor_; - LayoutWidget* mainLayout_; - LayoutWidget* thumbnailsLayout_; - LayerWidget* mainWidget_; - std::vector thumbnails_; + LayoutWidget* mainLayout_; + LayoutWidget* thumbnailsLayout_; + SliceViewerWidget* mainWidget_; + std::vector thumbnails_; std::map > instancesIdsPerSeriesId_; - std::map seriesTags_; - BaseCommandBuilder commandBuilder_; + std::map seriesTags_; + BaseCommandBuilder commandBuilder_; - unsigned int currentInstanceIndex_; - OrthancStone::WidgetViewport* wasmViewport1_; - OrthancStone::WidgetViewport* wasmViewport2_; + unsigned int currentInstanceIndex_; + OrthancStone::WidgetViewport* wasmViewport1_; + OrthancStone::WidgetViewport* wasmViewport2_; - IStatusBar* statusBar_; - std::auto_ptr smartLoader_; - std::auto_ptr orthancApiClient_; + IStatusBar* statusBar_; + std::auto_ptr smartLoader_; + std::auto_ptr orthancApiClient_; - Orthanc::Font font_; + Orthanc::Font font_; public: SimpleViewerApplication(MessageBroker& broker) : @@ -140,7 +140,7 @@ void SelectStudy(const std::string& studyId); - void OnWidgetGeometryChanged(const LayerWidget::GeometryChangedMessage& message); + void OnWidgetGeometryChanged(const SliceViewerWidget::GeometryChangedMessage& message); void SelectSeriesInMainViewport(const std::string& seriesId); diff -r a8b5cf760473 -r 20f149669c1f Applications/Samples/SimpleViewerApplicationSingleFile.h --- a/Applications/Samples/SimpleViewerApplicationSingleFile.h Fri Nov 09 17:11:35 2018 +0100 +++ b/Applications/Samples/SimpleViewerApplicationSingleFile.h Fri Nov 09 17:26:39 2018 +0100 @@ -26,7 +26,7 @@ #include "../../Framework/Layers/OrthancFrameLayerSource.h" #include "../../Framework/Layers/CircleMeasureTracker.h" #include "../../Framework/Layers/LineMeasureTracker.h" -#include "../../Framework/Widgets/LayerWidget.h" +#include "../../Framework/Widgets/SliceViewerWidget.h" #include "../../Framework/Widgets/LayoutWidget.h" #include "../../Framework/Messages/IObserver.h" #include "../../Framework/SmartLoader.h" @@ -52,6 +52,7 @@ { private: SimpleViewerApplication& application_; + public: ThumbnailInteractor(SimpleViewerApplication& application) : application_(application) @@ -76,27 +77,30 @@ } return NULL; } + virtual void MouseOver(CairoContext& context, WorldSceneWidget& widget, const ViewportGeometry& view, double x, double y, IStatusBar* statusBar) - {} + { + } virtual void MouseWheel(WorldSceneWidget& widget, MouseWheelDirection direction, KeyboardModifiers modifiers, IStatusBar* statusBar) - {} + { + } virtual void KeyPressed(WorldSceneWidget& widget, KeyboardKeys key, char keyChar, KeyboardModifiers modifiers, IStatusBar* statusBar) - {} - + { + } }; class MainWidgetInteractor : public IWorldSceneInteractor @@ -124,12 +128,12 @@ { if (application_.currentTool_ == Tools_LineMeasure) { - return new LineMeasureTracker(statusBar, dynamic_cast(widget).GetSlice(), + return new LineMeasureTracker(statusBar, dynamic_cast(widget).GetSlice(), x, y, 255, 0, 0, application_.GetFont()); } else if (application_.currentTool_ == Tools_CircleMeasure) { - return new CircleMeasureTracker(statusBar, dynamic_cast(widget).GetSlice(), + return new CircleMeasureTracker(statusBar, dynamic_cast(widget).GetSlice(), x, y, 255, 0, 0, application_.GetFont()); } } @@ -145,7 +149,7 @@ { 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)", @@ -198,10 +202,10 @@ : WasmPlatformApplicationAdapter(broker, application), viewerApplication_(application) { - } - virtual void HandleMessageFromWeb(std::string& output, const std::string& input) { + virtual void HandleMessageFromWeb(std::string& output, const std::string& input) + { if (input == "select-tool:line-measure") { viewerApplication_.currentTool_ = Tools_LineMeasure; @@ -216,7 +220,8 @@ output = "ok"; } - virtual void NotifyStatusUpdateFromCppToWeb(const std::string& statusUpdateMessage) { + virtual void NotifyStatusUpdateFromCppToWeb(const std::string& statusUpdateMessage) + { UpdateStoneApplicationStatusFromCpp(statusUpdateMessage.c_str()); } @@ -232,7 +237,7 @@ std::auto_ptr thumbnailInteractor_; LayoutWidget* mainLayout_; LayoutWidget* thumbnailsLayout_; - std::vector thumbnails_; + std::vector thumbnails_; std::map > instancesIdsPerSeriesId_; std::map seriesTags_; @@ -293,7 +298,7 @@ thumbnailsLayout_->SetBackgroundColor(50, 50, 50); thumbnailsLayout_->SetVertical(); - mainWidget_ = new LayerWidget(broker_, "main-viewport"); + mainWidget_ = new SliceViewerWidget(broker_, "main-viewport"); //mainWidget_->RegisterObserver(*this); // hierarchy @@ -319,9 +324,10 @@ if (parameters.count("studyId") < 1) { LOG(WARNING) << "The study ID is missing, will take the first studyId found in Orthanc"; - orthancApiClient_->GetJsonAsync("/studies", - new Callable - (*this, &SimpleViewerApplication::OnStudyListReceived)); + orthancApiClient_->GetJsonAsync( + "/studies", + new Callable + (*this, &SimpleViewerApplication::OnStudyListReceived)); } else { @@ -346,9 +352,10 @@ { for (size_t i=0; i < response["Series"].size(); i++) { - orthancApiClient_->GetJsonAsync("/series/" + response["Series"][(int)i].asString(), - new Callable - (*this, &SimpleViewerApplication::OnSeriesReceived)); + orthancApiClient_->GetJsonAsync( + "/series/" + response["Series"][(int)i].asString(), + new Callable + (*this, &SimpleViewerApplication::OnSeriesReceived)); } } } @@ -375,7 +382,7 @@ LoadThumbnailForSeries(seriesId, instancesIdsPerSeriesId_[seriesId][0]); // if this is the first thumbnail loaded, load the first instance in the mainWidget - LayerWidget& widget = *dynamic_cast(mainWidget_); + SliceViewerWidget& widget = *dynamic_cast(mainWidget_); if (widget.GetLayerCount() == 0) { smartLoader_->SetFrameInWidget(widget, 0, instancesIdsPerSeriesId_[seriesId][0], 0); @@ -386,10 +393,10 @@ void LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId) { LOG(INFO) << "Loading thumbnail for series " << seriesId; - LayerWidget* thumbnailWidget = new LayerWidget(IObserver::broker_, "thumbnail-series-" + seriesId); + SliceViewerWidget* thumbnailWidget = new SliceViewerWidget(IObserver::broker_, "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_); } @@ -399,14 +406,14 @@ orthancApiClient_->GetJsonAsync("/studies/" + studyId, new Callable(*this, &SimpleViewerApplication::OnStudyReceived)); } - void OnWidgetGeometryChanged(const LayerWidget::GeometryChangedMessage& message) + void OnWidgetGeometryChanged(const SliceViewerWidget::GeometryChangedMessage& message) { message.GetOrigin().FitContent(); } void SelectSeriesInMainViewport(const std::string& seriesId) { - LayerWidget& widget = *dynamic_cast(mainWidget_); + SliceViewerWidget& widget = *dynamic_cast(mainWidget_); smartLoader_->SetFrameInWidget(widget, 0, instancesIdsPerSeriesId_[seriesId][0], 0); } @@ -440,7 +447,5 @@ #endif }; - - } } diff -r a8b5cf760473 -r 20f149669c1f Applications/Samples/SingleFrameApplication.h --- a/Applications/Samples/SingleFrameApplication.h Fri Nov 09 17:11:35 2018 +0100 +++ b/Applications/Samples/SingleFrameApplication.h Fri Nov 09 17:26:39 2018 +0100 @@ -24,7 +24,7 @@ #include "SampleApplicationBase.h" #include "../../Framework/Layers/OrthancFrameLayerSource.h" -#include "../../Framework/Widgets/LayerWidget.h" +#include "../../Framework/Widgets/SliceViewerWidget.h" #include @@ -73,7 +73,7 @@ { 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)", @@ -147,9 +147,9 @@ } - LayerWidget& GetMainWidget() + SliceViewerWidget& GetMainWidget() { - return *dynamic_cast(mainWidget_); + return *dynamic_cast(mainWidget_); } @@ -243,7 +243,7 @@ int frame = parameters["frame"].as(); orthancApiClient_.reset(new OrthancApiClient(IObserver::broker_, context_->GetWebService())); - mainWidget_ = new LayerWidget(broker_, "main-widget"); + mainWidget_ = new SliceViewerWidget(broker_, "main-widget"); std::auto_ptr layer(new OrthancFrameLayerSource(broker_, *orthancApiClient_)); source_ = layer.get(); diff -r a8b5cf760473 -r 20f149669c1f Framework/SmartLoader.cpp --- a/Framework/SmartLoader.cpp Fri Nov 09 17:11:35 2018 +0100 +++ b/Framework/SmartLoader.cpp Fri Nov 09 17:26:39 2018 +0100 @@ -23,7 +23,7 @@ #include "Layers/OrthancFrameLayerSource.h" #include "Messages/MessageForwarder.h" #include "Core/Images/Image.h" -#include "Framework/Widgets/LayerWidget.h" +#include "Framework/Widgets/SliceViewerWidget.h" #include "Framework/StoneException.h" #include "Framework/Layers/FrameRenderer.h" #include "Core/Logging.h" @@ -127,7 +127,7 @@ { } - void SmartLoader::SetFrameInWidget(LayerWidget& layerWidget, + void SmartLoader::SetFrameInWidget(SliceViewerWidget& sliceViewer, size_t layerIndex, const std::string& instanceId, unsigned int frame) @@ -159,13 +159,13 @@ } // make sure that the widget registers the events before we trigger them - if (layerWidget.GetLayerCount() == layerIndex) + if (sliceViewer.GetLayerCount() == layerIndex) { - layerWidget.AddLayer(layerSource.release()); + sliceViewer.AddLayer(layerSource.release()); } - else if (layerWidget.GetLayerCount() > layerIndex) + else if (sliceViewer.GetLayerCount() > layerIndex) { - layerWidget.ReplaceLayer(layerIndex, layerSource.release()); + sliceViewer.ReplaceLayer(layerIndex, layerSource.release()); } else { diff -r a8b5cf760473 -r 20f149669c1f Framework/SmartLoader.h --- a/Framework/SmartLoader.h Fri Nov 09 17:11:35 2018 +0100 +++ b/Framework/SmartLoader.h Fri Nov 09 17:26:39 2018 +0100 @@ -28,7 +28,7 @@ namespace OrthancStone { - class LayerWidget; + class SliceViewerWidget; class SmartLoader : public IObservable, public IObserver { @@ -53,7 +53,7 @@ void SetImageQuality(SliceImageQuality imageQuality) { imageQuality_ = imageQuality; } - void SetFrameInWidget(LayerWidget& layerWidget, size_t layerIndex, const std::string& instanceId, unsigned int frame); + void SetFrameInWidget(SliceViewerWidget& sliceViewer, size_t layerIndex, const std::string& instanceId, unsigned int frame); void GetFirstInstanceIdForSeries(std::string& output, const std::string& seriesId); diff -r a8b5cf760473 -r 20f149669c1f Framework/Widgets/LayerWidget.cpp --- a/Framework/Widgets/LayerWidget.cpp Fri Nov 09 17:11:35 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,639 +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 "LayerWidget.h" - -#include "../Layers/SliceOutlineRenderer.h" -#include "../Toolbox/GeometryToolbox.h" -#include "Framework/Layers/FrameRenderer.h" - -#include - -#include - - -static const double THIN_SLICE_THICKNESS = 100.0 * std::numeric_limits::epsilon(); - -namespace OrthancStone -{ - class LayerWidget::Scene : public boost::noncopyable - { - private: - CoordinateSystem3D slice_; - 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& slice, - double thickness, - size_t countLayers) : - slice_(slice), - 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& GetSlice() const - { - return slice_; - } - - bool HasRenderer(size_t index) - { - return renderers_[index] != NULL; - } - - bool IsComplete() const - { - return countMissing_ == 0; - } - - unsigned int GetCountMissing() const - { - return countMissing_; - } - - bool RenderScene(CairoContext& context, - const ViewportGeometry& view, - const CoordinateSystem3D& viewportSlice) - { - bool fullQuality = true; - cairo_t *cr = context.GetObject(); - - for (size_t i = 0; i < renderers_.size(); i++) - { - if (renderers_[i] != NULL) - { - const CoordinateSystem3D& frameSlice = renderers_[i]->GetLayerSlice(); - - double x0, y0, x1, y1, x2, y2; - viewportSlice.ProjectPoint(x0, y0, frameSlice.GetOrigin()); - viewportSlice.ProjectPoint(x1, y1, frameSlice.GetOrigin() + frameSlice.GetAxisX()); - viewportSlice.ProjectPoint(x2, y2, frameSlice.GetOrigin() + frameSlice.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& slice) const - { - bool isOpposite; - if (!GeometryToolbox::IsParallelOrOpposite(isOpposite, - slice.GetNormal(), - slice_.GetNormal())) - { - return false; - } - else - { - double z = (slice_.ProjectAlongNormal(slice.GetOrigin()) - - slice_.ProjectAlongNormal(slice_.GetOrigin())); - - if (z < 0) - { - z = -z; - } - - return z <= thickness_; - } - } - - double GetThickness() const - { - return thickness_; - } - }; - - - bool LayerWidget::LookupLayer(size_t& index /* out */, - const ILayerSource& 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 LayerWidget::GetLayerExtent(Extent2D& extent, - ILayerSource& source) const - { - extent.Reset(); - - std::vector points; - if (source.GetExtent(points, slice_)) - { - for (size_t i = 0; i < points.size(); i++) - { - double x, y; - slice_.ProjectPoint(x, y, points[i]); - extent.AddPoint(x, y); - } - } - } - - - Extent2D LayerWidget::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 LayerWidget::RenderScene(CairoContext& context, - const ViewportGeometry& view) - { - if (currentScene_.get() != NULL) - { - return currentScene_->RenderScene(context, view, slice_); - } - else - { - return true; - } - } - - - void LayerWidget::ResetPendingScene() - { - double thickness; - if (pendingScene_.get() == NULL) - { - thickness = 1.0; - } - else - { - thickness = pendingScene_->GetThickness(); - } - - pendingScene_.reset(new Scene(slice_, thickness, layers_.size())); - } - - - void LayerWidget::UpdateLayer(size_t index, - ILayerRenderer* renderer, - const CoordinateSystem3D& slice) - { - 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(slice)) - { - currentScene_->SetLayer(index, tmp.release()); - NotifyContentChanged(); - } - else if (pendingScene_.get() != NULL && - pendingScene_->ContainsPlane(slice)) - { - pendingScene_->SetLayer(index, tmp.release()); - - if (currentScene_.get() == NULL || - !currentScene_->IsComplete() || - pendingScene_->IsComplete()) - { - currentScene_ = pendingScene_; - NotifyContentChanged(); - } - } - } - - - LayerWidget::LayerWidget(MessageBroker& broker, const std::string& name) : - WorldSceneWidget(name), - IObserver(broker), - IObservable(broker), - started_(false) - { - SetBackgroundCleared(true); - } - - - LayerWidget::~LayerWidget() - { - for (size_t i = 0; i < layers_.size(); i++) - { - delete layers_[i]; - } - } - - void LayerWidget::ObserveLayer(ILayerSource& layer) - { - layer.RegisterObserverCallback(new Callable(*this, &LayerWidget::OnGeometryReady)); - // currently ignore errors layer->RegisterObserverCallback(new Callable(*this, &LayerWidget::...)); - layer.RegisterObserverCallback(new Callable(*this, &LayerWidget::OnSliceChanged)); - layer.RegisterObserverCallback(new Callable(*this, &LayerWidget::OnContentChanged)); - layer.RegisterObserverCallback(new Callable(*this, &LayerWidget::OnLayerReady)); - layer.RegisterObserverCallback(new Callable(*this, &LayerWidget::OnLayerError)); - } - - - size_t LayerWidget::AddLayer(ILayerSource* 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 LayerWidget::ReplaceLayer(size_t index, ILayerSource* 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 LayerWidget::RemoveLayer(size_t index) - { - if (index >= layers_.size()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - ILayerSource* 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& LayerWidget::GetLayerStyle(size_t layer) const - { - if (layer >= layers_.size()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - assert(layers_.size() == styles_.size()); - return styles_[layer]; - } - - - void LayerWidget::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 LayerWidget::SetSlice(const CoordinateSystem3D& slice) - { - LOG(INFO) << "Setting slice origin: (" << slice.GetOrigin()[0] - << "," << slice.GetOrigin()[1] - << "," << slice.GetOrigin()[2] << ")"; - - Slice displayedSlice(slice_, THIN_SLICE_THICKNESS); - - //if (!displayedSlice.ContainsPlane(slice)) - { - if (currentScene_.get() == NULL || - (pendingScene_.get() != NULL && - pendingScene_->IsComplete())) - { - currentScene_ = pendingScene_; - } - - slice_ = slice; - ResetPendingScene(); - - InvalidateAllLayers(); // TODO Removing this line avoid loading twice the image in WASM - } - } - - void LayerWidget::OnGeometryReady(const ILayerSource::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(slice_); - } - EmitMessage(GeometryChangedMessage(*this)); - } - - void LayerWidget::InvalidateAllLayers() - { - for (size_t i = 0; i < layers_.size(); i++) - { - assert(layers_[i] != NULL); - changedLayers_[i] = true; - - //layers_[i]->ScheduleLayerCreation(slice_); - } - } - - - void LayerWidget::InvalidateLayer(size_t layer) - { - if (layer >= layers_.size()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - assert(layers_[layer] != NULL); - changedLayers_[layer] = true; - - //layers_[layer]->ScheduleLayerCreation(slice_); - } - - - void LayerWidget::OnContentChanged(const ILayerSource::ContentChangedMessage& message) - { - size_t index; - if (LookupLayer(index, message.GetOrigin())) - { - InvalidateLayer(index); - } - - EmitMessage(LayerWidget::ContentChangedMessage(*this)); - } - - - void LayerWidget::OnSliceChanged(const ILayerSource::SliceChangedMessage& message) - { - if (message.GetSlice().ContainsPlane(slice_)) - { - size_t index; - if (LookupLayer(index, message.GetOrigin())) - { - InvalidateLayer(index); - } - } - - EmitMessage(LayerWidget::ContentChangedMessage(*this)); - } - - - void LayerWidget::OnLayerReady(const ILayerSource::LayerReadyMessage& message) - { - size_t index; - if (LookupLayer(index, message.GetOrigin())) - { - LOG(INFO) << "Renderer ready for layer " << index; - UpdateLayer(index, message.CreateRenderer(), message.GetSlice()); - } - - EmitMessage(LayerWidget::ContentChangedMessage(*this)); - } - - - void LayerWidget::OnLayerError(const ILayerSource::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); - - EmitMessage(LayerWidget::ContentChangedMessage(*this)); - } - } - - - void LayerWidget::ResetChangedLayers() - { - changedLayers_.resize(layers_.size()); - - for (size_t i = 0; i < changedLayers_.size(); i++) - { - changedLayers_[i] = false; - } - } - - - void LayerWidget::DoAnimation() - { - assert(changedLayers_.size() <= layers_.size()); - - for (size_t i = 0; i < changedLayers_.size(); i++) - { - if (changedLayers_[i]) - { - layers_[i]->ScheduleLayerCreation(slice_); - } - } - - ResetChangedLayers(); - } -} diff -r a8b5cf760473 -r 20f149669c1f Framework/Widgets/LayerWidget.h --- a/Framework/Widgets/LayerWidget.h Fri Nov 09 17:11:35 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,128 +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 "WorldSceneWidget.h" -#include "../Layers/ILayerSource.h" -#include "../Toolbox/Extent2D.h" -#include "../../Framework/Messages/IObserver.h" - -#include - -namespace OrthancStone -{ - class LayerWidget : - public WorldSceneWidget, - public IObserver, - public IObservable - { - public: - typedef OriginMessage GeometryChangedMessage; - typedef OriginMessage ContentChangedMessage; - - private: - class Scene; - - typedef std::map LayersIndex; - - bool started_; - LayersIndex layersIndex_; - std::vector layers_; - std::vector styles_; - CoordinateSystem3D slice_; - std::auto_ptr currentScene_; - std::auto_ptr pendingScene_; - std::vector changedLayers_; - - bool LookupLayer(size_t& index /* out */, - const ILayerSource& layer) const; - - void GetLayerExtent(Extent2D& extent, - ILayerSource& source) const; - - void OnGeometryReady(const ILayerSource::GeometryReadyMessage& message); - - virtual void OnContentChanged(const ILayerSource::ContentChangedMessage& message); - - virtual void OnSliceChanged(const ILayerSource::SliceChangedMessage& message); - - virtual void OnLayerReady(const ILayerSource::LayerReadyMessage& message); - - virtual void OnLayerError(const ILayerSource::LayerErrorMessage& message); - - void ObserveLayer(ILayerSource& source); - - void ResetChangedLayers(); - - public: - LayerWidget(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& slice); - - void InvalidateAllLayers(); - - void InvalidateLayer(size_t layer); - - public: - virtual ~LayerWidget(); - - size_t AddLayer(ILayerSource* layer); // Takes ownership - - void ReplaceLayer(size_t layerIndex, ILayerSource* 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& slice); - - const CoordinateSystem3D& GetSlice() const - { - return slice_; - } - - virtual bool HasAnimation() const - { - return true; - } - - virtual void DoAnimation(); - }; -} diff -r a8b5cf760473 -r 20f149669c1f Framework/Widgets/SliceViewerWidget.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/SliceViewerWidget.cpp Fri Nov 09 17:26:39 2018 +0100 @@ -0,0 +1,651 @@ +/** + * 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 "SliceViewerWidget.h" + +#include "../Layers/SliceOutlineRenderer.h" +#include "../Toolbox/GeometryToolbox.h" +#include "Framework/Layers/FrameRenderer.h" + +#include + +#include + + +static const double THIN_SLICE_THICKNESS = 100.0 * std::numeric_limits::epsilon(); + +namespace OrthancStone +{ + class SliceViewerWidget::Scene : public boost::noncopyable + { + private: + CoordinateSystem3D slice_; + 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& slice, + double thickness, + size_t countLayers) : + slice_(slice), + 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& GetSlice() const + { + return slice_; + } + + bool HasRenderer(size_t index) + { + return renderers_[index] != NULL; + } + + bool IsComplete() const + { + return countMissing_ == 0; + } + + unsigned int GetCountMissing() const + { + return countMissing_; + } + + bool RenderScene(CairoContext& context, + const ViewportGeometry& view, + const CoordinateSystem3D& viewportSlice) + { + bool fullQuality = true; + cairo_t *cr = context.GetObject(); + + for (size_t i = 0; i < renderers_.size(); i++) + { + if (renderers_[i] != NULL) + { + const CoordinateSystem3D& frameSlice = renderers_[i]->GetLayerSlice(); + + double x0, y0, x1, y1, x2, y2; + viewportSlice.ProjectPoint(x0, y0, frameSlice.GetOrigin()); + viewportSlice.ProjectPoint(x1, y1, frameSlice.GetOrigin() + frameSlice.GetAxisX()); + viewportSlice.ProjectPoint(x2, y2, frameSlice.GetOrigin() + frameSlice.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& slice) const + { + bool isOpposite; + if (!GeometryToolbox::IsParallelOrOpposite(isOpposite, + slice.GetNormal(), + slice_.GetNormal())) + { + return false; + } + else + { + double z = (slice_.ProjectAlongNormal(slice.GetOrigin()) - + slice_.ProjectAlongNormal(slice_.GetOrigin())); + + if (z < 0) + { + z = -z; + } + + return z <= thickness_; + } + } + + double GetThickness() const + { + return thickness_; + } + }; + + + bool SliceViewerWidget::LookupLayer(size_t& index /* out */, + const ILayerSource& 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, + ILayerSource& source) const + { + extent.Reset(); + + std::vector points; + if (source.GetExtent(points, slice_)) + { + for (size_t i = 0; i < points.size(); i++) + { + double x, y; + slice_.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, slice_); + } + else + { + return true; + } + } + + + void SliceViewerWidget::ResetPendingScene() + { + double thickness; + if (pendingScene_.get() == NULL) + { + thickness = 1.0; + } + else + { + thickness = pendingScene_->GetThickness(); + } + + pendingScene_.reset(new Scene(slice_, thickness, layers_.size())); + } + + + void SliceViewerWidget::UpdateLayer(size_t index, + ILayerRenderer* renderer, + const CoordinateSystem3D& slice) + { + 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(slice)) + { + currentScene_->SetLayer(index, tmp.release()); + NotifyContentChanged(); + } + else if (pendingScene_.get() != NULL && + pendingScene_->ContainsPlane(slice)) + { + 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(ILayerSource& 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(ILayerSource* 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, ILayerSource* 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); + } + + ILayerSource* 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& slice) + { + LOG(INFO) << "Setting slice origin: (" << slice.GetOrigin()[0] + << "," << slice.GetOrigin()[1] + << "," << slice.GetOrigin()[2] << ")"; + + Slice displayedSlice(slice_, THIN_SLICE_THICKNESS); + + //if (!displayedSlice.ContainsPlane(slice)) + { + if (currentScene_.get() == NULL || + (pendingScene_.get() != NULL && + pendingScene_->IsComplete())) + { + currentScene_ = pendingScene_; + } + + slice_ = slice; + ResetPendingScene(); + + InvalidateAllLayers(); // TODO Removing this line avoid loading twice the image in WASM + } + } + + + void SliceViewerWidget::OnGeometryReady(const ILayerSource::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(slice_); + } + EmitMessage(GeometryChangedMessage(*this)); + } + + + void SliceViewerWidget::InvalidateAllLayers() + { + for (size_t i = 0; i < layers_.size(); i++) + { + assert(layers_[i] != NULL); + changedLayers_[i] = true; + + //layers_[i]->ScheduleLayerCreation(slice_); + } + } + + + 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(slice_); + } + + + void SliceViewerWidget::OnContentChanged(const ILayerSource::ContentChangedMessage& message) + { + size_t index; + if (LookupLayer(index, message.GetOrigin())) + { + InvalidateLayer(index); + } + + EmitMessage(SliceViewerWidget::ContentChangedMessage(*this)); + } + + + void SliceViewerWidget::OnSliceChanged(const ILayerSource::SliceChangedMessage& message) + { + if (message.GetSlice().ContainsPlane(slice_)) + { + size_t index; + if (LookupLayer(index, message.GetOrigin())) + { + InvalidateLayer(index); + } + } + + EmitMessage(SliceViewerWidget::ContentChangedMessage(*this)); + } + + + void SliceViewerWidget::OnLayerReady(const ILayerSource::LayerReadyMessage& message) + { + size_t index; + if (LookupLayer(index, message.GetOrigin())) + { + LOG(INFO) << "Renderer ready for layer " << index; + UpdateLayer(index, message.CreateRenderer(), message.GetSlice()); + } + + EmitMessage(SliceViewerWidget::ContentChangedMessage(*this)); + } + + + void SliceViewerWidget::OnLayerError(const ILayerSource::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); + + EmitMessage(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(slice_); + } + } + + ResetChangedLayers(); + } +} diff -r a8b5cf760473 -r 20f149669c1f Framework/Widgets/SliceViewerWidget.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/SliceViewerWidget.h Fri Nov 09 17:26:39 2018 +0100 @@ -0,0 +1,129 @@ +/** + * 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 "WorldSceneWidget.h" +#include "../Layers/ILayerSource.h" +#include "../Toolbox/Extent2D.h" +#include "../../Framework/Messages/IObserver.h" + +#include + +namespace OrthancStone +{ + class SliceViewerWidget : + public WorldSceneWidget, + public IObserver, + public IObservable + { + public: + typedef OriginMessage GeometryChangedMessage; + typedef OriginMessage ContentChangedMessage; + + private: + class Scene; + + typedef std::map LayersIndex; + + bool started_; + LayersIndex layersIndex_; + std::vector layers_; + std::vector styles_; + CoordinateSystem3D slice_; + std::auto_ptr currentScene_; + std::auto_ptr pendingScene_; + std::vector changedLayers_; + + bool LookupLayer(size_t& index /* out */, + const ILayerSource& layer) const; + + void GetLayerExtent(Extent2D& extent, + ILayerSource& source) const; + + void OnGeometryReady(const ILayerSource::GeometryReadyMessage& message); + + virtual void OnContentChanged(const ILayerSource::ContentChangedMessage& message); + + virtual void OnSliceChanged(const ILayerSource::SliceChangedMessage& message); + + virtual void OnLayerReady(const ILayerSource::LayerReadyMessage& message); + + virtual void OnLayerError(const ILayerSource::LayerErrorMessage& message); + + void ObserveLayer(ILayerSource& 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& slice); + + void InvalidateAllLayers(); + + void InvalidateLayer(size_t layer); + + public: + virtual ~SliceViewerWidget(); + + size_t AddLayer(ILayerSource* layer); // Takes ownership + + void ReplaceLayer(size_t layerIndex, ILayerSource* 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& slice); + + const CoordinateSystem3D& GetSlice() const + { + return slice_; + } + + virtual bool HasAnimation() const + { + return true; + } + + virtual void DoAnimation(); + }; +} diff -r a8b5cf760473 -r 20f149669c1f Framework/dev.h --- a/Framework/dev.h Fri Nov 09 17:11:35 2018 +0100 +++ b/Framework/dev.h Fri Nov 09 17:26:39 2018 +0100 @@ -25,7 +25,7 @@ #include "Layers/LayerSourceBase.h" #include "Layers/SliceOutlineRenderer.h" #include "Layers/LineLayerRenderer.h" -#include "Widgets/LayerWidget.h" +#include "Widgets/SliceViewerWidget.h" #include "Toolbox/DownloadStack.h" #include "Toolbox/GeometryToolbox.h" #include "Toolbox/OrthancSlicesLoader.h" @@ -702,7 +702,7 @@ protected ISlicedVolume::IObserver { private: - LayerWidget& widget_; + SliceViewerWidget& widget_; VolumeProjection projection_; std::auto_ptr slices_; size_t slice_; @@ -799,7 +799,7 @@ public: VolumeImageInteractor(OrthancVolumeImage& volume, - LayerWidget& widget, + SliceViewerWidget& widget, VolumeProjection projection) : widget_(widget), projection_(projection) @@ -894,10 +894,10 @@ } }; - LayerWidget& otherPlane_; + SliceViewerWidget& otherPlane_; public: - SliceLocationSource(MessageBroker& broker, LayerWidget& otherPlane) : + SliceLocationSource(MessageBroker& broker, SliceViewerWidget& otherPlane) : LayerSourceBase(broker), otherPlane_(otherPlane) { diff -r a8b5cf760473 -r 20f149669c1f Resources/CMake/OrthancStoneConfiguration.cmake --- a/Resources/CMake/OrthancStoneConfiguration.cmake Fri Nov 09 17:11:35 2018 +0100 +++ b/Resources/CMake/OrthancStoneConfiguration.cmake Fri Nov 09 17:26:39 2018 +0100 @@ -287,9 +287,9 @@ ${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/LayerWidget.cpp ${ORTHANC_STONE_ROOT}/Framework/Widgets/LayoutWidget.cpp ${ORTHANC_STONE_ROOT}/Framework/Widgets/PanMouseTracker.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 diff -r a8b5cf760473 -r 20f149669c1f Resources/OrthancStone.doxygen --- a/Resources/OrthancStone.doxygen Fri Nov 09 17:11:35 2018 +0100 +++ b/Resources/OrthancStone.doxygen Fri Nov 09 17:26:39 2018 +0100 @@ -655,7 +655,9 @@ # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = @ORTHANC_STONE_ROOT@/Framework @ORTHANC_STONE_ROOT@/Platforms +INPUT = @ORTHANC_STONE_ROOT@/Applications \ + @ORTHANC_STONE_ROOT@/Framework \ + @ORTHANC_STONE_ROOT@/Platforms # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is