changeset 866:c71ef52602a0 toa2019062501

Added the ability to edit existing measuring tools (demo not updated yet)
author Benjamin Golinvaux <bgo@osimis.io>
date Tue, 25 Jun 2019 17:54:46 +0200
parents a29c13497557
children fe96057e97b9
files Framework/Scene2D/ScenePoint2D.h Framework/Scene2DViewport/AngleMeasureTool.cpp Framework/Scene2DViewport/AngleMeasureTool.h Framework/Scene2DViewport/EditAngleMeasureCommand.cpp Framework/Scene2DViewport/EditAngleMeasureCommand.h Framework/Scene2DViewport/EditAngleMeasureTracker.cpp Framework/Scene2DViewport/EditAngleMeasureTracker.h Framework/Scene2DViewport/EditLineMeasureCommand.cpp Framework/Scene2DViewport/EditLineMeasureCommand.h Framework/Scene2DViewport/EditLineMeasureTracker.cpp Framework/Scene2DViewport/EditLineMeasureTracker.h Framework/Scene2DViewport/LineMeasureTool.cpp Framework/Scene2DViewport/LineMeasureTool.h Framework/Scene2DViewport/MeasureCommands.cpp Framework/Scene2DViewport/MeasureCommands.h Framework/Scene2DViewport/MeasureTool.h Framework/Scene2DViewport/MeasureTrackers.cpp Framework/Scene2DViewport/MeasureTrackers.h Resources/CMake/OrthancStoneConfiguration.cmake
diffstat 15 files changed, 686 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Scene2D/ScenePoint2D.h	Tue Jun 25 15:24:13 2019 +0200
+++ b/Framework/Scene2D/ScenePoint2D.h	Tue Jun 25 17:54:46 2019 +0200
@@ -73,6 +73,15 @@
       return v;
     }
 
+    const ScenePoint2D operator+(const ScenePoint2D& a) const
+    {
+      ScenePoint2D v;
+      v.x_ = x_ + a.x_;
+      v.y_ = y_ + a.y_;
+
+      return v;
+    }
+
     const ScenePoint2D operator*(double a) const
     {
       ScenePoint2D v;
--- a/Framework/Scene2DViewport/AngleMeasureTool.cpp	Tue Jun 25 15:24:13 2019 +0200
+++ b/Framework/Scene2DViewport/AngleMeasureTool.cpp	Tue Jun 25 17:54:46 2019 +0200
@@ -20,6 +20,7 @@
 
 #include "AngleMeasureTool.h"
 #include "MeasureToolsToolbox.h"
+#include "EditAngleMeasureTracker.h"
 #include "LayerHolder.h"
 
 #include <Core/Logging.h>
@@ -90,6 +91,26 @@
     SetAngleHighlightArea(AngleHighlightArea_None);
   }
 
+
+  boost::shared_ptr<OrthancStone::MeasureToolMemento> AngleMeasureTool::GetMemento() const
+  {
+    boost::shared_ptr<AngleMeasureToolMemento> memento(new AngleMeasureToolMemento());
+    memento->center_ = center_;
+    memento->side1End_ = side1End_;
+    memento->side2End_ = side2End_;
+    return memento;
+  }
+  
+  void AngleMeasureTool::SetMemento(boost::shared_ptr<MeasureToolMemento> mementoBase)
+  {
+    boost::shared_ptr<AngleMeasureToolMemento> memento = boost::dynamic_pointer_cast<AngleMeasureToolMemento>(mementoBase);
+    ORTHANC_ASSERT(memento.get() != NULL, "Internal error: wrong (or bad) memento");
+    center_   = memento->center_;
+    side1End_ = memento->side1End_;
+    side2End_ = memento->side2End_;
+    RefreshScene();
+  }
+
   void AngleMeasureTool::Highlight(ScenePoint2D p)
   {
     AngleHighlightArea angleHighlightArea = AngleHitTest(p);
@@ -140,6 +161,27 @@
     return AngleHitTest(p) != AngleHighlightArea_None;
   }
 
