changeset 774:66ac7a2d1e3a

A few renames and cleanups + moved GUI constants to controller + start work on hit tests for measure tools and mouse hover.
author Benjamin Golinvaux <bgo@osimis.io>
date Fri, 24 May 2019 15:59:51 +0200
parents 07adcffba38c
children cf1102295ae5
files Framework/Scene2DViewport/AngleMeasureTool.cpp Framework/Scene2DViewport/AngleMeasureTool.h Framework/Scene2DViewport/CreateAngleMeasureCommand.cpp Framework/Scene2DViewport/CreateAngleMeasureCommand.h Framework/Scene2DViewport/CreateCircleMeasureCommand.cpp Framework/Scene2DViewport/CreateCircleMeasureCommand.h Framework/Scene2DViewport/CreateLineMeasureCommand.cpp Framework/Scene2DViewport/CreateLineMeasureCommand.h Framework/Scene2DViewport/EditAngleMeasureTracker.cpp Framework/Scene2DViewport/EditAngleMeasureTracker.h Framework/Scene2DViewport/EditCircleMeasureTracker.cpp Framework/Scene2DViewport/EditCircleMeasureTracker.h Framework/Scene2DViewport/EditLineMeasureTracker.cpp Framework/Scene2DViewport/EditLineMeasureTracker.h Framework/Scene2DViewport/LineMeasureTool.cpp Framework/Scene2DViewport/LineMeasureTool.h Framework/Scene2DViewport/MeasureCommands.h Framework/Scene2DViewport/MeasureTool.cpp Framework/Scene2DViewport/MeasureTool.h Framework/Scene2DViewport/MeasureTools.cpp Framework/Scene2DViewport/MeasureTools.h Framework/Scene2DViewport/MeasureToolsToolbox.cpp Framework/Scene2DViewport/MeasureTrackers.h Framework/Scene2DViewport/PointerTypes.h Framework/Scene2DViewport/ViewportController.cpp Framework/Scene2DViewport/ViewportController.h Resources/CMake/OrthancStoneConfiguration.cmake Samples/Sdl/TrackerSampleApp.h
diffstat 22 files changed, 415 insertions(+), 403 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Scene2DViewport/AngleMeasureTool.cpp	Thu May 23 10:25:48 2019 +0200
+++ b/Framework/Scene2DViewport/AngleMeasureTool.cpp	Fri May 24 15:59:51 2019 +0200
@@ -75,6 +75,12 @@
     RefreshScene();
   }
 
