changeset 1998:1fa3f484008e

added arrows to AnnotationsSceneLayer::Segment
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 02 Nov 2022 13:08:50 +0100
parents c622219a3388
children 709b90ae0f89
files Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp Applications/StoneWebViewer/WebApplication/app.js Applications/StoneWebViewer/WebApplication/index.html Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.cpp OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.h
diffstat 6 files changed, 128 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp	Tue Nov 01 19:42:27 2022 +0100
+++ b/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp	Wed Nov 02 13:08:50 2022 +0100
@@ -81,7 +81,7 @@
             << "  c\tCreate circle annotations" << std::endl
             << "  d\tDelete mode for annotations" << std::endl
             << "  e\tCreate ellipse probe" << std::endl
-            << "  l\tCreate line annotations" << std::endl
+            << "  l\tCreate length annotations" << std::endl
             << "  m\tModification/edit mode, don't create annotation (default)" << std::endl
             << "  p\tCreate pixel probe" << std::endl
             << "  r\tCreate rectangle probe" << std::endl
@@ -317,7 +317,7 @@
 
                   case SDLK_l:
 #if SAMPLE_USE_ANNOTATIONS_LAYER == 1
-                    annotations.SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Segment);
+                    annotations.SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Length);
 #else
                     if (activeTool == ActiveTool_Line)
                     {
--- a/Applications/StoneWebViewer/WebApplication/app.js	Tue Nov 01 19:42:27 2022 +0100
+++ b/Applications/StoneWebViewer/WebApplication/app.js	Wed Nov 02 13:08:50 2022 +0100
@@ -42,7 +42,7 @@
 var MOUSE_TOOL_ZOOM = 2;
 var MOUSE_TOOL_PAN = 3;
 var MOUSE_TOOL_CROSSHAIR = 4;
-var MOUSE_TOOL_CREATE_SEGMENT = 5;
+var MOUSE_TOOL_CREATE_LENGTH = 5;
 var MOUSE_TOOL_CREATE_ANGLE = 6;
 var MOUSE_TOOL_CREATE_CIRCLE = 7;
 var MOUSE_TOOL_REMOVE_MEASURE = 8;
--- a/Applications/StoneWebViewer/WebApplication/index.html	Tue Nov 01 19:42:27 2022 +0100
+++ b/Applications/StoneWebViewer/WebApplication/index.html	Wed Nov 02 13:08:50 2022 +0100
@@ -530,8 +530,8 @@
             
             <div class="ng-scope inline-object">
               <button class="wvButton--underline text-center"
-                      v-bind:class="{ 'active' : mouseTool == MOUSE_TOOL_CREATE_SEGMENT }"
-                      v-on:click="SetLeftMouseButtonAction(MOUSE_TOOL_CREATE_SEGMENT, stone.WebViewerAction.CREATE_SEGMENT)"
+                      v-bind:class="{ 'active' : mouseTool == MOUSE_TOOL_CREATE_LENGTH }"
+                      v-on:click="SetLeftMouseButtonAction(MOUSE_TOOL_CREATE_LENGTH, stone.WebViewerAction.CREATE_LENGTH)"
                       data-toggle="tooltip" data-title="Measure length">
                 <i class="fas fa-ruler"></i>
               </button>
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Tue Nov 01 19:42:27 2022 +0100
+++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Wed Nov 02 13:08:50 2022 +0100
@@ -145,7 +145,7 @@
     
     WebViewerAction_CreateAngle,
     WebViewerAction_CreateCircle,
-    WebViewerAction_CreateSegment,
+    WebViewerAction_CreateLength,
     WebViewerAction_RemoveMeasure,
     WebViewerAction_CreatePixelProbe,      // New in 2.4
     WebViewerAction_CreateEllipseProbe,    // New in 2.4
@@ -178,7 +178,7 @@
     case WebViewerAction_Crosshair:
     case WebViewerAction_CreateAngle:
     case WebViewerAction_CreateCircle:
-    case WebViewerAction_CreateSegment:
+    case WebViewerAction_CreateLength:
     case WebViewerAction_RemoveMeasure:
     case WebViewerAction_CreatePixelProbe:
     case WebViewerAction_CreateEllipseProbe:
@@ -3231,8 +3231,8 @@
               viewer_.stoneAnnotations_->SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Circle);
               break;
               
-            case WebViewerAction_CreateSegment:
-              viewer_.stoneAnnotations_->SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Segment);
+            case WebViewerAction_CreateLength:
+              viewer_.stoneAnnotations_->SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Length);
               break;
 
             case WebViewerAction_RemoveMeasure:
--- a/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.cpp	Tue Nov 01 19:42:27 2022 +0100
+++ b/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.cpp	Wed Nov 02 13:08:50 2022 +0100
@@ -34,11 +34,15 @@
 #include <boost/math/constants/constants.hpp>
 #include <list>
 
