Mercurial > hg > orthanc-stone
changeset 1809:79a5838739a6
starting the integration of AnnotationsSceneLayer into Stone Web viewer
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 20 May 2021 18:52:02 +0200 |
parents | 797633f48a9c |
children | b05d5f0d014f |
files | Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp Applications/StoneWebViewer/WebApplication/index.html Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.cpp OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.h |
diffstat | 5 files changed, 162 insertions(+), 47 deletions(-) [+] |
line wrap: on
line diff
--- a/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp Thu May 20 17:28:16 2021 +0200 +++ b/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp Thu May 20 18:52:02 2021 +0200 @@ -314,7 +314,7 @@ #if SAMPLE_USE_ANNOTATIONS_LAYER == 1 case SDLK_d: - annotations.SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Erase); + annotations.SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Remove); break; #endif
--- a/Applications/StoneWebViewer/WebApplication/index.html Thu May 20 17:28:16 2021 +0200 +++ b/Applications/StoneWebViewer/WebApplication/index.html Thu May 20 18:52:02 2021 +0200 @@ -472,6 +472,27 @@ <div class="ng-scope inline-object"> <button class="wvButton--underline text-center" + data-toggle="tooltip" data-title="Measure length"> + <i class="fas fa-arrows-alt-h"></i> + </button> + </div> + + <div class="ng-scope inline-object"> + <button class="wvButton--underline text-center" + data-toggle="tooltip" data-title="Measure angle"> + <i class="fas fa-angle-left fa-lg"></i> + </button> + </div> + + <div class="ng-scope inline-object"> + <button class="wvButton--underline text-center" + data-toggle="tooltip" data-title="Measure circle"> + <i class="far fa-circle"></i> + </button> + </div> + + <div class="ng-scope inline-object"> + <button class="wvButton--underline text-center" data-toggle="tooltip" data-title="Synchronized browsing" v-bind:class="{ 'active' : synchronizedBrowsing }" v-on:click="synchronizedBrowsing = !synchronizedBrowsing">
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Thu May 20 17:28:16 2021 +0200 +++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Thu May 20 18:52:02 2021 +0200 @@ -70,6 +70,7 @@ #include <Messages/ObserverBase.h> #include <Oracle/ParseDicomFromWadoCommand.h> #include <Oracle/ParseDicomSuccessMessage.h> +#include <Scene2D/AnnotationsSceneLayer.h> #include <Scene2D/ArrowSceneLayer.h> #include <Scene2D/ColorTextureSceneLayer.h> #include <Scene2D/FloatTextureSceneLayer.h> @@ -133,7 +134,12 @@ WebViewerAction_Zoom, WebViewerAction_Pan, WebViewerAction_Rotate, - WebViewerAction_Crosshair + WebViewerAction_Crosshair, + + WebViewerAction_CreateAngle, + WebViewerAction_CreateCircle, + WebViewerAction_CreateSegment, + WebViewerAction_DeleteMeasure }; @@ -155,6 +161,10 @@ return OrthancStone::MouseAction_Rotate; case WebViewerAction_Crosshair: + case WebViewerAction_CreateAngle: + case WebViewerAction_CreateCircle: + case WebViewerAction_CreateSegment: + case WebViewerAction_DeleteMeasure: return OrthancStone::MouseAction_None; default: @@ -1195,7 +1205,8 @@ private: static const int LAYER_TEXTURE = 0; static const int LAYER_REFERENCE_LINES = 1; - static const int LAYER_ANNOTATIONS = 2; + static const int LAYER_ANNOTATIONS_OSIRIX = 2; + static const int LAYER_ANNOTATIONS_STONE = 3; class ICommand : public Orthanc::IDynamicObject @@ -1568,6 +1579,7 @@ size_t focusFrameNumber_; boost::shared_ptr<OrthancStone::OsiriX::CollectionOfAnnotations> annotations_; + boost::shared_ptr<OrthancStone::AnnotationsSceneLayer> annotationsStone_; void ScheduleNextPrefetch() { @@ -1741,7 +1753,7 @@ layer->SetPixelSpacing(pixelSpacingX, pixelSpacingY); } - std::unique_ptr<OrthancStone::MacroSceneLayer> annotationsLayer; + std::unique_ptr<OrthancStone::MacroSceneLayer> annotationsOsiriX; if (annotations_) { @@ -1750,8 +1762,8 @@ if (plane.IsValid() && !a.empty()) { - annotationsLayer.reset(new OrthancStone::MacroSceneLayer); - // annotationsLayer->Reserve(a.size()); + annotationsOsiriX.reset(new OrthancStone::MacroSceneLayer); + // annotationsOsiriX->Reserve(a.size()); OrthancStone::OsiriXLayerFactory factory; factory.SetColor(0, 255, 0); @@ -1759,35 +1771,39 @@ for (std::set<size_t>::const_iterator it = a.begin(); it != a.end(); ++it) { const OrthancStone::OsiriX::Annotation& annotation = annotations_->GetAnnotation(*it); - annotationsLayer->AddLayer(factory.Create(annotation, plane)); + annotationsOsiriX->AddLayer(factory.Create(annotation, plane)); } } } - std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); - - OrthancStone::Scene2D& scene = lock->GetController().GetScene(); - - scene.SetLayer(LAYER_TEXTURE, layer.release()); - - if (annotationsLayer.get() != NULL) { - scene.SetLayer(LAYER_ANNOTATIONS, annotationsLayer.release()); + std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); + + OrthancStone::Scene2D& scene = lock->GetController().GetScene(); + + scene.SetLayer(LAYER_TEXTURE, layer.release()); + + if (annotationsOsiriX.get() != NULL) + { + scene.SetLayer(LAYER_ANNOTATIONS_OSIRIX, annotationsOsiriX.release()); + } + else + { + scene.DeleteLayer(LAYER_ANNOTATIONS_OSIRIX); + } + + if (fitNextContent_) + { + lock->RefreshCanvasSize(); + lock->GetCompositor().FitContent(scene); + fitNextContent_ = false; + } + + annotationsStone_->Render(scene); + + //lock->GetCompositor().Refresh(scene); + lock->Invalidate(); } - else - { - scene.DeleteLayer(LAYER_ANNOTATIONS); - } - - if (fitNextContent_) - { - lock->RefreshCanvasSize(); - lock->GetCompositor().FitContent(scene); - fitNextContent_ = false; - } - - //lock->GetCompositor().Refresh(scene); - lock->Invalidate(); } @@ -2010,12 +2026,24 @@ std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); std::string ttf; Orthanc::EmbeddedResources::GetFileResource(ttf, Orthanc::EmbeddedResources::UBUNTU_FONT); - lock->GetCompositor().SetFont(0, ttf, 24 /* font size */, Orthanc::Encoding_Latin1); + lock->GetCompositor().SetFont(0, ttf, 16 /* font size */, Orthanc::Encoding_Latin1); } emscripten_set_wheel_callback(viewport_->GetCanvasCssSelector().c_str(), this, true, OnWheel); SetWindowingPreset(); + + { + annotationsStone_.reset(new OrthancStone::AnnotationsSceneLayer(LAYER_ANNOTATIONS_STONE)); + annotationsStone_->AddSegmentAnnotation(OrthancStone::ScenePoint2D(0, 0), + OrthancStone::ScenePoint2D(100, 100)); + annotationsStone_->AddAngleAnnotation(OrthancStone::ScenePoint2D(100, 50), + OrthancStone::ScenePoint2D(150, 40), + OrthancStone::ScenePoint2D(200, 50)); + annotationsStone_->AddCircleAnnotation(OrthancStone::ScenePoint2D(50, 200), + OrthancStone::ScenePoint2D(100, 250)); + annotationsStone_->SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Edit); + } } @@ -2095,6 +2123,36 @@ dynamic_cast<const ICommand&>(message.GetOrigin().GetPayload()).Handle(message); } + + void RefreshAnnotations() + { + std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); + annotationsStone_->Render(lock->GetController().GetScene()); + lock->Invalidate(); + } + + void Handle(const OrthancStone::ViewportController::SceneTransformChanged& message) + { + RefreshAnnotations(); + } + + void Handle(const OrthancStone::AnnotationsSceneLayer::AnnotationChangedMessage& message) + { + RefreshAnnotations(); + } + + void Handle(const OrthancStone::AnnotationsSceneLayer::AnnotationAddedMessage& message) + { + RefreshAnnotations(); + LOG(WARNING) << "annotation added"; + } + + void Handle(const OrthancStone::AnnotationsSceneLayer::AnnotationRemovedMessage& message) + { + RefreshAnnotations(); + LOG(WARNING) << "annotation removed"; + } + public: virtual ~ViewerViewport() { @@ -2125,11 +2183,21 @@ viewport->Register<OrthancStone::ParseDicomSuccessMessage>( lock->GetOracleObservable(), &ViewerViewport::Handle); + + viewport->Register<OrthancStone::AnnotationsSceneLayer::AnnotationChangedMessage>( + *viewport->annotationsStone_, &ViewerViewport::Handle); + + viewport->Register<OrthancStone::AnnotationsSceneLayer::AnnotationAddedMessage>( + *viewport->annotationsStone_, &ViewerViewport::Handle); + + viewport->Register<OrthancStone::AnnotationsSceneLayer::AnnotationRemovedMessage>( + *viewport->annotationsStone_, &ViewerViewport::Handle); } { std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport->viewport_->Lock()); viewport->Register<OrthancStone::ViewportController::GrayscaleWindowingChanged>(lock->GetController(), &ViewerViewport::Handle); + viewport->Register<OrthancStone::ViewportController::SceneTransformChanged>(lock->GetController(), &ViewerViewport::Handle); } return viewport; @@ -2223,6 +2291,8 @@ lock->GetCompositor().FitContent(lock->GetController().GetScene()); } + annotationsStone_->Render(lock->GetController().GetScene()); + lock->Invalidate(); } @@ -2559,6 +2629,18 @@ } else { + { + std::unique_ptr<OrthancStone::IViewport::ILock> lock2(lock1->Lock()); + + std::unique_ptr<OrthancStone::IFlexiblePointerTracker> t; + t.reset(viewer_.annotationsStone_->CreateTracker(event.GetMainPosition(), lock2->GetController().GetScene())); + + if (t.get() != NULL) + { + return t.release(); + } + } + return DefaultViewportInteractor::CreateTracker( viewport, event, viewportWidth, viewportHeight); }
--- a/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.cpp Thu May 20 17:28:16 2021 +0200 +++ b/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.cpp Thu May 20 18:52:02 2021 +0200 @@ -233,7 +233,7 @@ } virtual bool IsHit(const ScenePoint2D& p, - const Scene2D& scene) const + const Scene2D& scene) const ORTHANC_OVERRIDE { const double zoom = scene.GetSceneToCanvasTransform().ComputeZoom(); @@ -332,7 +332,7 @@ } virtual bool IsHit(const ScenePoint2D& p, - const Scene2D& scene) const + const Scene2D& scene) const ORTHANC_OVERRIDE { const double zoom = scene.GetSceneToCanvasTransform().ComputeZoom(); return (ScenePoint2D::SquaredDistancePtSegment(p1_ + delta_, p2_ + delta_, p) * zoom * zoom <= @@ -415,7 +415,7 @@ } virtual bool IsHit(const ScenePoint2D& p, - const Scene2D& scene) const + const Scene2D& scene) const ORTHANC_OVERRIDE { return false; } @@ -544,7 +544,7 @@ } virtual bool IsHit(const ScenePoint2D& p, - const Scene2D& scene) const + const Scene2D& scene) const ORTHANC_OVERRIDE { return false; } @@ -631,7 +631,7 @@ } virtual bool IsHit(const ScenePoint2D& p, - const Scene2D& scene) const + const Scene2D& scene) const ORTHANC_OVERRIDE { return false; } @@ -677,15 +677,18 @@ class AnnotationsSceneLayer::EditPrimitiveTracker : public IFlexiblePointerTracker { private: - GeometricPrimitive& primitive_; - ScenePoint2D sceneClick_; - AffineTransform2D canvasToScene_; - bool alive_; + AnnotationsSceneLayer& that_; + GeometricPrimitive& primitive_; + ScenePoint2D sceneClick_; + AffineTransform2D canvasToScene_; + bool alive_; public: - EditPrimitiveTracker(GeometricPrimitive& primitive, + EditPrimitiveTracker(AnnotationsSceneLayer& that, + GeometricPrimitive& primitive, const ScenePoint2D& sceneClick, const AffineTransform2D& canvasToScene) : + that_(that), primitive_(primitive), sceneClick_(sceneClick), canvasToScene_(canvasToScene), @@ -696,12 +699,14 @@ virtual void PointerMove(const PointerEvent& event) ORTHANC_OVERRIDE { primitive_.MovePreview(event.GetMainPosition().Apply(canvasToScene_) - sceneClick_); + that_.BroadcastMessage(AnnotationChangedMessage(that_)); } virtual void PointerUp(const PointerEvent& event) ORTHANC_OVERRIDE { primitive_.MoveDone(event.GetMainPosition().Apply(canvasToScene_) - sceneClick_); alive_ = false; + that_.BroadcastMessage(AnnotationChangedMessage(that_)); } virtual void PointerDown(const PointerEvent& event) ORTHANC_OVERRIDE @@ -1141,6 +1146,8 @@ assert(handle2_ != NULL); handle2_->SetCenter(event.GetMainPosition().Apply(canvasToScene_)); annotation_->SignalMove(*handle2_); + + that_.BroadcastMessage(AnnotationChangedMessage(that_)); } } @@ -1197,12 +1204,14 @@ { segment_->GetHandle2().SetCenter(event.GetMainPosition().Apply(canvasToScene_)); segment_->SignalMove(segment_->GetHandle2()); + that_.BroadcastMessage(AnnotationChangedMessage(that_)); } if (angle_ != NULL) { angle_->GetEndHandle().SetCenter(event.GetMainPosition().Apply(canvasToScene_)); angle_->SignalMove(angle_->GetEndHandle()); + that_.BroadcastMessage(AnnotationChangedMessage(that_)); } } @@ -1218,6 +1227,8 @@ that_.DeleteAnnotation(segment_); segment_ = NULL; + + that_.BroadcastMessage(AnnotationChangedMessage(that_)); } else { @@ -1256,10 +1267,10 @@ // Dummy tracker that is only used for deletion, in order to warn // the caller that the mouse action was taken into consideration - class AnnotationsSceneLayer::EraseTracker : public IFlexiblePointerTracker + class AnnotationsSceneLayer::RemoveTracker : public IFlexiblePointerTracker { public: - EraseTracker() + RemoveTracker() { } @@ -1484,15 +1495,15 @@ if (bestHit != NULL) { - if (activeTool_ == Tool_Erase) + if (activeTool_ == Tool_Remove) { DeleteAnnotation(&bestHit->GetParentAnnotation()); BroadcastMessage(AnnotationRemovedMessage(*this)); - return new EraseTracker; + return new RemoveTracker; } else { - return new EditPrimitiveTracker(*bestHit, s, scene.GetCanvasToSceneTransform()); + return new EditPrimitiveTracker(*this, *bestHit, s, scene.GetCanvasToSceneTransform()); } } else
--- a/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.h Thu May 20 17:28:16 2021 +0200 +++ b/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.h Thu May 20 18:52:02 2021 +0200 @@ -31,6 +31,7 @@ public: ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, AnnotationAddedMessage, AnnotationsSceneLayer); ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, AnnotationRemovedMessage, AnnotationsSceneLayer); + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, AnnotationChangedMessage, AnnotationsSceneLayer); enum Tool { @@ -39,7 +40,7 @@ Tool_Segment, Tool_Angle, Tool_Circle, - Tool_Erase + Tool_Remove }; private: @@ -58,7 +59,7 @@ class EditPrimitiveTracker; class CreateSegmentOrCircleTracker; class CreateAngleTracker; - class EraseTracker; + class RemoveTracker; typedef std::set<GeometricPrimitive*> GeometricPrimitives; typedef std::set<Annotation*> Annotations;