+
+  bool AngleMeasureTool::HitTest(ScenePoint2D p) const
+  {
+    throw std::logic_error("The method or operation is not implemented.");
+  }
+
   void AngleMeasureTool::SetCenter(ScenePoint2D pt)
   {
     center_ = pt;
@@ -85,12 +91,9 @@
   {
     if (IsSceneAlive())
     {
+      ViewportControllerPtr controller = GetController();
       if (IsEnabled())
       {
-        // get the scaling factor 
-        const double pixelToScene =
-          GetScene()->GetCanvasToSceneTransform().ComputeZoom();
-
         layerHolder_->CreateLayersIfNeeded();
 
         {
@@ -121,13 +124,15 @@
             {
               PolylineSceneLayer::Chain chain;
               //TODO: take DPI into account
-              AddSquare(chain, GetScene(), side1End_, 10.0 * pixelToScene);
+              AddSquare(chain, GetScene(), side1End_, 
+                GetController()->GetHandleSideLengthS());
               polylineLayer->AddChain(chain, true);
             }
             {
               PolylineSceneLayer::Chain chain;
               //TODO: take DPI into account
-              AddSquare(chain, GetScene(), side2End_, 10.0 * pixelToScene); 
+              AddSquare(chain, GetScene(), side2End_, 
+                GetController()->GetHandleSideLengthS());
               polylineLayer->AddChain(chain, true);
             }
           }
@@ -136,9 +141,8 @@
           {
             PolylineSceneLayer::Chain chain;
 
-            const double ARC_RADIUS_CANVAS_COORD = 30.0;
             AddShortestArc(chain, side1End_, center_, side2End_,
-                           ARC_RADIUS_CANVAS_COORD * pixelToScene);
+                           controller->GetAngleToolArcRadiusS());
             polylineLayer->AddChain(chain, false);
           }
         }
@@ -156,13 +160,11 @@
           double delta = NormalizeAngle(p2cAngle - p1cAngle);
           double theta = p1cAngle + delta / 2;
 
-          const double TEXT_CENTER_DISTANCE_CANVAS_COORD = 90;
+          double ox = controller->GetAngleTopTextLabelDistanceS() * cos(theta);
+          double oy = controller->GetAngleTopTextLabelDistanceS() * sin(theta);
 
-          double offsetX = TEXT_CENTER_DISTANCE_CANVAS_COORD * cos(theta);
-          double offsetY = TEXT_CENTER_DISTANCE_CANVAS_COORD * sin(theta);
-
-          double pointX = center_.GetX() + offsetX * pixelToScene;
-          double pointY = center_.GetY() + offsetY * pixelToScene;
+          double pointX = center_.GetX() + ox;
+          double pointY = center_.GetY() + oy;
 
           char buf[64];
           double angleDeg = RadiansToDegrees(delta);
@@ -207,11 +209,11 @@
             TrackerSample_SetInfoDisplayMessage("p2cAngle (deg)",
               boost::lexical_cast<std::string>(RadiansToDegrees(p2cAngle)));
 
-            TrackerSample_SetInfoDisplayMessage("offsetX (pix)",
-              boost::lexical_cast<std::string>(offsetX));
+            TrackerSample_SetInfoDisplayMessage("ox (scene)",
+              boost::lexical_cast<std::string>(ox));
 
-            TrackerSample_SetInfoDisplayMessage("offsetY (pix)",
-              boost::lexical_cast<std::string>(offsetY));
+            TrackerSample_SetInfoDisplayMessage("offsetY (scene)",
+              boost::lexical_cast<std::string>(oy));
 
             TrackerSample_SetInfoDisplayMessage("pointX",
               boost::lexical_cast<std::string>(pointX));
--- a/Framework/Scene2DViewport/AngleMeasureTool.h	Thu May 23 10:25:48 2019 +0200
+++ b/Framework/Scene2DViewport/AngleMeasureTool.h	Fri May 24 15:59:51 2019 +0200
@@ -20,7 +20,7 @@
 
 #pragma once
 
-#include "MeasureTools.h"
+#include "MeasureTool.h"
 
 #include "../Scene2DViewport/LayerHolder.h"
 #include "../Scene2D/Scene2D.h"
@@ -47,6 +47,9 @@
     void SetCenter(ScenePoint2D start);
     void SetSide2End(ScenePoint2D start);
 
+
+    virtual bool HitTest(ScenePoint2D p) const ORTHANC_OVERRIDE;
+
   private:
     virtual void        RefreshScene() ORTHANC_OVERRIDE;
     void                RemoveFromScene();
--- a/Framework/Scene2DViewport/EditAngleMeasureTracker.cpp	Thu May 23 10:25:48 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-namespace OrthancStone
-{
-}
--- a/Framework/Scene2DViewport/EditAngleMeasureTracker.h	Thu May 23 10:25:48 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-#pragma once
-
-namespace OrthancStone
-{
-}
--- a/Framework/Scene2DViewport/EditCircleMeasureTracker.cpp	Thu May 23 10:25:48 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-namespace OrthancStone
-{
-}
--- a/Framework/Scene2DViewport/EditCircleMeasureTracker.h	Thu May 23 10:25:48 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-#pragma once
-
-namespace OrthancStone
-{
-}
--- a/Framework/Scene2DViewport/EditLineMeasureTracker.cpp	Thu May 23 10:25:48 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-namespace OrthancStone
-{
-}
--- a/Framework/Scene2DViewport/EditLineMeasureTracker.h	Thu May 23 10:25:48 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-#pragma once
-
-namespace OrthancStone
-{
-}
--- a/Framework/Scene2DViewport/LineMeasureTool.cpp	Thu May 23 10:25:48 2019 +0200
+++ b/Framework/Scene2DViewport/LineMeasureTool.cpp	Fri May 24 15:59:51 2019 +0200
@@ -72,16 +72,34 @@
     RefreshScene();
   }
 
+  
+
+  bool LineMeasureTool::HitTest(ScenePoint2D p) const
+  {
+    const double pixelToScene =
+      GetScene()->GetCanvasToSceneTransform().ComputeZoom();
+
+    // the hit test will return true if the supplied point (in scene coords)
+    // is close to the handle or to the line.
+
+    // since the handle is small, a nice approximation is to defined this
+    // as a threshold on the distance between the point and the handle center.
+
+    // this threshold is defined as a constant value in CANVAS units.
+
+
+    // line equation from two points (non-normalized)
+    // (y0-y1)*x + (x1-x0)*xy + (x0*y1 - x1*y0) = 0
+    // 
+    return false;
+  }
+
   void LineMeasureTool::RefreshScene()
   {
     if (IsSceneAlive())
     {
       if (IsEnabled())
       {
-        // get the scaling factor 
-        const double pixelToScene =
-          GetScene()->GetCanvasToSceneTransform().ComputeZoom();
-
         layerHolder_->CreateLayersIfNeeded();
 
         {
@@ -89,7 +107,10 @@
 
           PolylineSceneLayer* polylineLayer = layerHolder_->GetPolylineLayer(0);
           polylineLayer->ClearAllChains();
-          polylineLayer->SetColor(0, 223, 21);
+          polylineLayer->SetColor(
+            TOOL_LINES_COLOR_RED, 
+            TOOL_LINES_COLOR_GREEN, 
+            TOOL_LINES_COLOR_BLUE);
 
           {
             PolylineSceneLayer::Chain chain;
@@ -100,13 +121,12 @@
 
           // handles
           {
-            //void AddSquare(PolylineSceneLayer::Chain& chain,const Scene2D& scene,const ScenePoint2D& centerS,const double& sideLength)
-
             {
               PolylineSceneLayer::Chain chain;
               
               //TODO: take DPI into account
-              AddSquare(chain, GetScene(), start_, 10.0 * pixelToScene);
+              AddSquare(chain, GetScene(), start_, 
+                GetController()->GetHandleSideLengthS());
               
               polylineLayer->AddChain(chain, true);
             }
@@ -115,7 +135,8 @@
               PolylineSceneLayer::Chain chain;
               
               //TODO: take DPI into account
-              AddSquare(chain, GetScene(), end_, 10.0 * pixelToScene);
+              AddSquare(chain, GetScene(), end_, 
+                GetController()->GetHandleSideLengthS());
               
               polylineLayer->AddChain(chain, true);
             }
@@ -123,8 +144,7 @@
 
         }
         {
-          // Set the text layer proporeties
-
+          // Set the text layer propreties
           double deltaX = end_.GetX() - start_.GetX();
           double deltaY = end_.GetY() - start_.GetY();
           double squareDist = deltaX * deltaX + deltaY * deltaY;
@@ -147,4 +167,4 @@
       }
     }
   }
-}
\ No newline at end of file
+}
--- a/Framework/Scene2DViewport/LineMeasureTool.h	Thu May 23 10:25:48 2019 +0200
+++ b/Framework/Scene2DViewport/LineMeasureTool.h	Fri May 24 15:59:51 2019 +0200
@@ -24,7 +24,7 @@
 #include "../Scene2D/Scene2D.h"
 #include "../Scene2D/ScenePoint2D.h"
 #include "../Scene2D/TextSceneLayer.h"
-#include "MeasureTools.h"
+#include "MeasureTool.h"
 
 #include <boost/shared_ptr.hpp>
 #include <boost/weak_ptr.hpp>
@@ -45,6 +45,9 @@
     void SetEnd(ScenePoint2D end);
     void Set(ScenePoint2D start, ScenePoint2D end);
 
+
+    virtual bool HitTest(ScenePoint2D p) const ORTHANC_OVERRIDE;
+
   private:
     virtual void        RefreshScene() ORTHANC_OVERRIDE;
     void                RemoveFromScene();
--- a/Framework/Scene2DViewport/MeasureCommands.h	Thu May 23 10:25:48 2019 +0200
+++ b/Framework/Scene2DViewport/MeasureCommands.h	Fri May 24 15:59:51 2019 +0200
@@ -23,7 +23,7 @@
 
 // to be moved into Stone
 #include "PointerTypes.h"
-#include "MeasureTools.h"
+#include "MeasureTool.h"
 #include "LineMeasureTool.h"
 #include "AngleMeasureTool.h"
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Scene2DViewport/MeasureTool.cpp	Fri May 24 15:59:51 2019 +0200
@@ -0,0 +1,115 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "MeasureTool.h"
+
+#include <Core/Logging.h>
+#include <Core/Enumerations.h>
+#include <Core/OrthancException.h>
+
+#include <boost/math/constants/constants.hpp>
+
+namespace OrthancStone
+{
+  MeasureTool::~MeasureTool()
+  {
+
+  }
+
+  void MeasureTool::Enable()
+  {
+    enabled_ = true;
+    RefreshScene();
+  }
+
+  void MeasureTool::Disable()
+  {
+    enabled_ = false;
+    RefreshScene();
+  }
+
+  bool MeasureTool::IsEnabled() const
+  {
+    return enabled_;
+  }
+
+
+  ViewportControllerConstPtr MeasureTool::GetController() const
+  {
+    ViewportControllerConstPtr controller = controllerW_.lock();
+    if (!controller)
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
+        "Using dead ViewportController object!");
+    return controller;
+  }
+
+  ViewportControllerPtr MeasureTool::GetController()
+  {
+#if 1
+    return boost::const_pointer_cast<ViewportController>
+      (const_cast<const MeasureTool*>(this)->GetController());
+    //return boost::const_<ViewportControllerPtr>
+    //  (const_cast<const MeasureTool*>(this)->GetController());
+#else
+    ViewportControllerPtr controller = controllerW_.lock();
+    if (!controller)
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, 
+        "Using dead ViewportController object!");
+    return controller;
+#endif
+  }
+
+  Scene2DPtr MeasureTool::GetScene()
+  {
+    return GetController()->GetScene();
+  }
+
+  Scene2DConstPtr MeasureTool::GetScene() const
+  {
+    return GetController()->GetScene();
+  }
+
+  MeasureTool::MeasureTool(MessageBroker& broker,
+    ViewportControllerWPtr controllerW)
+    : IObserver(broker)
+    , controllerW_(controllerW)
+    , enabled_(true)
+  {
+    GetController()->RegisterObserverCallback(
+      new Callable<MeasureTool, ViewportController::SceneTransformChanged>
+      (*this, &MeasureTool::OnSceneTransformChanged));
+  }
+
+
+  bool MeasureTool::IsSceneAlive() const
+  {
+    ViewportControllerPtr controller = controllerW_.lock();
+    return (controller.get() != NULL);
+  }
+
+  void MeasureTool::OnSceneTransformChanged(
+    const ViewportController::SceneTransformChanged& message)
+  {
+    RefreshScene();
+  }
+
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Scene2DViewport/MeasureTool.h	Fri May 24 15:59:51 2019 +0200
@@ -0,0 +1,110 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#pragma once
+
+#include "../Scene2D/PolylineSceneLayer.h"
+#include "../Scene2D/Scene2D.h"
+#include "../Scene2D/ScenePoint2D.h"
+#include "../Scene2D/TextSceneLayer.h"
+#include "../Scene2DViewport/PointerTypes.h"
+#include "../Scene2DViewport/ViewportController.h"
+
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+
+#include <vector>
+#include <cmath>
+
+namespace OrthancStone
+{
+  class MeasureTool : public IObserver
+  {
+  public:
+    virtual ~MeasureTool();
+
+    /**
+    Enabled tools are rendered in the scene.
+    */
+    void Enable();
+
+    /**
+    Disabled tools are not rendered in the scene. This is useful to be able
+    to use them as their own memento in command stacks (when a measure tool
+    creation command has been undone, the measure remains alive in the
+    command object but is disabled so that it can be redone later on easily)
+    */
+    void Disable();
+
+    /**
+    This method is called when the scene transform changes. It allows to 
+    recompute the visual elements whose content depend upon the scene transform
+    */
+    void OnSceneTransformChanged(
+      const ViewportController::SceneTransformChanged& message);
+    
+    /**
+    This function must be implemented by the measuring tool to return whether
+    a given point in scene coords is close to the measuring tool.
+
+    This is used for mouse hover highlighting.
+
+    It is assumed that if the pointer position leads to this function returning
+    true, then a click at that position will return a tracker to edit the 
+    measuring tool
+    */
+    virtual bool HitTest(ScenePoint2D p) const = 0;
+  protected:
+    MeasureTool(MessageBroker& broker, ViewportControllerWPtr controllerW);
+
+    /**
+    The measuring tool may exist in a standalone fashion, without any available
+    scene (because the controller is dead or dying). This call allows to check 
+    before accessing the scene.
+    */
+    bool IsSceneAlive() const;
+    
+    /**
+    This is the meat of the tool: this method must [create (if needed) and]
+    update the layers and their data according to the measure tool kind and
+    current state. This is repeatedly called during user interaction
+    */
+    virtual void RefreshScene() = 0;
+
+    ViewportControllerConstPtr GetController() const;
+    ViewportControllerPtr      GetController();
+
+    Scene2DConstPtr            GetScene() const;
+    Scene2DPtr                 GetScene();
+
+    /**
+    enabled_ is not accessible by subclasses because there is a state machine
+    that we do not wanna mess with
+    */
+    bool IsEnabled() const;
+
+  private:
+    ViewportControllerWPtr controllerW_;
+    bool     enabled_;
+  };
+}
+
+extern void TrackerSample_SetInfoDisplayMessage(
+  std::string key, std::string value);
--- a/Framework/Scene2DViewport/MeasureTools.cpp	Thu May 23 10:25:48 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,95 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-#include "MeasureTools.h"
-
-#include <Core/Logging.h>
-#include <Core/Enumerations.h>
-#include <Core/OrthancException.h>
-
-#include <boost/math/constants/constants.hpp>
-
-namespace OrthancStone
-{
-
-  MeasureTool::~MeasureTool()
-  {
-
-  }
-
-  void MeasureTool::Enable()
-  {
-    enabled_ = true;
-    RefreshScene();
-  }
-
-  void MeasureTool::Disable()
-  {
-    enabled_ = false;
-    RefreshScene();
-  }
-
-  bool MeasureTool::IsEnabled() const
-  {
-    return enabled_;
-  }
-
-
-  ViewportControllerPtr MeasureTool::GetController()
-  {
-    ViewportControllerPtr controller = controllerW_.lock();
-    if (!controller)
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, 
-        "Using dead ViewportController object!");
-    return controller;
-  }
-
-  OrthancStone::Scene2DPtr MeasureTool::GetScene()
-  {
-    return GetController()->GetScene();
-  }
-
-  MeasureTool::MeasureTool(MessageBroker& broker,
-    ViewportControllerWPtr controllerW)
-    : IObserver(broker)
-    , controllerW_(controllerW)
-    , enabled_(true)
-  {
-    GetController()->RegisterObserverCallback(
-      new Callable<MeasureTool, ViewportController::SceneTransformChanged>
-      (*this, &MeasureTool::OnSceneTransformChanged));
-  }
-
-
-  bool MeasureTool::IsSceneAlive() const
-  {
-    ViewportControllerPtr controller = controllerW_.lock();
-    return (controller.get() != NULL);
-  }
-
-  void MeasureTool::OnSceneTransformChanged(
-    const ViewportController::SceneTransformChanged& message)
-  {
-    RefreshScene();
-  }
-
-
-}
-
--- a/Framework/Scene2DViewport/MeasureTools.h	Thu May 23 10:25:48 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-#pragma once
-
-#include "../Scene2D/PolylineSceneLayer.h"
-#include "../Scene2D/Scene2D.h"
-#include "../Scene2D/ScenePoint2D.h"
-#include "../Scene2D/TextSceneLayer.h"
-#include "../Scene2DViewport/PointerTypes.h"
-#include "../Scene2DViewport/ViewportController.h"
-
-#include <boost/shared_ptr.hpp>
-#include <boost/weak_ptr.hpp>
-
-#include <vector>
-#include <cmath>
-
-namespace OrthancStone
-{
-  class MeasureTool : public IObserver
-  {
-  public:
-    virtual ~MeasureTool();
-
-    /**
-    Enabled tools are rendered in the scene.
-    */
-    void Enable();
-
-    /**
-    Disabled tools are not rendered in the scene. This is useful to be able
-    to use them as their own memento in command stacks (when a measure tool
-    creation command has been undone, the measure remains alive in the
-    command object but is disabled so that it can be redone later on easily)
-    */
-    void Disable();
-
-    /**
-    This method is called when the scene transform changes. It allows to 
-    recompute the visual elements whose content depend upon the scene transform
-    */
-    void OnSceneTransformChanged(
-      const ViewportController::SceneTransformChanged& message);
-
-  protected:
-    MeasureTool(MessageBroker& broker, ViewportControllerWPtr controllerW);
-
-    /**
-    The measuring tool may exist in a standalone fashion, without any available
-    scene (because the controller is dead or dying). This call allows to check 
-    before accessing the scene.
-    */
-    bool IsSceneAlive() const;
-    
-    /**
-    This is the meat of the tool: this method must [create (if needed) and]
-    update the layers and their data according to the measure tool kind and
-    current state. This is repeatedly called during user interaction
-    */
-    virtual void RefreshScene() = 0;
-
-    ViewportControllerPtr GetController();
-    Scene2DPtr GetScene();
-    
-    /**
-    enabled_ is not accessible by subclasses because there is a state machine
-    that we do not wanna mess with
-    */
-    bool IsEnabled() const;
-
-  private:
-    ViewportControllerWPtr controllerW_;
-    bool     enabled_;
-  };
-}
-
-extern void TrackerSample_SetInfoDisplayMessage(
-  std::string key, std::string value);
--- a/Framework/Scene2DViewport/MeasureToolsToolbox.cpp	Thu May 23 10:25:48 2019 +0200
+++ b/Framework/Scene2DViewport/MeasureToolsToolbox.cpp	Fri May 24 15:59:51 2019 +0200
@@ -21,6 +21,7 @@
 #include "MeasureToolsToolbox.h"
 #include "PointerTypes.h"
 #include "LayerHolder.h"