+static const double PI = boost::math::constants::pi<double>();
+
 static const double HANDLE_SIZE = 10.0;
-static const double PI = boost::math::constants::pi<double>();
+static const double ARROW_LENGTH = 1.5 * HANDLE_SIZE;
+static const double ARROW_ANGLE = 20.0 * PI / 180.0;
 
 static const char* const KEY_ANNOTATIONS = "annotations";
 static const char* const KEY_TYPE = "type";
+static const char* const KEY_TEXT = "text";
 static const char* const KEY_X = "x";
 static const char* const KEY_Y = "y";
 static const char* const KEY_X1 = "x1";
@@ -51,12 +55,13 @@
 
 static const char* const VALUE_ANGLE = "angle";
 static const char* const VALUE_CIRCLE = "circle";
-static const char* const VALUE_SEGMENT = "segment";
+static const char* const VALUE_LENGTH = "length";
 static const char* const VALUE_MILLIMETERS = "millimeters";
 static const char* const VALUE_PIXELS = "pixels";
 static const char* const VALUE_PIXEL_PROBE = "pixel-probe";
 static const char* const VALUE_RECTANGLE_PROBE = "rectangle-probe";
 static const char* const VALUE_ELLIPSE_PROBE = "ellipse-probe";
+static const char* const VALUE_TEXT_ANNOTATION = "text";
 
 #if 0
 static OrthancStone::Color COLOR_PRIMITIVES(192, 192, 192);
@@ -301,9 +306,7 @@
     void SetCenter(double x,
                    double y)
     {
-      SetModified(true);
-      center_ = ScenePoint2D(x, y);
-      delta_ = ScenePoint2D(0, 0);
+      SetCenter(ScenePoint2D(x, y));
     }
 
     ScenePoint2D GetCenter() const
@@ -394,6 +397,8 @@
     ScenePoint2D  p1_;
     ScenePoint2D  p2_;
     ScenePoint2D  delta_;
+    bool          hasStartArrow_;
+    bool          hasEndArrow_;
       
   public:
     Segment(Annotation& parentAnnotation,
@@ -402,7 +407,9 @@
       GeometricPrimitive(parentAnnotation, 1),  // Can only be selected if no handle matches
       p1_(p1),
       p2_(p2),
-      delta_(0, 0)
+      delta_(0, 0),
+      hasStartArrow_(false),
+      hasEndArrow_(false)
     {
     }
 
@@ -414,7 +421,9 @@
       GeometricPrimitive(parentAnnotation, 1),  // Can only be selected if no handle matches
       p1_(x1, y1),
       p2_(x2, y2),
-      delta_(0, 0)
+      delta_(0, 0),
+      hasStartArrow_(false),
+      hasEndArrow_(false)
     {
     }
 
@@ -448,6 +457,28 @@
       return p2_ + delta_;
     }
 