+
+  boost::shared_ptr<IFlexiblePointerTracker> AngleMeasureTool::CreateEditionTracker(const PointerEvent& e)
+  {
+    ScenePoint2D scenePos = e.GetMainPosition().Apply(
+      GetScene()->GetCanvasToSceneTransform());
+
+    if (!HitTest(scenePos))
+      return boost::shared_ptr<IFlexiblePointerTracker>();
+
+    /**
+      new EditLineMeasureTracker(
+        boost::shared_ptr<LineMeasureTool> measureTool;
+        MessageBroker & broker,
+        boost::weak_ptr<ViewportController>          controllerW,
+        const PointerEvent & e);
+    */
+    boost::shared_ptr<EditAngleMeasureTracker> editAngleMeasureTracker(
+      new EditAngleMeasureTracker(shared_from_this(), GetBroker(), GetController(), e));
+    return editAngleMeasureTracker;
+  }
+
   void AngleMeasureTool::SetCenter(ScenePoint2D pt)
   {
     center_ = pt;
--- a/Framework/Scene2DViewport/AngleMeasureTool.h	Tue Jun 25 15:24:13 2019 +0200
+++ b/Framework/Scene2DViewport/AngleMeasureTool.h	Tue Jun 25 17:54:46 2019 +0200
@@ -30,13 +30,14 @@
 
 #include <boost/shared_ptr.hpp>
 #include <boost/weak_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
 
 #include <vector>
 #include <cmath>
 
 namespace OrthancStone
 {
-  class AngleMeasureTool : public MeasureTool
+  class AngleMeasureTool : public MeasureTool, public boost::enable_shared_from_this<AngleMeasureTool>
   {
   public:
     AngleMeasureTool(MessageBroker& broker, boost::weak_ptr<ViewportController> controllerW);
@@ -50,6 +51,9 @@
     virtual bool HitTest(ScenePoint2D p) const ORTHANC_OVERRIDE;
     virtual void Highlight(ScenePoint2D p) ORTHANC_OVERRIDE;
     virtual void ResetHighlightState() ORTHANC_OVERRIDE;
+    virtual boost::shared_ptr<IFlexiblePointerTracker> CreateEditionTracker(const PointerEvent& e) ORTHANC_OVERRIDE;
+    virtual boost::shared_ptr<MeasureToolMemento> GetMemento() const ORTHANC_OVERRIDE;
+    virtual void SetMemento(boost::shared_ptr<MeasureToolMemento>) ORTHANC_OVERRIDE;
 
     enum AngleHighlightArea
     {
@@ -76,6 +80,14 @@
     boost::shared_ptr<LayerHolder>  layerHolder_;
     AngleHighlightArea              angleHighlightArea_;
   };
+
+  class AngleMeasureToolMemento : public MeasureToolMemento
+  {
+  public:
+    ScenePoint2D                    side1End_;
+    ScenePoint2D                    side2End_;
+    ScenePoint2D                    center_;
+  };
 }
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Scene2DViewport/EditAngleMeasureTracker.cpp	Tue Jun 25 17:54:46 2019 +0200
@@ -0,0 +1,112 @@
+/**
+ * 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 "EditAngleMeasureTracker.h"
+
+namespace OrthancStone
+{
+  EditAngleMeasureTracker::EditAngleMeasureTracker(
+    boost::shared_ptr<AngleMeasureTool>  measureTool,
+    MessageBroker& broker,
+    boost::weak_ptr<ViewportController> controllerW,
+    const PointerEvent& e)
+    : EditMeasureTracker(controllerW, e)
+  {
+    ScenePoint2D scenePos = e.GetMainPosition().Apply(
+      GetScene()->GetCanvasToSceneTransform());
+
+    modifiedZone_ = measureTool->AngleHitTest(scenePos);
+
+    command_.reset(new EditAngleMeasureCommand(measureTool, broker, controllerW));
+  }
+
+  EditAngleMeasureTracker::~EditAngleMeasureTracker()
+  {
+
+  }
+
+  void EditAngleMeasureTracker::PointerMove(const PointerEvent& e)
+  {
+    ScenePoint2D scenePos = e.GetMainPosition().Apply(
+      GetScene()->GetCanvasToSceneTransform());
+
+    ScenePoint2D delta = scenePos - GetOriginalClickPosition();
+
+    boost::shared_ptr<AngleMeasureToolMemento> memento =
+      boost::dynamic_pointer_cast<AngleMeasureToolMemento>(command_->mementoOriginal_);
+
+    ORTHANC_ASSERT(memento.get() != NULL);
+
+    switch (modifiedZone_)
+    {
+    case AngleMeasureTool::AngleHighlightArea_Center:
+    {
+      ScenePoint2D newCenter = memento->center_ + delta;
+      GetCommand()->SetCenter(newCenter);
+    }
+    break;
+    case AngleMeasureTool::AngleHighlightArea_Side1:
+    case AngleMeasureTool::AngleHighlightArea_Side2:
+    {
+      ScenePoint2D newCenter = memento->center_ + delta;
+      ScenePoint2D newSide1End = memento->side1End_ + delta;
+      ScenePoint2D newSide2End = memento->side2End_ + delta;
+      GetCommand()->SetCenter(newCenter);
+      GetCommand()->SetSide1End(newSide1End);
+      GetCommand()->SetSide2End(newSide2End);
+    }
+    break;
+    case AngleMeasureTool::AngleHighlightArea_Side1End:
+    {
+      ScenePoint2D newSide1End = memento->side1End_ + delta;
+      GetCommand()->SetSide1End(newSide1End);
+    }
+    break;
+    case AngleMeasureTool::AngleHighlightArea_Side2End:
+    {
+      ScenePoint2D newSide2End = memento->side2End_ + delta;
+      GetCommand()->SetSide2End(newSide2End);
+    }
+    break;
+    default:
+      LOG(WARN) << "Warning: please retry the measuring tool editing operation!";
+      break;
+    }
+  }
+
+  void EditAngleMeasureTracker::PointerUp(const PointerEvent& e)
+  {
+    alive_ = false;
+  }
+
+  void EditAngleMeasureTracker::PointerDown(const PointerEvent& e)
+  {
+    LOG(WARNING) << "Additional touches (fingers, pen, mouse buttons...) "
+      "are ignored when the edit angle tracker is active";
+  }
+
+  boost::shared_ptr<EditAngleMeasureCommand> EditAngleMeasureTracker::GetCommand()
+  {
+    boost::shared_ptr<EditAngleMeasureCommand> ret = boost::dynamic_pointer_cast<EditAngleMeasureCommand>(command_);
+    ORTHANC_ASSERT(ret.get() != NULL, "Internal error in EditAngleMeasureTracker::GetCommand()");
+    return ret;
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Scene2DViewport/EditAngleMeasureTracker.h	Tue Jun 25 17:54:46 2019 +0200
@@ -0,0 +1,54 @@
+/**
+ * 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 "MeasureTrackers.h"
+
+namespace OrthancStone
+{
+  class EditAngleMeasureTracker : public EditMeasureTracker
+  {
+  public:
+    /**
+    When you create this tracker, you need to supply it with the undo stack
+    where it will store the commands that perform the actual measure tool
+    creation and modification.
+    In turn, a container for these commands to store the actual measuring
+    must be supplied, too
+    */
+    EditAngleMeasureTracker(
+      boost::shared_ptr<AngleMeasureTool>  measureTool,
+      MessageBroker& broker,
+      boost::weak_ptr<ViewportController> controllerW,
+      const PointerEvent& e);
+
+    ~EditAngleMeasureTracker();
+
+    virtual void PointerMove(const PointerEvent& e) ORTHANC_OVERRIDE;
+    virtual void PointerUp(const PointerEvent& e) ORTHANC_OVERRIDE;
+    virtual void PointerDown(const PointerEvent& e) ORTHANC_OVERRIDE;
+
+  private:
+    AngleMeasureTool::AngleHighlightArea modifiedZone_;
+
+    boost::shared_ptr<EditAngleMeasureCommand> GetCommand();
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Scene2DViewport/EditLineMeasureTracker.cpp	Tue Jun 25 17:54:46 2019 +0200
@@ -0,0 +1,107 @@
+/**
+ * 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 "EditLineMeasureTracker.h"
+
+namespace OrthancStone
+{
+  EditLineMeasureTracker::EditLineMeasureTracker(
+    boost::shared_ptr<LineMeasureTool>  measureTool,
+    MessageBroker& broker,
+    boost::weak_ptr<ViewportController> controllerW,
+    const PointerEvent& e) 
+    : EditMeasureTracker(controllerW, e)
+  {
+    ScenePoint2D scenePos = e.GetMainPosition().Apply(
+      GetScene()->GetCanvasToSceneTransform());
+
+    modifiedZone_ = measureTool->LineHitTest(scenePos);
+
+    command_.reset(
+      new EditLineMeasureCommand(
+        measureTool,
+        broker,
+        controllerW));
+  }
+
+  EditLineMeasureTracker::~EditLineMeasureTracker()
+  {
+
+  }
+
+  void EditLineMeasureTracker::PointerMove(const PointerEvent& e)
+  {
+    ScenePoint2D scenePos = e.GetMainPosition().Apply(
+      GetScene()->GetCanvasToSceneTransform());
+
+    ScenePoint2D delta = scenePos - GetOriginalClickPosition();
+
+    boost::shared_ptr<LineMeasureToolMemento> memento =
+      boost::dynamic_pointer_cast<LineMeasureToolMemento>(command_->mementoOriginal_);
+
+    ORTHANC_ASSERT(memento.get() != NULL);
+
+    switch (modifiedZone_)
+    {
+    case LineMeasureTool::LineHighlightArea_Start:
+    {
+      ScenePoint2D newStart = memento->start_ + delta;
+      GetCommand()->SetStart(newStart);
+    }
+    break;
+    case LineMeasureTool::LineHighlightArea_End:
+    {
+      ScenePoint2D newEnd = memento->end_ + delta;
+      GetCommand()->SetEnd(newEnd);
+    }
+    break;
+    case LineMeasureTool::LineHighlightArea_Segment:
+    {
+      ScenePoint2D newStart = memento->start_ + delta;
+      ScenePoint2D newEnd = memento->end_ + delta;
+      GetCommand()->SetStart(newStart);
+      GetCommand()->SetEnd(newEnd);
+    }
+    break;
+    default:
+      LOG(WARN) << "Warning: please retry the measuring tool editing operation!";
+        break;
+    }
+  }
+
+  void EditLineMeasureTracker::PointerUp(const PointerEvent& e)
+  {
+    alive_ = false;
+  }
+
+  void EditLineMeasureTracker::PointerDown(const PointerEvent& e)
+  {
+    LOG(WARNING) << "Additional touches (fingers, pen, mouse buttons...) "
+      "are ignored when the edit line tracker is active";
+  }
+
+  boost::shared_ptr<EditLineMeasureCommand> EditLineMeasureTracker::GetCommand()
+  {
+    boost::shared_ptr<EditLineMeasureCommand> ret = boost::dynamic_pointer_cast<EditLineMeasureCommand>(command_);
+    ORTHANC_ASSERT(ret.get() != NULL, "Internal error in EditLineMeasureTracker::GetCommand()");
+    return ret;
+  }
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Scene2DViewport/EditLineMeasureTracker.h	Tue Jun 25 17:54:46 2019 +0200
@@ -0,0 +1,54 @@
+/**
+ * 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 "MeasureTrackers.h"
+
+namespace OrthancStone
+{
+  class EditLineMeasureTracker : public EditMeasureTracker
+  {
+  public:
+    /**
+    When you create this tracker, you need to supply it with the undo stack
+    where it will store the commands that perform the actual measure tool
+    creation and modification.
+    In turn, a container for these commands to store the actual measuring
+    must be supplied, too
+    */
+    EditLineMeasureTracker(
+      boost::shared_ptr<LineMeasureTool>  measureTool,
+      MessageBroker&                      broker,
+      boost::weak_ptr<ViewportController> controllerW,
+      const PointerEvent&                 e);
+
+    ~EditLineMeasureTracker();
+
+    virtual void PointerMove(const PointerEvent& e) ORTHANC_OVERRIDE;
+    virtual void PointerUp(const PointerEvent& e) ORTHANC_OVERRIDE;
+    virtual void PointerDown(const PointerEvent& e) ORTHANC_OVERRIDE;
+
+  private:
+    LineMeasureTool::LineHighlightArea modifiedZone_;
+
+    boost::shared_ptr<EditLineMeasureCommand> GetCommand();
+  };
+}
--- a/Framework/Scene2DViewport/LineMeasureTool.cpp	Tue Jun 25 15:24:13 2019 +0200
+++ b/Framework/Scene2DViewport/LineMeasureTool.cpp	Tue Jun 25 17:54:46 2019 +0200
@@ -20,6 +20,7 @@
 
 #include "LineMeasureTool.h"
 #include "MeasureToolsToolbox.h"
