changeset 2001:e943a84da9ac

creation of text annotations
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 02 Nov 2022 14:56:35 +0100
parents 3e9ced39cd1b
children 1bb0a9716876
files Applications/StoneWebViewer/NEWS Applications/StoneWebViewer/WebApplication/app.js Applications/StoneWebViewer/WebAssembly/ParseWebAssemblyExports.py Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.cpp OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.h
diffstat 6 files changed, 182 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/StoneWebViewer/NEWS	Wed Nov 02 13:52:14 2022 +0100
+++ b/Applications/StoneWebViewer/NEWS	Wed Nov 02 14:56:35 2022 +0100
@@ -2,6 +2,7 @@
 ===============================
 
 * New types of annotations:
+  - Text annotation
   - Pixel probe
   - Rectangle probe
   - Ellipse probe
--- a/Applications/StoneWebViewer/WebApplication/app.js	Wed Nov 02 13:52:14 2022 +0100
+++ b/Applications/StoneWebViewer/WebApplication/app.js	Wed Nov 02 14:56:35 2022 +0100
@@ -1188,6 +1188,15 @@
     window.addEventListener('StoneAnnotationRemoved', function() {
       // Ignore
     });
+
+    window.addEventListener('TextAnnotationRequired', function(args) {
+      var label = prompt('Enter your annotation:', '');
+      if (label !== null) {
+        stone.AddTextAnnotation(args.detail.canvasId, label,
+                                args.detail.pointedX, args.detail.pointedY,
+                                args.detail.labelX, args.detail.labelY);
+      }
+    });
   }
 });
 
--- a/Applications/StoneWebViewer/WebAssembly/ParseWebAssemblyExports.py	Wed Nov 02 13:52:14 2022 +0100
+++ b/Applications/StoneWebViewer/WebAssembly/ParseWebAssemblyExports.py	Wed Nov 02 14:56:35 2022 +0100
@@ -174,6 +174,8 @@
                         arg['type'] = "'int'"
                     elif argType == 'const char *':
                         arg['type'] = "'string'"
+                    elif argType == 'double':
+                        arg['type'] = "'double'"
                     else:
                         raise Exception('Unknown type for argument "%s" in function "%s()": %s' %
                                         (child.displayname, node.spelling, argType))
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Wed Nov 02 13:52:14 2022 +0100
+++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Wed Nov 02 14:56:35 2022 +0100
@@ -1597,6 +1597,10 @@
     virtual void SignalStoneAnnotationAdded(const ViewerViewport& viewport) = 0;
 
     virtual void SignalStoneAnnotationRemoved(const ViewerViewport& viewport) = 0;
+
+    virtual void SignalStoneTextAnnotationRequired(const ViewerViewport& viewport,
+                                                   const OrthancStone::ScenePoint2D& pointedPosition,
+                                                   const OrthancStone::ScenePoint2D& labelPosition) = 0;
   };
 
 private:
@@ -2692,6 +2696,14 @@
     }
   }
 
+  void Handle(const OrthancStone::AnnotationsSceneLayer::TextAnnotationRequiredMessage& message)
+  {
+    if (observer_.get() != NULL)
+    {
+      observer_->SignalStoneTextAnnotationRequired(*this, message.GetPointedPosition(), message.GetLabelPosition());
+    }
+  }
+
 public:
   virtual ~ViewerViewport()
   {
@@ -2732,6 +2744,9 @@
 
       viewport->Register<OrthancStone::AnnotationsSceneLayer::AnnotationRemovedMessage>(
         *viewport->stoneAnnotations_, &ViewerViewport::Handle);
+
+      viewport->Register<OrthancStone::AnnotationsSceneLayer::TextAnnotationRequiredMessage>(
+        *viewport->stoneAnnotations_, &ViewerViewport::Handle);
     }
 
     {
@@ -3435,6 +3450,14 @@
       Redraw();
     }
   }
+
+  void AddTextAnnotation(const std::string& label,
+                         const OrthancStone::ScenePoint2D& pointedPosition,
+                         const OrthancStone::ScenePoint2D& labelPosition)
+  {
+    stoneAnnotations_->AddTextAnnotation(label, pointedPosition, labelPosition);
+    Redraw();
+  }
 };
 
 