+    void SetStartArrow(bool enabled)
+    {
+      SetModified(true);
+      hasStartArrow_ = enabled;
+    }
+
+    bool HasStartArrow() const
+    {
+      return hasStartArrow_;
+    }
+
+    void SetEndArrow(bool enabled)
+    {
+      SetModified(true);
+      hasEndArrow_ = enabled;
+    }
+
+    bool HasEndArrow() const
+    {
+      return hasEndArrow_;
+    }
+
     virtual bool IsHit(const ScenePoint2D& p,
                        const Scene2D& scene) const ORTHANC_OVERRIDE
     {
@@ -459,7 +490,38 @@
     virtual void RenderPolylineLayer(PolylineSceneLayer& polyline,
                                      const Scene2D& scene) ORTHANC_OVERRIDE
     {
-      polyline.AddSegment(p1_ + delta_, p2_ + delta_, GetActiveColor());
+      const Color color = GetActiveColor();
+      const ScenePoint2D a(p1_ + delta_);
+      const ScenePoint2D b(p2_ + delta_);
+      
+      polyline.AddSegment(a, b, color);
+      
+      if (hasStartArrow_ ||
+          hasEndArrow_)
+      {
+        const double length = ARROW_LENGTH / scene.GetSceneToCanvasTransform().ComputeZoom();
+        const double angle = atan2(b.GetY() - a.GetY(), b.GetX() - a.GetX());
+
+        if (hasStartArrow_)
+        {
+          polyline.AddSegment(a, a + ScenePoint2D(
+                                length * cos(angle + ARROW_ANGLE),
+                              length * sin(angle + ARROW_ANGLE)), color);
+          polyline.AddSegment(a, a + ScenePoint2D(
+                                length * cos(angle - ARROW_ANGLE),
+                                length * sin(angle - ARROW_ANGLE)), color);
+        }
+
+        if (hasEndArrow_)
+        {
+          polyline.AddSegment(b, b + ScenePoint2D(
+                                length * cos(angle + ARROW_ANGLE + PI),
+                                length * sin(angle + ARROW_ANGLE + PI)), color);
+          polyline.AddSegment(b, b + ScenePoint2D(
+                                length * cos(angle - ARROW_ANGLE + PI),
+                                length * sin(angle - ARROW_ANGLE + PI)), color);
+        }
+      }
     }
       
     virtual void RenderOtherLayers(MacroSceneLayer& macro,
@@ -996,7 +1058,7 @@
   };
 
 
-  class AnnotationsSceneLayer::SegmentAnnotation : public Annotation
+  class AnnotationsSceneLayer::LengthAnnotation : public Annotation
   {
   private:
     bool      showLabel_;
@@ -1054,11 +1116,11 @@
     }
 
   public:
-    SegmentAnnotation(AnnotationsSceneLayer& that,
-                      Units units,
-                      bool showLabel,
-                      const ScenePoint2D& p1,
-                      const ScenePoint2D& p2) :
+    LengthAnnotation(AnnotationsSceneLayer& that,
+                     Units units,
+                     bool showLabel,
+                     const ScenePoint2D& p1,
+                     const ScenePoint2D& p2) :
       Annotation(that, units),
       showLabel_(showLabel),
       handle1_(AddTypedPrimitive<Handle>(new Handle(*this, Handle::Shape_Square, p1))),
@@ -1118,7 +1180,7 @@
     virtual void Serialize(Json::Value& target) ORTHANC_OVERRIDE
     {
       target = Json::objectValue;
-      target[KEY_TYPE] = VALUE_SEGMENT;
+      target[KEY_TYPE] = VALUE_LENGTH;
       target[KEY_X1] = handle1_.GetCenter().GetX();
       target[KEY_Y1] = handle1_.GetCenter().GetY();
       target[KEY_X2] = handle2_.GetCenter().GetX();
@@ -1138,13 +1200,13 @@
           source[KEY_X2].isNumeric() &&
           source[KEY_Y2].isNumeric())
       {
-        new SegmentAnnotation(target, units, true,
-                              ScenePoint2D(source[KEY_X1].asDouble(), source[KEY_Y1].asDouble()),
-                              ScenePoint2D(source[KEY_X2].asDouble(), source[KEY_Y2].asDouble()));
+        new LengthAnnotation(target, units, true,
+                             ScenePoint2D(source[KEY_X1].asDouble(), source[KEY_Y1].asDouble()),
+                             ScenePoint2D(source[KEY_X2].asDouble(), source[KEY_Y2].asDouble()));
       }
       else
       {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Cannot unserialize an segment annotation");
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Cannot unserialize a length annotation");
       }
     }
   };
@@ -2179,7 +2241,7 @@
   {
   private:
     AnnotationsSceneLayer&  that_;
-    SegmentAnnotation*      segment_;
+    LengthAnnotation*       length_;
     AngleAnnotation*        angle_;
     AffineTransform2D       canvasToScene_;
       
@@ -2189,20 +2251,20 @@
                        const ScenePoint2D& sceneClick,
                        const AffineTransform2D& canvasToScene) :
       that_(that),