+#include "EditLineMeasureTracker.h"
 #include "LayerHolder.h"
 
 #include <Core/Logging.h>
@@ -119,6 +120,44 @@
     return LineHitTest(p) != LineHighlightArea_None;
   }
 
+  boost::shared_ptr<IFlexiblePointerTracker> LineMeasureTool::CreateEditionTracker(const PointerEvent& e)
+  {
+    ScenePoint2D scenePos = e.GetMainPosition().Apply(
+      GetScene()->GetCanvasToSceneTransform());
+
+    if (!HitTest(scenePos))
+      return boost::shared_ptr<IFlexiblePointerTracker>();
+
+    /**
+      new EditLineMeasureTracker(
+        boost::shared_ptr<LineMeasureTool> measureTool;
+        MessageBroker & broker,
+        boost::weak_ptr<ViewportController>          controllerW,
+        const PointerEvent & e);
+    */
+    boost::shared_ptr<EditLineMeasureTracker> editLineMeasureTracker(
+      new EditLineMeasureTracker(shared_from_this(), GetBroker(), GetController(), e));
+    return editLineMeasureTracker;
+  }
+
+
+  boost::shared_ptr<MeasureToolMemento> LineMeasureTool::GetMemento() const
+  {
+    boost::shared_ptr<LineMeasureToolMemento> memento(new LineMeasureToolMemento());
+    memento->start_ = start_;
+    memento->end_ = end_;
+    return memento;
+  }
+
+  void LineMeasureTool::SetMemento(boost::shared_ptr<MeasureToolMemento> mementoBase)
+  {
+    boost::shared_ptr<LineMeasureToolMemento> memento = boost::dynamic_pointer_cast<LineMeasureToolMemento>(mementoBase);
+    ORTHANC_ASSERT(memento.get() != NULL, "Internal error: wrong (or bad) memento");
+    start_ = memento->start_;
+    end_ = memento->end_;
+    RefreshScene();
+  }
+
   void LineMeasureTool::RefreshScene()
   {
     if (IsSceneAlive())
--- a/Framework/Scene2DViewport/LineMeasureTool.h	Tue Jun 25 15:24:13 2019 +0200
+++ b/Framework/Scene2DViewport/LineMeasureTool.h	Tue Jun 25 17:54:46 2019 +0200
@@ -28,13 +28,14 @@
 
 #include <boost/shared_ptr.hpp>
 #include <boost/weak_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
 
 #include <vector>
 #include <cmath>
 
 namespace OrthancStone
 {
-  class LineMeasureTool : public MeasureTool
+  class LineMeasureTool : public MeasureTool, public boost::enable_shared_from_this<LineMeasureTool>
   {
   public:
     LineMeasureTool(MessageBroker& broker, boost::weak_ptr<ViewportController> controllerW);
@@ -49,6 +50,9 @@
     virtual bool HitTest(ScenePoint2D p) const ORTHANC_OVERRIDE;
     virtual void Highlight(ScenePoint2D p) ORTHANC_OVERRIDE;
     virtual void ResetHighlightState() ORTHANC_OVERRIDE;
+    virtual boost::shared_ptr<IFlexiblePointerTracker> CreateEditionTracker(const PointerEvent& e) ORTHANC_OVERRIDE;
+    virtual boost::shared_ptr<MeasureToolMemento> GetMemento() const ORTHANC_OVERRIDE;
+    virtual void SetMemento(boost::shared_ptr<MeasureToolMemento>) ORTHANC_OVERRIDE;
 
     enum LineHighlightArea
     {
@@ -76,5 +80,12 @@
     LineHighlightArea               lineHighlightArea_;
   };
 
+  class LineMeasureToolMemento : public MeasureToolMemento
+  {
+  public:
+    ScenePoint2D                    start_;
+    ScenePoint2D                    end_;
+  };
+
 }
 
--- a/Framework/Scene2DViewport/MeasureCommands.cpp	Tue Jun 25 15:24:13 2019 +0200
+++ b/Framework/Scene2DViewport/MeasureCommands.cpp	Tue Jun 25 17:54:46 2019 +0200
@@ -50,6 +50,30 @@
     // we thus leave it as is
   }
 
+  EditMeasureCommand::EditMeasureCommand(boost::shared_ptr<MeasureTool> measureTool, boost::weak_ptr<ViewportController> controllerW)
+    : TrackerCommand(controllerW)
+    , mementoOriginal_(measureTool->GetMemento())
+    , mementoModified_(measureTool->GetMemento())
+  {
+
+  }
+
+  EditMeasureCommand::~EditMeasureCommand()
+  {
+
+  }
+
+  void EditMeasureCommand::Undo()
+  {
+    // simply disable the measure tool upon undo
+    GetMeasureTool()->SetMemento(mementoOriginal_);
+  }
+
+  void EditMeasureCommand::Redo()
+  {
+    GetMeasureTool()->SetMemento(mementoModified_);
+  }
+
   CreateLineMeasureCommand::CreateLineMeasureCommand(
     MessageBroker&         broker, 
     boost::weak_ptr<ViewportController> controllerW,
@@ -67,6 +91,29 @@
     measureTool_->SetEnd(scenePos);
   }
 
+  EditLineMeasureCommand::EditLineMeasureCommand(
+    boost::shared_ptr<LineMeasureTool>  measureTool,
+    MessageBroker& broker,
+    boost::weak_ptr<ViewportController> controllerW)
+    : EditMeasureCommand(measureTool,controllerW)
+    , measureTool_(measureTool)
+  {
+  }
+
+
+  void EditLineMeasureCommand::SetStart(ScenePoint2D scenePos)
+  {
+    measureTool_->SetStart(scenePos);
+    mementoModified_ = measureTool_->GetMemento();
+  }
+
+
+  void EditLineMeasureCommand::SetEnd(ScenePoint2D scenePos)
+  {
+    measureTool_->SetEnd(scenePos);
+    mementoModified_ = measureTool_->GetMemento();
+  }
+    
   CreateAngleMeasureCommand::CreateAngleMeasureCommand(
     MessageBroker&         broker, 
     boost::weak_ptr<ViewportController> controllerW,
@@ -99,4 +146,34 @@
     assert(controller); // accessing dead object?
     return controller;
   }
+
+  EditAngleMeasureCommand::EditAngleMeasureCommand(
+    boost::shared_ptr<AngleMeasureTool>  measureTool,
+    MessageBroker& broker,
+    boost::weak_ptr<ViewportController> controllerW)
+    : EditMeasureCommand(measureTool, controllerW)
+    , measureTool_(measureTool)
+  {
+  }
+
+  void EditAngleMeasureCommand::SetCenter(ScenePoint2D scenePos)
+  {
+    measureTool_->SetCenter(scenePos);
+    mementoModified_ = measureTool_->GetMemento();
+  }
+
+
+  void EditAngleMeasureCommand::SetSide1End(ScenePoint2D scenePos)
+  {
+    measureTool_->SetSide1End(scenePos);
+    mementoModified_ = measureTool_->GetMemento();
+  }
+
+
+  void EditAngleMeasureCommand::SetSide2End(ScenePoint2D scenePos)
+  {
+    measureTool_->SetSide2End(scenePos);
+    mementoModified_ = measureTool_->GetMemento();
+  }
+
 }
