diff OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.cpp @ 1821:36430d73e36c

introducing measure units in AnnotationsSceneLayer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 26 May 2021 14:02:12 +0200
parents fe402c678d18
children 781e9fc8925e
line wrap: on
line diff
--- a/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.cpp	Wed May 26 13:08:49 2021 +0200
+++ b/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.cpp	Wed May 26 14:02:12 2021 +0200
@@ -42,10 +42,13 @@
 static const char* const KEY_Y2 = "y2";
 static const char* const KEY_X3 = "x3";
 static const char* const KEY_Y3 = "y3";
+static const char* const KEY_UNITS = "units";
 
 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_MILLIMETERS = "millimeters";
+static const char* const VALUE_PIXELS = "pixels";
 
 #if 0
 static OrthancStone::Color COLOR_PRIMITIVES(192, 192, 192);
@@ -166,10 +169,13 @@
       
     AnnotationsSceneLayer&  that_;
     GeometricPrimitives     primitives_;
+    Units                   units_;
       
   public:
-    explicit Annotation(AnnotationsSceneLayer& that) :
-      that_(that)
+    explicit Annotation(AnnotationsSceneLayer& that,
+                        Units units) :
+      that_(that),
+      units_(units)
     {
       that.AddAnnotation(this);
     }
@@ -182,6 +188,11 @@
       }
     }
 