@@ -3695,6 +3718,27 @@
       },
       viewport.GetCanvasId().c_str());
   }
+
+  virtual void SignalStoneTextAnnotationRequired(const ViewerViewport& viewport,
+                                                 const OrthancStone::ScenePoint2D& pointedPosition,
+                                                 const OrthancStone::ScenePoint2D& labelPosition) ORTHANC_OVERRIDE
+  {
+    EM_ASM({
+        const customEvent = document.createEvent("CustomEvent");
+        customEvent.initCustomEvent("TextAnnotationRequired", false, false,
+                                    { "canvasId" : UTF8ToString($0),
+                                      "pointedX" : $1,
+                                      "pointedY" : $2,
+                                      "labelX" : $3,
+                                      "labelY" : $4 });
+        window.dispatchEvent(customEvent);
+      },
+      viewport.GetCanvasId().c_str(),
+      pointedPosition.GetX(),
+      pointedPosition.GetY(),
+      labelPosition.GetX(),
+      labelPosition.GetY() );
+  }
 };
 
 
@@ -4497,4 +4541,21 @@
     EXTERN_CATCH_EXCEPTIONS;
     return false;
   }
+
+
+  EMSCRIPTEN_KEEPALIVE
+  void AddTextAnnotation(const char* canvas,
+                         const char* label,
+                         double pointedX,
+                         double pointedY,
+                         double labelX,
+                         double labelY)
+  {
+    try
+    {
+      GetViewport(canvas)->AddTextAnnotation(label, OrthancStone::ScenePoint2D(pointedX, pointedY),
+                                             OrthancStone::ScenePoint2D(labelX, labelY));
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+  }
 }
--- a/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.cpp	Wed Nov 02 13:52:14 2022 +0100
+++ b/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.cpp	Wed Nov 02 14:56:35 2022 +0100
@@ -1272,14 +1272,25 @@
   public:
     TextAnnotation(AnnotationsSceneLayer& that,
                    const std::string& label,
-                   const ScenePoint2D& p1,
-                   const ScenePoint2D& p2) :
-      SegmentAnnotation(that, Handle::Shape_Invisible, p1, Handle::Shape_Square, p2)
+                   const ScenePoint2D& pointedPosition,
+                   const ScenePoint2D& labelPosition) :
+      SegmentAnnotation(that, Handle::Shape_Invisible, pointedPosition /* p1 */,
+                        Handle::Shape_Square, labelPosition /* p2 */)
     {
       SetStartArrow(true);
       UpdateLabel(label);
     }
 
+    ScenePoint2D GetPointedPosition() const
+    {
+      return GetHandle1().GetCenter();
+    }
+
+    ScenePoint2D GetLabelPosition() const
+    {
+      return GetHandle2().GetCenter();
+    }
+
     void UpdateLabel(const std::string& label)
     {
       TextSceneLayer content;
@@ -2325,6 +2336,25 @@
     AnnotationsSceneLayer&  layer_;
     Annotation*             annotation_;
     AffineTransform2D       canvasToScene_;
+
+  protected:
+    AnnotationsSceneLayer& GetLayer() const
+    {
+      return layer_;
+    }
+    
+    const Annotation& GetAnnotation() const
+    {
+      if (IsAlive())
+      {
+        assert(annotation_ != NULL);
+        return *annotation_;
+      }
+      else
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+      }
+    }
       
   public:
     CreateTwoHandlesTracker(Annotation& annotation,
@@ -2506,6 +2536,35 @@
   };
 
 
+  class AnnotationsSceneLayer::CreateTextAnnotationTracker : public CreateTwoHandlesTracker
+  {
+  public:
+    CreateTextAnnotationTracker(AnnotationsSceneLayer& that,
+                                const std::string& label,
+                                const ScenePoint2D& position,
+                                const AffineTransform2D& canvasToScene) :
+      CreateTwoHandlesTracker(*new TextAnnotation(that, label, position, position), canvasToScene)
+    {
+    }
+
+    virtual void PointerUp(const PointerEvent& event,
+                           const Scene2D& scene) ORTHANC_OVERRIDE
+    {
+      std::unique_ptr<TextAnnotationRequiredMessage> request;
+      
+      {
+        const TextAnnotation& annotation = dynamic_cast<const TextAnnotation&>(GetAnnotation());
+        request.reset(new TextAnnotationRequiredMessage(GetLayer(), annotation.GetPointedPosition(), annotation.GetLabelPosition()));
+      }
+      
+      Cancel(scene);  // Warning: "annotation_" is now invalid!
+      
+      GetLayer().BroadcastMessage(AnnotationChangedMessage(GetLayer()));
+      GetLayer().BroadcastMessage(*request);
+    }
+  };
+
+
   // Dummy tracker that is only used for deletion, in order to warn
   // the caller that the mouse action was taken into consideration
   class AnnotationsSceneLayer::RemoveTracker : public IFlexiblePointerTracker