--- a/Framework/Scene2DViewport/MeasureCommands.h	Tue Jun 25 15:24:13 2019 +0200
+++ b/Framework/Scene2DViewport/MeasureCommands.h	Tue Jun 25 17:54:46 2019 +0200
@@ -42,6 +42,8 @@
     }
     virtual void Undo() = 0;
     virtual void Redo() = 0;
+    
+    virtual ~TrackerCommand() {};
 
   protected:
     boost::shared_ptr<ViewportController>  GetController();
@@ -52,7 +54,7 @@
   {
   public:
     CreateMeasureCommand(boost::weak_ptr<ViewportController> controllerW);
-    ~CreateMeasureCommand();
+    virtual ~CreateMeasureCommand();
     virtual void Undo() ORTHANC_OVERRIDE;
     virtual void Redo() ORTHANC_OVERRIDE;
   private:
@@ -60,6 +62,27 @@
     virtual boost::shared_ptr<MeasureTool> GetMeasureTool() = 0;
   };
   
+  class EditMeasureCommand : public TrackerCommand
+  {
+  public:
+    EditMeasureCommand(boost::shared_ptr<MeasureTool> measureTool, boost::weak_ptr<ViewportController> controllerW);
+    virtual ~EditMeasureCommand();
+    virtual void Undo() ORTHANC_OVERRIDE;
+    virtual void Redo() ORTHANC_OVERRIDE;
+
+    /** This memento is the original object state */
+    boost::shared_ptr<MeasureToolMemento> mementoOriginal_;
+
+  private:
+    /** Must be implemented by the subclasses that edit the actual tool */
+    virtual boost::shared_ptr<MeasureTool> GetMeasureTool() = 0;
+
+  protected:
+
+    /** This memento is updated by the subclasses upon modifications */
+    boost::shared_ptr<MeasureToolMemento> mementoModified_;
+  };
+
   class CreateLineMeasureCommand : public CreateMeasureCommand
   {
   public:
@@ -80,6 +103,26 @@
   };
 
 