+    Units GetUnits() const
+    {
+      return units_;
+    }
+
     GeometricPrimitive* AddPrimitive(GeometricPrimitive* primitive)
     {
       if (primitive == NULL)
@@ -772,7 +783,21 @@
         double dx = x1 - x2;
         double dy = y1 - y2;
         char buf[32];
-        sprintf(buf, "%0.2f cm", sqrt(dx * dx + dy * dy) / 10.0);
+
+        switch (GetUnits())
+        {
+          case Units_Millimeters:
+            sprintf(buf, "%0.2f cm", sqrt(dx * dx + dy * dy) / 10.0);
+            break;
+
+          case Units_Pixels:
+            sprintf(buf, "%0.1f px", sqrt(dx * dx + dy * dy));
+            break;
+
+          default:
+            throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+        }
+            
         content.SetText(buf);
 
         label_.SetContent(content);
@@ -781,10 +806,11 @@
 
   public:
     SegmentAnnotation(AnnotationsSceneLayer& that,
+                      Units units,
                       bool showLabel,
                       const ScenePoint2D& p1,
                       const ScenePoint2D& p2) :
-      Annotation(that),
+      Annotation(that, units),
       showLabel_(showLabel),
       handle1_(AddTypedPrimitive<Handle>(new Handle(*this, p1))),
       handle2_(AddTypedPrimitive<Handle>(new Handle(*this, p2))),
@@ -832,6 +858,7 @@
     }
 
     static void Unserialize(AnnotationsSceneLayer& target,
+                            Units units,
                             const Json::Value& source)
     {
       if (source.isMember(KEY_X1) &&
@@ -843,7 +870,7 @@
           source[KEY_X2].isNumeric() &&
           source[KEY_Y2].isNumeric())
       {
-        new SegmentAnnotation(target, true,
+        new SegmentAnnotation(target, units, true,
                               ScenePoint2D(source[KEY_X1].asDouble(), source[KEY_Y1].asDouble()),
                               ScenePoint2D(source[KEY_X2].asDouble(), source[KEY_Y2].asDouble()));
       }
@@ -898,10 +925,11 @@
 
   public:
     AngleAnnotation(AnnotationsSceneLayer& that,
+                    Units units,
                     const ScenePoint2D& start,
                     const ScenePoint2D& middle,
                     const ScenePoint2D& end) :
-      Annotation(that),
+      Annotation(that, units),
       startHandle_(AddTypedPrimitive<Handle>(new Handle(*this, start))),
       middleHandle_(AddTypedPrimitive<Handle>(new Handle(*this, middle))),
       endHandle_(AddTypedPrimitive<Handle>(new Handle(*this, end))),
@@ -970,6 +998,7 @@
     }
 
     static void Unserialize(AnnotationsSceneLayer& target,
+                            Units units,
                             const Json::Value& source)
     {
       if (source.isMember(KEY_X1) &&
@@ -985,7 +1014,7 @@
           source[KEY_X3].isNumeric() &&
           source[KEY_Y3].isNumeric())
       {
-        new AngleAnnotation(target,
+        new AngleAnnotation(target, units,
                             ScenePoint2D(source[KEY_X1].asDouble(), source[KEY_Y1].asDouble()),
                             ScenePoint2D(source[KEY_X2].asDouble(), source[KEY_Y2].asDouble()),
                             ScenePoint2D(source[KEY_X3].asDouble(), source[KEY_Y3].asDouble()));
@@ -1036,10 +1065,25 @@
       double area = PI * diameter * diameter / 4.0;
         
       char buf[32];
-      sprintf(buf, "%0.2f cm\n%0.2f cm%c%c",
-              diameter / 10.0,
-              area / 100.0,
-              0xc2, 0xb2 /* two bytes corresponding to two power in UTF-8 */);
+
+      switch (GetUnits())
+      {
+        case Units_Millimeters:
+          sprintf(buf, "%0.2f cm\n%0.2f cm%c%c",
+                  diameter / 10.0,
+                  area / 100.0,
+                  0xc2, 0xb2 /* two bytes corresponding to two power in UTF-8 */);
+          break;
+
+        case Units_Pixels:
+          // Don't report area (pixel-times-pixel is a strange unit)
+          sprintf(buf, "%0.1f px", diameter);
+          break;
+          
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+      }
+
       content.SetText(buf);
 
       label_.SetContent(content);
@@ -1047,9 +1091,10 @@
 
   public:
     CircleAnnotation(AnnotationsSceneLayer& that,
+                     Units units,
                      const ScenePoint2D& p1,
                      const ScenePoint2D& p2) :
-      Annotation(that),
+      Annotation(that, units),
       handle1_(AddTypedPrimitive<Handle>(new Handle(*this, p1))),
       handle2_(AddTypedPrimitive<Handle>(new Handle(*this, p2))),
       segment_(AddTypedPrimitive<Segment>(new Segment(*this, p1, p2))),
@@ -1094,6 +1139,7 @@
     }
 
     static void Unserialize(AnnotationsSceneLayer& target,
+                            Units units,
                             const Json::Value& source)
     {
       if (source.isMember(KEY_X1) &&
@@ -1105,7 +1151,7 @@
           source[KEY_X2].isNumeric() &&
           source[KEY_Y2].isNumeric())
       {
-        new CircleAnnotation(target,
+        new CircleAnnotation(target, units,
                              ScenePoint2D(source[KEY_X1].asDouble(), source[KEY_Y1].asDouble()),
                              ScenePoint2D(source[KEY_X2].asDouble(), source[KEY_Y2].asDouble()));
       }
@@ -1127,6 +1173,7 @@
       
   public:
     CreateSegmentOrCircleTracker(AnnotationsSceneLayer& that,
+                                 Units units,
                                  bool isCircle,
                                  const ScenePoint2D& sceneClick,
                                  const AffineTransform2D& canvasToScene) :
@@ -1137,12 +1184,12 @@
     {
       if (isCircle)
       {
-        annotation_ = new CircleAnnotation(that, sceneClick, sceneClick);
+        annotation_ = new CircleAnnotation(that, units, sceneClick, sceneClick);
         handle2_ = &dynamic_cast<CircleAnnotation*>(annotation_)->GetHandle2();
       }
       else
       {
-        annotation_ = new SegmentAnnotation(that, true /* show label */, sceneClick, sceneClick);
+        annotation_ = new SegmentAnnotation(that, units, true /* show label */, sceneClick, sceneClick);
         handle2_ = &dynamic_cast<SegmentAnnotation*>(annotation_)->GetHandle2();
       }
         
@@ -1199,6 +1246,7 @@
       
   public:
     CreateAngleTracker(AnnotationsSceneLayer& that,
+                       Units units,
                        const ScenePoint2D& sceneClick,
                        const AffineTransform2D& canvasToScene) :
       that_(that),
@@ -1206,7 +1254,7 @@
       angle_(NULL),
       canvasToScene_(canvasToScene)
     {
-      segment_ = new SegmentAnnotation(that, false /* no length label */, sceneClick, sceneClick);
+      segment_ = new SegmentAnnotation(that, units, false /* no length label */, sceneClick, sceneClick);
     }
 
     virtual void PointerMove(const PointerEvent& event) ORTHANC_OVERRIDE
@@ -1232,7 +1280,7 @@
       {
         // End of first step: The first segment is available, now create the angle
 
-        angle_ = new AngleAnnotation(that_, segment_->GetHandle1().GetCenter(),
+        angle_ = new AngleAnnotation(that_, segment_->GetUnits(), segment_->GetHandle1().GetCenter(),
                                      segment_->GetHandle2().GetCenter(),
                                      segment_->GetHandle2().GetCenter());
           
@@ -1348,7 +1396,8 @@
   AnnotationsSceneLayer::AnnotationsSceneLayer(size_t macroLayerIndex) :
     activeTool_(Tool_Edit),
     macroLayerIndex_(macroLayerIndex),
-    polylineSubLayer_(0)  // dummy initialization
+    polylineSubLayer_(0),  // dummy initialization
+    units_(Units_Pixels)
   {
   }
     
@@ -1366,18 +1415,28 @@
     ClearHover();
   }
 
+  
+  void AnnotationsSceneLayer::SetUnits(Units units)
+  {
+    if (units_ != units)
+    {
+      Clear();
+      units_ = units;
+    }
+  }
+
 
   void AnnotationsSceneLayer::AddSegmentAnnotation(const ScenePoint2D& p1,
                                                    const ScenePoint2D& p2)
   {
-    annotations_.insert(new SegmentAnnotation(*this, true /* show label */, p1, p2));
+    annotations_.insert(new SegmentAnnotation(*this, units_, true /* show label */, p1, p2));
   }
   
 
   void AnnotationsSceneLayer::AddCircleAnnotation(const ScenePoint2D& p1,
                                                   const ScenePoint2D& p2)
   {
-    annotations_.insert(new CircleAnnotation(*this, p1, p2));
+    annotations_.insert(new CircleAnnotation(*this, units_, p1, p2));
   }
   
 
@@ -1385,7 +1444,7 @@
                                                  const ScenePoint2D& p2,
                                                  const ScenePoint2D& p3)
   {
-    annotations_.insert(new AngleAnnotation(*this, p1, p2, p3));
+    annotations_.insert(new AngleAnnotation(*this, units_, p1, p2, p3));
   }
   
 
@@ -1524,13 +1583,13 @@
         switch (activeTool_)
         {
           case Tool_Segment:
-            return new CreateSegmentOrCircleTracker(*this, false /* segment */, s, scene.GetCanvasToSceneTransform());
+            return new CreateSegmentOrCircleTracker(*this, units_, false /* segment */, s, scene.GetCanvasToSceneTransform());
 
           case Tool_Circle:
-            return new CreateSegmentOrCircleTracker(*this, true /* circle */, s, scene.GetCanvasToSceneTransform());
+            return new CreateSegmentOrCircleTracker(*this, units_, true /* circle */, s, scene.GetCanvasToSceneTransform());
 
           case Tool_Angle:
-            return new CreateAngleTracker(*this, s, scene.GetCanvasToSceneTransform());
+            return new CreateAngleTracker(*this, units_, s, scene.GetCanvasToSceneTransform());
 
           default:
             return NULL;
@@ -1555,6 +1614,20 @@
 
     target = Json::objectValue;
     target[KEY_ANNOTATIONS] = annotations;
+
+    switch (units_)
+    {
+      case Units_Millimeters:
+        target[KEY_UNITS] = VALUE_MILLIMETERS;
+        break;
+
+      case Units_Pixels:
+        target[KEY_UNITS] = VALUE_PIXELS;
+        break;
+
+      default:
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
   }
 
 
@@ -1564,11 +1637,28 @@
       
     if (serialized.type() != Json::objectValue ||
         !serialized.isMember(KEY_ANNOTATIONS) ||
-        serialized[KEY_ANNOTATIONS].type() != Json::arrayValue)
+        !serialized.isMember(KEY_UNITS) ||
+        serialized[KEY_ANNOTATIONS].type() != Json::arrayValue ||
+        serialized[KEY_UNITS].type() != Json::stringValue)
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Cannot unserialize a set of annotations");
     }
 
+    const std::string& u = serialized[KEY_UNITS].asString();
+
+    if (u == VALUE_MILLIMETERS)
+    {
+      units_ = Units_Millimeters;
+    }
+    else if (u == VALUE_PIXELS)
+    {
+      units_ = Units_Pixels;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Unknown units: " + u);
+    }
+
     const Json::Value& annotations = serialized[KEY_ANNOTATIONS];
 
     for (Json::Value::ArrayIndex i = 0; i < annotations.size(); i++)
@@ -1584,15 +1674,15 @@
 
       if (type == VALUE_ANGLE)
       {
-        AngleAnnotation::Unserialize(*this, annotations[i]);
+        AngleAnnotation::Unserialize(*this, units_, annotations[i]);
       }
       else if (type == VALUE_CIRCLE)
       {
-        CircleAnnotation::Unserialize(*this, annotations[i]);
+        CircleAnnotation::Unserialize(*this, units_, annotations[i]);
       }
       else if (type == VALUE_SEGMENT)
       {
-        SegmentAnnotation::Unserialize(*this, annotations[i]);
+        SegmentAnnotation::Unserialize(*this, units_, annotations[i]);
       }
       else
       {