@@ -2616,6 +2675,7 @@
                                                   const ScenePoint2D& p2)
   {
     annotations_.insert(new LengthAnnotation(*this, units_, true /* show label */, p1, p2));
+    BroadcastMessage(AnnotationChangedMessage(*this));
   }
   
 
@@ -2623,6 +2683,7 @@
                                                   const ScenePoint2D& p2)
   {
     annotations_.insert(new CircleAnnotation(*this, units_, p1, p2));
+    BroadcastMessage(AnnotationChangedMessage(*this));
   }
   
 
@@ -2631,6 +2692,7 @@
                                                  const ScenePoint2D& p3)
   {
     annotations_.insert(new AngleAnnotation(*this, p1, p2, p3));
+    BroadcastMessage(AnnotationChangedMessage(*this));
   }
   
 
@@ -2810,10 +2872,7 @@
           }
 
           case Tool_TextAnnotation:
-          {
-            Annotation* annotation = new TextAnnotation(*this, "" /* empty label */, s, s);
-            return new CreateTwoHandlesTracker(*annotation, scene.GetCanvasToSceneTransform());
-          }
+            return new CreateTextAnnotationTracker(*this, "" /* empty label */, s, scene.GetCanvasToSceneTransform());
 
           default:
             return NULL;
@@ -2930,4 +2989,13 @@
       }
     }
   }
+
+
+  void AnnotationsSceneLayer::AddTextAnnotation(const std::string& label,
+                                                const ScenePoint2D& pointedPosition,
+                                                const ScenePoint2D& labelPosition)
+  {
+    annotations_.insert(new TextAnnotation(*this, label, pointedPosition, labelPosition));
+    BroadcastMessage(AnnotationChangedMessage(*this));
+  }
 }
--- a/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.h	Wed Nov 02 13:52:14 2022 +0100
+++ b/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.h	Wed Nov 02 14:56:35 2022 +0100
@@ -34,6 +34,35 @@
     ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, AnnotationRemovedMessage, AnnotationsSceneLayer);
     ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, AnnotationChangedMessage, AnnotationsSceneLayer);
 
+    class TextAnnotationRequiredMessage : public OriginMessage<AnnotationsSceneLayer>
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+
+    private:
+      ScenePoint2D pointedPosition_;
+      ScenePoint2D labelPosition_;
+
+    public:
+      TextAnnotationRequiredMessage(const AnnotationsSceneLayer& origin,
+                                    ScenePoint2D pointedPosition,
+                                    ScenePoint2D labelPosition) :
+        OriginMessage(origin),
+        pointedPosition_(pointedPosition),
+        labelPosition_(labelPosition)
+      {
+      }
+      
+      const ScenePoint2D& GetPointedPosition() const
+      {
+        return pointedPosition_;
+      }
+      
+      const ScenePoint2D& GetLabelPosition() const
+      {
+        return labelPosition_;
+      }
+    };
+    
     enum Tool
     {
       Tool_Edit,
@@ -73,6 +102,7 @@
     class CreateAngleTracker;
     class CreatePixelProbeTracker;
     class RemoveTracker;
+    class CreateTextAnnotationTracker;
 
     typedef std::set<GeometricPrimitive*>  GeometricPrimitives;
     typedef std::set<Annotation*>          Annotations;
@@ -155,5 +185,9 @@
     {
       return probedLayer_;
     }
+
+    void AddTextAnnotation(const std::string& label,
+                           const ScenePoint2D& pointedPosition,
+                           const ScenePoint2D& labelPosition);
   };
 }