+  class EditLineMeasureCommand : public EditMeasureCommand
+  {
+  public:
+    EditLineMeasureCommand(
+      boost::shared_ptr<LineMeasureTool>  measureTool,
+      MessageBroker&                      broker,
+      boost::weak_ptr<ViewportController> controllerW);
+
+    void SetStart(ScenePoint2D scenePos);
+    void SetEnd(ScenePoint2D scenePos);
+
+  private:
+    virtual boost::shared_ptr<MeasureTool> GetMeasureTool() ORTHANC_OVERRIDE
+    {
+      return measureTool_;
+    }
+    boost::shared_ptr<LineMeasureTool> measureTool_;
+  };
+
+
   class CreateAngleMeasureCommand : public CreateMeasureCommand
   {
   public:
@@ -103,5 +146,31 @@
     boost::shared_ptr<AngleMeasureTool> measureTool_;
   };
 
+  class EditAngleMeasureCommand : public EditMeasureCommand
+  {
+  public:
+    /** Ctor sets end of side 1*/
+    EditAngleMeasureCommand(
+      boost::shared_ptr<AngleMeasureTool>  measureTool,
+      MessageBroker& broker,
+      boost::weak_ptr<ViewportController> controllerW);
+
+    /** This method sets center*/
+    void SetCenter(ScenePoint2D scenePos);
+
+    /** This method sets end of side 1*/
+    void SetSide1End(ScenePoint2D scenePos);
+
+    /** This method sets end of side 2*/
+    void SetSide2End(ScenePoint2D scenePos);
+
+  private:
+    virtual boost::shared_ptr<MeasureTool> GetMeasureTool() ORTHANC_OVERRIDE
+    {
+      return measureTool_;
+    }
+    boost::shared_ptr<AngleMeasureTool> measureTool_;
+  };
+
 }
 