+#include "ViewportController.h"
 
 #include "../Scene2D/TextSceneLayer.h"
 #include "../Scene2D/Scene2D.h"
@@ -281,7 +282,6 @@
 }
 #endif
 
-
   /**
   This utility function assumes that the layer holder contains 5 text layers
   and will use the first four ones for the text background and the fifth one
@@ -304,24 +304,22 @@
       textLayer->SetText(text);
 
       if (i == 4)
-        textLayer->SetColor(0, 223, 81);
+      {
+        textLayer->SetColor(TEXT_COLOR_RED,
+                            TEXT_COLOR_GREEN,
+                            TEXT_COLOR_BLUE);
+      }
       else
-        textLayer->SetColor(0, 56, 21);
+      {
+        textLayer->SetColor(TEXT_OUTLINE_COLOR_RED,
+                            TEXT_OUTLINE_COLOR_GREEN,
+                            TEXT_OUTLINE_COLOR_BLUE);
+      }
 
       ScenePoint2D textAnchor;
-      //GetPositionOnBisectingLine(
-      //  textAnchor, side1End_, center_, side2End_, 40.0*pixelToScene);
       textLayer->SetPosition(
         p.GetX() + xoffsets[i] * pixelToScene,
         p.GetY() + yoffsets[i] * pixelToScene);
     }
   }
-
-
-
-
-
-
-
-
 }
--- a/Framework/Scene2DViewport/MeasureTrackers.h	Thu May 23 10:25:48 2019 +0200
+++ b/Framework/Scene2DViewport/MeasureTrackers.h	Fri May 24 15:59:51 2019 +0200
@@ -24,7 +24,7 @@
 #include "../Scene2D/Scene2D.h"
 #include "../Scene2D/PointerEvent.h"
 
-#include "MeasureTools.h"
+#include "MeasureTool.h"
 #include "MeasureCommands.h"
 
 #include <vector>
--- a/Framework/Scene2DViewport/PointerTypes.h	Thu May 23 10:25:48 2019 +0200
+++ b/Framework/Scene2DViewport/PointerTypes.h	Fri May 24 15:59:51 2019 +0200
@@ -75,6 +75,7 @@
 
   class ViewportController;
   typedef boost::shared_ptr<ViewportController> ViewportControllerPtr;
+  typedef boost::shared_ptr<const ViewportController> ViewportControllerConstPtr;
   typedef boost::weak_ptr<ViewportController> ViewportControllerWPtr;
 
   class LayerHolder;
--- a/Framework/Scene2DViewport/ViewportController.cpp	Thu May 23 10:25:48 2019 +0200
+++ b/Framework/Scene2DViewport/ViewportController.cpp	Fri May 24 15:59:51 2019 +0200
@@ -30,10 +30,16 @@
   ViewportController::ViewportController(MessageBroker& broker)
     : IObservable(broker)
     , numAppliedCommands_(0)
+    , canvasToSceneFactor_(0.0)
   {
     scene_ = boost::make_shared<Scene2D>();
   }
 
+  Scene2DConstPtr ViewportController::GetScene() const
+  {
+    return scene_;
+  }
+
   Scene2DPtr ViewportController::GetScene()
   {
     return scene_;
@@ -49,11 +55,11 @@
   {
     std::vector<MeasureToolPtr> ret;
     
-
-    //for (size_t i = 0; i < measureTools_.size(); ++i)
-    //{
-
-    //}
+    for (size_t i = 0; i < measureTools_.size(); ++i)
+    {
+      if (measureTools_[i]->HitTest(p))
+        ret.push_back(measureTools_[i]);
+    }
     return ret;
   }
 
@@ -72,6 +78,10 @@
   {
     scene_->SetSceneToCanvasTransform(transform);
     BroadcastMessage(SceneTransformChanged(*this));
+    
+    // update the canvas to scene factor
+    canvasToSceneFactor_ = 0.0;
+    canvasToSceneFactor_ = GetCanvasToSceneFactor();
   }
 
   void ViewportController::FitContent(
@@ -132,5 +142,36 @@
       std::remove(measureTools_.begin(), measureTools_.end(), measureTool), 
       measureTools_.end());
   }
+
+
+  double ViewportController::GetCanvasToSceneFactor() const
+  {
+    if (canvasToSceneFactor_ == 0)
+    {
+      canvasToSceneFactor_ =
+        GetScene()->GetCanvasToSceneTransform().ComputeZoom();
+    }
+    return canvasToSceneFactor_;
+  }
+
+  double ViewportController::GetHandleSideLengthS() const
+  {
+    return HANDLE_SIDE_LENGTH_CANVAS_COORD * GetCanvasToSceneFactor();
+  }
+
+  double ViewportController::GetAngleToolArcRadiusS() const
+  {
+    return ARC_RADIUS_CANVAS_COORD * GetCanvasToSceneFactor();
+  }
+
+  double ViewportController::GetHitTestMaximumDistanceS() const
+  {
+    return HIT_TEST_MAX_DISTANCE_CANVAS_COORD * GetCanvasToSceneFactor();
+  }
+
+  double ViewportController::GetAngleTopTextLabelDistanceS() const
+  {
+    return TEXT_CENTER_DISTANCE_CANVAS_COORD * GetCanvasToSceneFactor();
+  }
 }
 
--- a/Framework/Scene2DViewport/ViewportController.h	Thu May 23 10:25:48 2019 +0200
+++ b/Framework/Scene2DViewport/ViewportController.h	Fri May 24 15:59:51 2019 +0200
@@ -31,6 +31,29 @@
 namespace OrthancStone
 {
   /**
+    These constats are used 
+  
+  */
+  const double ARC_RADIUS_CANVAS_COORD = 30.0;
+  const double TEXT_CENTER_DISTANCE_CANVAS_COORD = 90;
+
+  const double HANDLE_SIDE_LENGTH_CANVAS_COORD = 10.0;
+  const double HIT_TEST_MAX_DISTANCE_CANVAS_COORD = 15.0;
+
+  const uint8_t TEXT_COLOR_RED = 0;
+  const uint8_t TEXT_COLOR_GREEN = 223;
+  const uint8_t TEXT_COLOR_BLUE = 81;
+
+  const uint8_t TOOL_LINES_COLOR_RED = 0;
+  const uint8_t TOOL_LINES_COLOR_GREEN = 223;
+  const uint8_t TOOL_LINES_COLOR_BLUE = 21;
+
+
+  const uint8_t TEXT_OUTLINE_COLOR_RED = 0;
+  const uint8_t TEXT_OUTLINE_COLOR_GREEN = 56;
+  const uint8_t TEXT_OUTLINE_COLOR_BLUE = 21;
+
+  /**
   This object is responsible for hosting a scene, responding to messages from
   the model and updating the scene accordingly.
 
@@ -50,7 +73,8 @@
 
     ViewportController(MessageBroker& broker);
 
-    Scene2DPtr GetScene();
+    Scene2DConstPtr GetScene() const;
+    Scene2DPtr      GetScene();
 
     /** 
     This method is called by the GUI system and should update/delete the
@@ -119,7 +143,35 @@
     /** Removes a measure tool or throws if it cannot be found */
     void RemoveMeasureTool(MeasureToolPtr measureTool);
 