-      segment_(NULL),
+      length_(NULL),
       angle_(NULL),
       canvasToScene_(canvasToScene)
     {
-      segment_ = new SegmentAnnotation(that, units, false /* no length label */, sceneClick, sceneClick);
+      length_ = new LengthAnnotation(that, units, false /* no length label */, sceneClick, sceneClick);
     }
 
     virtual void PointerMove(const PointerEvent& event,
                              const Scene2D& scene) ORTHANC_OVERRIDE
     {
-      if (segment_ != NULL)
+      if (length_ != NULL)
       {
-        segment_->GetHandle(1).SetCenter(event.GetMainPosition().Apply(canvasToScene_));
-        segment_->SignalMove(segment_->GetHandle(1), scene);
+        length_->GetHandle(1).SetCenter(event.GetMainPosition().Apply(canvasToScene_));
+        length_->SignalMove(length_->GetHandle(1), scene);
         that_.BroadcastMessage(AnnotationChangedMessage(that_));
       }
 
@@ -2217,16 +2279,16 @@
     virtual void PointerUp(const PointerEvent& event,
                            const Scene2D& scene) ORTHANC_OVERRIDE
     {
-      if (segment_ != NULL)
+      if (length_ != NULL)
       {
         // End of first step: The first segment is available, now create the angle
 
-        angle_ = new AngleAnnotation(that_, segment_->GetUnits(), segment_->GetHandle(0).GetCenter(),
-                                     segment_->GetHandle(1).GetCenter(),
-                                     segment_->GetHandle(1).GetCenter());
+        angle_ = new AngleAnnotation(that_, length_->GetUnits(), length_->GetHandle(0).GetCenter(),
+                                     length_->GetHandle(1).GetCenter(),
+                                     length_->GetHandle(1).GetCenter());
           
-        that_.DeleteAnnotation(segment_);
-        segment_ = NULL;
+        that_.DeleteAnnotation(length_);
+        length_ = NULL;
 
         that_.BroadcastMessage(AnnotationChangedMessage(that_));
       }
@@ -2245,16 +2307,16 @@
 
     virtual bool IsAlive() const ORTHANC_OVERRIDE
     {
-      return (segment_ != NULL ||
+      return (length_ != NULL ||
               angle_ != NULL);
     }
 
     virtual void Cancel(const Scene2D& scene) ORTHANC_OVERRIDE
     {
-      if (segment_ != NULL)
+      if (length_ != NULL)
       {
-        that_.DeleteAnnotation(segment_);
-        segment_ = NULL;
+        that_.DeleteAnnotation(length_);
+        length_ = NULL;
       }
 
       if (angle_ != NULL)
@@ -2411,10 +2473,10 @@
   }
 
 
-  void AnnotationsSceneLayer::AddSegmentAnnotation(const ScenePoint2D& p1,
-                                                   const ScenePoint2D& p2)
+  void AnnotationsSceneLayer::AddLengthAnnotation(const ScenePoint2D& p1,
+                                                  const ScenePoint2D& p2)
   {
-    annotations_.insert(new SegmentAnnotation(*this, units_, true /* show label */, p1, p2));
+    annotations_.insert(new LengthAnnotation(*this, units_, true /* show label */, p1, p2));
   }
   
 
@@ -2578,9 +2640,9 @@
       {
         switch (activeTool_)
         {
-          case Tool_Segment:
+          case Tool_Length:
           {
-            Annotation* annotation = new SegmentAnnotation(*this, units_, true /* show label */, s, s);
+            Annotation* annotation = new LengthAnnotation(*this, units_, true /* show label */, s, s);
             return new CreateTwoHandlesTracker(*annotation, scene.GetCanvasToSceneTransform());
           }
 
@@ -2608,6 +2670,12 @@
             return new CreateTwoHandlesTracker(*annotation, scene.GetCanvasToSceneTransform());
           }
 
+          /*case Tool_TextAnnotation:
+          {
+            Annotation* annotation = new TextAnnotation(*this, units_, true show label, s, s);
+            return new CreateTwoHandlesTracker(*annotation, scene.GetCanvasToSceneTransform());
+          }*/
+
           default:
             return NULL;
         }
@@ -2697,9 +2765,9 @@
       {
         CircleAnnotation::Unserialize(*this, units_, annotations[i]);
       }
-      else if (type == VALUE_SEGMENT)
+      else if (type == VALUE_LENGTH)
       {
-        SegmentAnnotation::Unserialize(*this, units_, annotations[i]);
+        LengthAnnotation::Unserialize(*this, units_, annotations[i]);
       }
       else if (type == VALUE_PIXEL_PROBE)
       {
@@ -2713,6 +2781,10 @@
       {
         EllipseProbeAnnotation::Unserialize(*this, units_, annotations[i]);
       }
+      /*else if (type == VALUE_TEXT_ANNOTATION)
+      {
+        TextAnnotation::Unserialize(*this, units_, annotations[i]);
+        }*/
       else
       {
         LOG(ERROR) << "Cannot unserialize unknown type of annotation: " << type;
--- a/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.h	Tue Nov 01 19:42:27 2022 +0100
+++ b/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.h	Wed Nov 02 13:08:50 2022 +0100
@@ -38,7 +38,7 @@
     {
       Tool_Edit,
       Tool_None,
-      Tool_Segment,
+      Tool_Length,
       Tool_Angle,
       Tool_Circle,
       Tool_Remove,
@@ -61,10 +61,12 @@
     class ProbingAnnotation;
     class PixelProbeAnnotation;
     class SegmentAnnotation;
+    class LengthAnnotation;
     class AngleAnnotation;
     class CircleAnnotation;
     class RectangleProbeAnnotation;
     class EllipseProbeAnnotation;
+    class TextAnnotation;
     
     class EditPrimitiveTracker;
     class CreateTwoHandlesTracker;
@@ -120,8 +122,8 @@
       return units_;
     }
 
-    void AddSegmentAnnotation(const ScenePoint2D& p1,
-                              const ScenePoint2D& p2);
+    void AddLengthAnnotation(const ScenePoint2D& p1,
+                             const ScenePoint2D& p2);
 
     void AddCircleAnnotation(const ScenePoint2D& p1,
                              const ScenePoint2D& p2);