--- a/Framework/Scene2DViewport/MeasureTool.h	Tue Jun 25 15:24:13 2019 +0200
+++ b/Framework/Scene2DViewport/MeasureTool.h	Tue Jun 25 17:54:46 2019 +0200
@@ -35,6 +35,9 @@
 
 namespace OrthancStone
 {
+  class IFlexiblePointerTracker;
+  class MeasureToolMemento;
+
   class MeasureTool : public IObserver
   {
   public:
@@ -73,6 +76,25 @@
     virtual bool HitTest(ScenePoint2D p) const = 0;
 
     /**
+    This method must return a memento the captures the tool state (not including
+    the highlighting state
+    */
+    virtual boost::shared_ptr<MeasureToolMemento> GetMemento() const = 0;
+
+    /**
+    This method must apply the supplied memento (this requires RTTI to check
+    the type)
+    */
+    virtual void SetMemento(boost::shared_ptr<MeasureToolMemento>) = 0;
+
+    /**
+    This must create an edition tracker suitable for the supplied click position,
+    or an empty pointer if no hit test (although this should have been checked
+    first)
+    */
+    virtual boost::shared_ptr<IFlexiblePointerTracker> CreateEditionTracker(const PointerEvent& e) = 0;
+
+    /**
     Will change the measuring tool to provide visual feedback on the GUI 
     element that is in the pointer hit zone
     */
@@ -116,6 +138,13 @@
     boost::weak_ptr<ViewportController> controllerW_;
     bool     enabled_;
   };
+
+  class MeasureToolMemento
+  {
+    public:
+      virtual ~MeasureToolMemento() {};
+  };
+
 }
 
 extern void TrackerSample_SetInfoDisplayMessage(
--- a/Framework/Scene2DViewport/MeasureTrackers.cpp	Tue Jun 25 15:24:13 2019 +0200
+++ b/Framework/Scene2DViewport/MeasureTrackers.cpp	Tue Jun 25 17:54:46 2019 +0200
@@ -46,7 +46,6 @@
   {
     // if the tracker completes successfully, we add the command
     // to the undo stack
-
     // otherwise, we simply undo it
     if (commitResult_)
       controllerW_.lock()->PushCommand(command_);
@@ -59,6 +58,41 @@
     return controllerW_.lock()->GetScene();
   }
 
+  EditMeasureTracker::EditMeasureTracker(boost::weak_ptr<ViewportController> controllerW, const PointerEvent& e)
+    : controllerW_(controllerW)
+    , alive_(true)
+    , commitResult_(true)
+  {
+    boost::shared_ptr<ViewportController> controller = controllerW.lock();
+    originalClickPosition_ = e.GetMainPosition().Apply(controller->GetScene()->GetCanvasToSceneTransform());
+  }
+
+  boost::shared_ptr<Scene2D> EditMeasureTracker::GetScene()
+  {
+    return controllerW_.lock()->GetScene();
+  }
+
+  void EditMeasureTracker::Cancel()
+  {
+    commitResult_ = false;
+    alive_ = false;
+  }
+
+  bool EditMeasureTracker::IsAlive() const
+  {
+    return alive_;
+  }
+
+  EditMeasureTracker::~EditMeasureTracker()
+  {
+    // if the tracker completes successfully, we add the command
+    // to the undo stack
+    // otherwise, we simply undo it
+    if (commitResult_)
+      controllerW_.lock()->PushCommand(command_);
+    else
+      command_->Undo();
+  }
 }
 
 