+    /**
+    The square handle side length in *scene* coordinates
+    */
+    double GetHandleSideLengthS() const;
+
+    /**
+    The angle measure too arc  radius in *scene* coordinates
+
+    Note: you might wonder why this is not part of the AngleMeasureTool itself,
+    but we prefer to put all such constants in the same location, to ease 
+    */
+    double GetAngleToolArcRadiusS() const;
+
+    /**
+    The hit test maximum distance in *scene* coordinates.
+    If a pointer event is less than GetHandleSideLengthS() to a GUI element,
+    the hit test for this GUI element is seen as true
+    */
+    double GetHitTestMaximumDistanceS() const;
+
+    /**
+    Distance between the top of the angle measuring tool and the center of 
+    the label showing the actual measure, in *scene* coordinates
+    */
+    double GetAngleTopTextLabelDistanceS() const;
+
   private:
+    double GetCanvasToSceneFactor() const;
+
     std::vector<TrackerCommandPtr> commandStack_;
     
     /**
@@ -128,9 +180,12 @@
     - If numAppliedCommands_ > 0, one can undo
     - If numAppliedCommands_ < numAppliedCommands_.size(), one can redo
     */
-    size_t                         numAppliedCommands_;
-    std::vector<MeasureToolPtr>    measureTools_;
-    Scene2DPtr                     scene_;
-    FlexiblePointerTrackerPtr      tracker_;
+    size_t                      numAppliedCommands_;
+    std::vector<MeasureToolPtr> measureTools_;
+    Scene2DPtr                  scene_;
+    FlexiblePointerTrackerPtr   tracker_;
+    
+    // this is cached
+    mutable double              canvasToSceneFactor_;
   };
 }
