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;