--- a/Framework/Scene2DViewport/MeasureTrackers.h	Tue Jun 25 15:24:13 2019 +0200
+++ b/Framework/Scene2DViewport/MeasureTrackers.h	Tue Jun 25 17:54:46 2019 +0200
@@ -50,5 +50,30 @@
   private:
     bool                            commitResult_;
   };
+
+  class EditMeasureTracker : public IFlexiblePointerTracker
+  {
+  public:
+    virtual void Cancel() ORTHANC_OVERRIDE;
+    virtual bool IsAlive() const ORTHANC_OVERRIDE;
+  protected:
+    EditMeasureTracker(boost::weak_ptr<ViewportController> controllerW, const PointerEvent& e);
+
+    ~EditMeasureTracker();
+
+  protected:
+    boost::shared_ptr<EditMeasureCommand> command_;
+    boost::weak_ptr<ViewportController>   controllerW_;
+    bool                                  alive_;
+    boost::shared_ptr<Scene2D>            GetScene();
+
+    ScenePoint2D                          GetOriginalClickPosition() const
+    {
+      return originalClickPosition_;
+    }
+  private:
+    ScenePoint2D                          originalClickPosition_;
+    bool                                  commitResult_;
+  };
 }
 
--- a/Resources/CMake/OrthancStoneConfiguration.cmake	Tue Jun 25 15:24:13 2019 +0200
+++ b/Resources/CMake/OrthancStoneConfiguration.cmake	Tue Jun 25 17:54:46 2019 +0200
@@ -474,6 +474,14 @@
   ${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/EditAngleMeasureCommand.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/EditAngleMeasureCommand.h
+  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/EditAngleMeasureTracker.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/EditAngleMeasureTracker.h
+  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/EditLineMeasureCommand.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/EditLineMeasureCommand.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