--- a/Resources/CMake/OrthancStoneConfiguration.cmake	Thu May 23 10:25:48 2019 +0200
+++ b/Resources/CMake/OrthancStoneConfiguration.cmake	Fri May 24 15:59:51 2019 +0200
@@ -394,21 +394,21 @@
   ${ORTHANC_STONE_ROOT}/Framework/Scene2D/ZoomSceneTracker.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/AngleMeasureTool.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/AngleMeasureTool.h
+  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/CreateAngleMeasureCommand.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/CreateAngleMeasureCommand.h
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/CreateAngleMeasureTracker.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/CreateAngleMeasureTracker.h
+  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/CreateCircleMeasureCommand.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/CreateCircleMeasureCommand.h
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/CreateCircleMeasureTracker.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/CreateCircleMeasureTracker.h
+  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/CreateLineMeasureCommand.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/CreateLineMeasureCommand.h
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/CreateLineMeasureTracker.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/CreateLineMeasureTracker.h
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/CreateMeasureTracker.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/CreateMeasureTracker.h
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/CreateSimpleTrackerAdapter.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/EditAngleMeasureTracker.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/EditAngleMeasureTracker.h
-  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/EditCircleMeasureTracker.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/EditCircleMeasureTracker.h
-  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/EditLineMeasureTracker.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/EditLineMeasureTracker.h
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/IFlexiblePointerTracker.h
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/LayerHolder.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/LayerHolder.h
@@ -416,8 +416,8 @@
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/LineMeasureTool.h
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/MeasureCommands.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/MeasureCommands.h
-  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/MeasureTools.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/MeasureTools.h
+  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/MeasureTool.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/MeasureTool.h
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/MeasureToolsToolbox.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/MeasureToolsToolbox.h
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/MeasureTrackers.cpp
@@ -426,8 +426,7 @@
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/OneGesturePointerTracker.h
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/PointerTypes.h
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/ViewportController.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/ViewportController.h 
-
+  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/ViewportController.h
   ${ORTHANC_STONE_ROOT}/Framework/Fonts/FontRenderer.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Fonts/Glyph.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Fonts/GlyphAlphabet.cpp
--- a/Samples/Sdl/TrackerSampleApp.h	Thu May 23 10:25:48 2019 +0200
+++ b/Samples/Sdl/TrackerSampleApp.h	Fri May 24 15:59:51 2019 +0200
@@ -22,7 +22,7 @@
 #include "../../Framework/Messages/IObserver.h"
 #include "../../Framework/Scene2D/OpenGLCompositor.h"
 #include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h"
-#include "../../Framework/Scene2DViewport/MeasureTools.h"
+#include "../../Framework/Scene2DViewport/MeasureTool.h"
 #include "../../Framework/Scene2DViewport/PointerTypes.h"
 #include "../../Framework/Scene2DViewport/ViewportController.h"