changeset 1975:5a434f5889f8

starting pixel probe
author Sebastien Jodogne <s.jodogne@gmail.com>
date Sat, 29 Oct 2022 11:57:00 +0200
parents 446e0d3e9019
children d71acf30970a
files Applications/Samples/Common/RtViewerApp.cpp Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewerApplication.h Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.cpp OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.h OrthancStone/Sources/Scene2D/GrayscaleWindowingSceneTracker.cpp OrthancStone/Sources/Scene2D/GrayscaleWindowingSceneTracker.h OrthancStone/Sources/Scene2D/PanSceneTracker.cpp OrthancStone/Sources/Scene2D/PanSceneTracker.h OrthancStone/Sources/Scene2D/RotateSceneTracker.cpp OrthancStone/Sources/Scene2D/RotateSceneTracker.h OrthancStone/Sources/Scene2D/ZoomSceneTracker.cpp OrthancStone/Sources/Scene2D/ZoomSceneTracker.h OrthancStone/Sources/Scene2DViewport/IFlexiblePointerTracker.h OrthancStone/Sources/Scene2DViewport/MeasureTrackers.cpp OrthancStone/Sources/Scene2DViewport/MeasureTrackers.h
diffstat 17 files changed, 254 insertions(+), 51 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/Samples/Common/RtViewerApp.cpp	Fri Oct 28 17:58:59 2022 +0200
+++ b/Applications/Samples/Common/RtViewerApp.cpp	Sat Oct 29 11:57:00 2022 +0200
@@ -99,7 +99,12 @@
   {
     if (activeTracker_)
     {
-      activeTracker_->Cancel();
+      // Creating "dummyScene" is a HACK: It won't work with trackers
+      // that probe the values of the textures. For such trackers, the
+      // actual underlying scene should be provided.
+      Scene2D dummyScene;
+      activeTracker_->Cancel(dummyScene);
+      
       activeTracker_.reset();
     }
   }
--- a/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp	Fri Oct 28 17:58:59 2022 +0200
+++ b/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp	Sat Oct 29 11:57:00 2022 +0200
@@ -82,6 +82,7 @@
             << "  d\tDelete mode for annotations" << std::endl
             << "  e\tEdit mode, don't create annotation (default)" << std::endl
             << "  l\tCreate line annotations" << std::endl
+            << "  p\tCreate pixel probe" << std::endl
 #else
             << "  a\tEnable/disable the angle annotation tool" << std::endl
             << "  l\tEnable/disable the line annotation tool" << std::endl
@@ -194,6 +195,7 @@
 #if SAMPLE_USE_ANNOTATIONS_LAYER == 1
         OrthancStone::AnnotationsSceneLayer annotations(10);
         annotations.SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Edit);
+        annotations.SetProbedLayer(0);
 
 #else
         ActiveTool activeTool = ActiveTool_None;
@@ -380,6 +382,12 @@
 #endif
                     break;
 
+#if SAMPLE_USE_ANNOTATIONS_LAYER == 1
+                  case SDLK_p:
+                    annotations.SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_PixelProbe);
+                    break;
+#endif
+
                   default:
                     break;
                 }
--- a/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewerApplication.h	Fri Oct 28 17:58:59 2022 +0200
+++ b/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewerApplication.h	Sat Oct 29 11:57:00 2022 +0200
@@ -130,7 +130,8 @@
 
     std::unique_ptr<TextureBaseSceneLayer> layer(
       message.GetInstanceParameters().CreateTexture(message.GetImage()));
-    layer->SetLinearInterpolation(true);
+    //layer->SetLinearInterpolation(true);
+    layer->SetLinearInterpolation(false);
 
     {
       std::unique_ptr<IViewport::ILock> lock(viewport_->Lock());
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Fri Oct 28 17:58:59 2022 +0200
+++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Sat Oct 29 11:57:00 2022 +0200
@@ -142,7 +142,8 @@
     WebViewerAction_CreateAngle,
     WebViewerAction_CreateCircle,
     WebViewerAction_CreateSegment,
-    WebViewerAction_RemoveMeasure
+    WebViewerAction_RemoveMeasure,
+    WebViewerAction_CreatePixelProbe
     };
   
 
@@ -2561,6 +2562,7 @@
     SetWindowingPreset();
 
     stoneAnnotations_.reset(new OrthancStone::AnnotationsSceneLayer(LAYER_ANNOTATIONS_STONE));
+    stoneAnnotations_->SetProbedLayer(LAYER_TEXTURE);
   }
 
 
@@ -3211,13 +3213,17 @@
               break;
               
             case WebViewerAction_CreateSegment:
-              viewer_.stoneAnnotations_->SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Segment);
+              viewer_.stoneAnnotations_->SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_PixelProbe);
               break;
 
             case WebViewerAction_RemoveMeasure:
               viewer_.stoneAnnotations_->SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Remove);
               break;
 
+            case WebViewerAction_CreatePixelProbe:
+              viewer_.stoneAnnotations_->SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_PixelProbe);
+              break;
+
             default:
               viewer_.stoneAnnotations_->SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Edit);
               break;
--- a/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.cpp	Fri Oct 28 17:58:59 2022 +0200
+++ b/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.cpp	Sat Oct 29 11:57:00 2022 +0200
@@ -26,6 +26,7 @@
 #include "MacroSceneLayer.h"
 #include "PolylineSceneLayer.h"
 #include "TextSceneLayer.h"
+#include "TextureBaseSceneLayer.h"  // TODO REMOVE
 
 #include <OrthancException.h>
 
@@ -37,6 +38,8 @@
 
 static const char* const KEY_ANNOTATIONS = "annotations";
 static const char* const KEY_TYPE = "type";
+static const char* const KEY_X = "x";
+static const char* const KEY_Y = "y";
 static const char* const KEY_X1 = "x1";
 static const char* const KEY_Y1 = "y1";
 static const char* const KEY_X2 = "x2";
@@ -50,6 +53,7 @@
 static const char* const VALUE_SEGMENT = "segment";
 static const char* const VALUE_MILLIMETERS = "millimeters";
 static const char* const VALUE_PIXELS = "pixels";
+static const char* const VALUE_PIXEL_PROBE = "pixel-probe";
 
 #if 0
 static OrthancStone::Color COLOR_PRIMITIVES(192, 192, 192);
@@ -157,9 +161,11 @@
     virtual void RenderOtherLayers(MacroSceneLayer& macro,
                                    const Scene2D& scene) = 0;
 
-    virtual void MovePreview(const ScenePoint2D& delta) = 0;
+    virtual void MovePreview(const ScenePoint2D& delta,
+                             const Scene2D& scene) = 0;
 
-    virtual void MoveDone(const ScenePoint2D& delta) = 0;
+    virtual void MoveDone(const ScenePoint2D& delta,
+                          const Scene2D& scene) = 0;
   };
     
 
@@ -216,7 +222,8 @@
       return *primitive;
     }
 
-    virtual void SignalMove(GeometricPrimitive& primitive) = 0;
+    virtual void SignalMove(GeometricPrimitive& primitive,
+                            const Scene2D& scene) = 0;
 
     virtual void Serialize(Json::Value& target) = 0;
   };
@@ -299,19 +306,21 @@
     {
     }
 
-    virtual void MovePreview(const ScenePoint2D& delta) ORTHANC_OVERRIDE
+    virtual void MovePreview(const ScenePoint2D& delta,
+                             const Scene2D& scene) ORTHANC_OVERRIDE
     {
       SetModified(true);
       delta_ = delta;
-      GetParentAnnotation().SignalMove(*this);
+      GetParentAnnotation().SignalMove(*this, scene);
     }
 
-    virtual void MoveDone(const ScenePoint2D& delta) ORTHANC_OVERRIDE
+    virtual void MoveDone(const ScenePoint2D& delta,
+                          const Scene2D& scene) ORTHANC_OVERRIDE
     {
       SetModified(true);
       center_ = center_ + delta;
       delta_ = ScenePoint2D(0, 0);
-      GetParentAnnotation().SignalMove(*this);
+      GetParentAnnotation().SignalMove(*this, scene);
     }
   };
 
@@ -384,20 +393,22 @@
     {
     }
 
-    virtual void MovePreview(const ScenePoint2D& delta) ORTHANC_OVERRIDE
+    virtual void MovePreview(const ScenePoint2D& delta,
+                             const Scene2D& scene) ORTHANC_OVERRIDE
     {
       SetModified(true);
       delta_ = delta;
-      GetParentAnnotation().SignalMove(*this);
+      GetParentAnnotation().SignalMove(*this, scene);
     }
 
-    virtual void MoveDone(const ScenePoint2D& delta) ORTHANC_OVERRIDE
+    virtual void MoveDone(const ScenePoint2D& delta,
+                          const Scene2D& scene) ORTHANC_OVERRIDE
     {
       SetModified(true);
       p1_ = p1_ + delta;
       p2_ = p2_ + delta;
       delta_ = ScenePoint2D(0, 0);
-      GetParentAnnotation().SignalMove(*this);
+      GetParentAnnotation().SignalMove(*this, scene);
     }
   };
 
@@ -491,20 +502,22 @@
     {
     }
 
-    virtual void MovePreview(const ScenePoint2D& delta) ORTHANC_OVERRIDE
+    virtual void MovePreview(const ScenePoint2D& delta,
+                             const Scene2D& scene) ORTHANC_OVERRIDE
     {
       SetModified(true);
       delta_ = delta;
-      GetParentAnnotation().SignalMove(*this);
+      GetParentAnnotation().SignalMove(*this, scene);
     }
 
-    virtual void MoveDone(const ScenePoint2D& delta) ORTHANC_OVERRIDE
+    virtual void MoveDone(const ScenePoint2D& delta,
+                          const Scene2D& scene) ORTHANC_OVERRIDE
     {
       SetModified(true);
       p1_ = p1_ + delta;
       p2_ = p2_ + delta;
       delta_ = ScenePoint2D(0, 0);
-      GetParentAnnotation().SignalMove(*this);
+      GetParentAnnotation().SignalMove(*this, scene);
     }
   };
 
@@ -626,12 +639,14 @@
     {
     }
 
-    virtual void MovePreview(const ScenePoint2D& delta) ORTHANC_OVERRIDE
+    virtual void MovePreview(const ScenePoint2D& delta,
+                             const Scene2D& scene) ORTHANC_OVERRIDE
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);  // No hit is possible
     }
 
-    virtual void MoveDone(const ScenePoint2D& delta) ORTHANC_OVERRIDE
+    virtual void MoveDone(const ScenePoint2D& delta,
+                          const Scene2D& scene) ORTHANC_OVERRIDE
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);  // No hit is possible
     }
@@ -702,12 +717,14 @@
       }
     }
 
-    virtual void MovePreview(const ScenePoint2D& delta) ORTHANC_OVERRIDE
+    virtual void MovePreview(const ScenePoint2D& delta,
+                             const Scene2D& scene) ORTHANC_OVERRIDE
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);  // No hit is possible
     }
 
-    virtual void MoveDone(const ScenePoint2D& delta) ORTHANC_OVERRIDE
+    virtual void MoveDone(const ScenePoint2D& delta,
+                          const Scene2D& scene) ORTHANC_OVERRIDE
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);  // No hit is possible
     }
@@ -739,14 +756,14 @@
     virtual void PointerMove(const PointerEvent& event,
                              const Scene2D& scene) ORTHANC_OVERRIDE
     {
-      primitive_.MovePreview(event.GetMainPosition().Apply(canvasToScene_) - sceneClick_);
+      primitive_.MovePreview(event.GetMainPosition().Apply(canvasToScene_) - sceneClick_, scene);
       that_.BroadcastMessage(AnnotationChangedMessage(that_));
     }
       
     virtual void PointerUp(const PointerEvent& event,
                            const Scene2D& scene) ORTHANC_OVERRIDE
     {
-      primitive_.MoveDone(event.GetMainPosition().Apply(canvasToScene_) - sceneClick_);
+      primitive_.MoveDone(event.GetMainPosition().Apply(canvasToScene_) - sceneClick_, scene);
       alive_ = false;
       that_.BroadcastMessage(AnnotationChangedMessage(that_));
     }
@@ -761,9 +778,10 @@
       return alive_;
     }
 
-    virtual void Cancel() ORTHANC_OVERRIDE
+    virtual void Cancel(const Scene2D& scene) ORTHANC_OVERRIDE
     {
-      primitive_.MoveDone(ScenePoint2D(0, 0));
+      //primitive_.MoveDone(ScenePoint2D(0, 0), scene);
+      primitive_.MoveDone(sceneClick_, scene);   // TODO Check this
     }
   };
 
@@ -852,7 +870,8 @@
       return handle2_;
     }
 
-    virtual void SignalMove(GeometricPrimitive& primitive) ORTHANC_OVERRIDE
+    virtual void SignalMove(GeometricPrimitive& primitive,
+                            const Scene2D& scene) ORTHANC_OVERRIDE
     {
       if (&primitive == &handle1_ ||
           &primitive == &handle2_)
@@ -903,6 +922,103 @@
   };
 
     
+  class AnnotationsSceneLayer::PixelProbeAnnotation : public Annotation
+  {
+  private:
+    int       probedLayer_;
+    Handle&   handle_;
+    Text&     label_;
+
+    void UpdateLabel(const Scene2D& scene)
+    {
+      TextSceneLayer content;
+
+      content.SetPosition(handle_.GetCenter().GetX(), handle_.GetCenter().GetY());
+      content.SetAnchor(BitmapAnchor_CenterLeft);
+      content.SetBorder(10);
+
+      if (scene.HasLayer(probedLayer_))
+      {
+        const TextureBaseSceneLayer& layer = dynamic_cast<TextureBaseSceneLayer&>(scene.GetLayer(probedLayer_));
+        const AffineTransform2D sceneToTexture = AffineTransform2D::Invert(layer.GetTransform());
+
+        double x = handle_.GetCenter().GetX();
+        double y = handle_.GetCenter().GetY();
+        sceneToTexture.Apply(x, y);
+        
+        int textureX = static_cast<int>(std::floor(x));
+        int textureY = static_cast<int>(std::floor(y));
+        
+        char buf[64];
+        sprintf(buf, "Hello %d x %d / (%d,%d)\n", layer.GetTexture().GetWidth(), layer.GetTexture().GetHeight(),
+                textureX, textureY);
+        content.SetText(buf);
+      }
+      else
+      {
+        content.SetText("????");
+      }
+
+      label_.SetContent(content);
+    }
+
+  public:
+    PixelProbeAnnotation(AnnotationsSceneLayer& that,
+                         Units units,
+                         const ScenePoint2D& p,
+                         const Scene2D& scene,
+                         int probedLayer) :
+      Annotation(that, units),
+      probedLayer_(probedLayer),
+      handle_(AddTypedPrimitive<Handle>(new Handle(*this, p))),
+      label_(AddTypedPrimitive<Text>(new Text(that, *this)))
+    {
+      label_.SetColor(COLOR_TEXT);
+      UpdateLabel(scene);
+    }
+
+    Handle& GetHandle() const
+    {
+      return handle_;
+    }
+
+    virtual void SignalMove(GeometricPrimitive& primitive,
+                            const Scene2D& scene) ORTHANC_OVERRIDE
+    {
+      UpdateLabel(scene);
+    }
+
+    virtual void Serialize(Json::Value& target) ORTHANC_OVERRIDE
+    {
+      target = Json::objectValue;
+      target[KEY_TYPE] = VALUE_PIXEL_PROBE;
+      target[KEY_X] = handle_.GetCenter().GetX();
+      target[KEY_Y] = handle_.GetCenter().GetY();
+    }
+
+    static void Unserialize(AnnotationsSceneLayer& target,
+                            Units units,
+                            int probedLayer,
+                            const Json::Value& source)
+    {
+      if (source.isMember(KEY_X) &&
+          source.isMember(KEY_Y) &&
+          source[KEY_X].isNumeric() &&
+          source[KEY_Y].isNumeric())
+      {
+        Scene2D dummyScene;  // TODO
+        new PixelProbeAnnotation(target, units,
+                                 ScenePoint2D(source[KEY_X].asDouble(), source[KEY_Y].asDouble()),
+                                 dummyScene, probedLayer);
+      }
+      else
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Cannot unserialize a pixel probe");
+      }
+    }
+  };
+
+    
   class AnnotationsSceneLayer::AngleAnnotation : public Annotation
   {
   private:
@@ -968,7 +1084,8 @@
       return endHandle_;
     }
 
-    virtual void SignalMove(GeometricPrimitive& primitive) ORTHANC_OVERRIDE
+    virtual void SignalMove(GeometricPrimitive& primitive,
+                            const Scene2D& scene) ORTHANC_OVERRIDE
     {
       if (&primitive == &startHandle_)
       {
@@ -1131,7 +1248,8 @@
       return handle2_;
     }
 
-    virtual void SignalMove(GeometricPrimitive& primitive) ORTHANC_OVERRIDE
+    virtual void SignalMove(GeometricPrimitive& primitive,
+                            const Scene2D& scene) ORTHANC_OVERRIDE
     {
       if (&primitive == &handle1_ ||
           &primitive == &handle2_)
@@ -1231,7 +1349,7 @@
       {
         assert(handle2_ != NULL);
         handle2_->SetCenter(event.GetMainPosition().Apply(canvasToScene_));
-        annotation_->SignalMove(*handle2_);
+        annotation_->SignalMove(*handle2_, scene);
 
         that_.BroadcastMessage(AnnotationChangedMessage(that_));
       }
@@ -1255,7 +1373,7 @@
       return (annotation_ != NULL);
     }
 
-    virtual void Cancel() ORTHANC_OVERRIDE
+    virtual void Cancel(const Scene2D& scene) ORTHANC_OVERRIDE
     {
       if (annotation_ != NULL)
       {
@@ -1293,14 +1411,14 @@
       if (segment_ != NULL)
       {
         segment_->GetHandle2().SetCenter(event.GetMainPosition().Apply(canvasToScene_));
-        segment_->SignalMove(segment_->GetHandle2());
+        segment_->SignalMove(segment_->GetHandle2(), scene);
         that_.BroadcastMessage(AnnotationChangedMessage(that_));
       }
 
       if (angle_ != NULL)
       {
         angle_->GetEndHandle().SetCenter(event.GetMainPosition().Apply(canvasToScene_));
-        angle_->SignalMove(angle_->GetEndHandle());
+        angle_->SignalMove(angle_->GetEndHandle(), scene);
         that_.BroadcastMessage(AnnotationChangedMessage(that_));
       }
     }
@@ -1340,7 +1458,7 @@
               angle_ != NULL);
     }
 
-    virtual void Cancel() ORTHANC_OVERRIDE
+    virtual void Cancel(const Scene2D& scene) ORTHANC_OVERRIDE
     {
       if (segment_ != NULL)
       {
@@ -1357,6 +1475,49 @@
   };
 
 
+  class AnnotationsSceneLayer::CreatePixelProbeTracker : public IFlexiblePointerTracker
+  {
+  private:
+    AnnotationsSceneLayer&  that_;
+
+  public:
+    CreatePixelProbeTracker(AnnotationsSceneLayer& that,
+                            Units units,
+                            const ScenePoint2D& sceneClick,
+                            const Scene2D& scene,
+                            int probedLayer) :
+      that_(that)
+    {
+      new PixelProbeAnnotation(that, units, sceneClick, scene, probedLayer);
+    }
+
+    virtual void PointerMove(const PointerEvent& event,
+                             const Scene2D& scene) ORTHANC_OVERRIDE
+    {
+    }
+      
+    virtual void PointerUp(const PointerEvent& event,
+                           const Scene2D& scene) ORTHANC_OVERRIDE
+    {
+      that_.BroadcastMessage(AnnotationAddedMessage(that_));
+    }
+
+    virtual void PointerDown(const PointerEvent& event,
+                             const Scene2D& scene) ORTHANC_OVERRIDE
+    {
+    }
+
+    virtual bool IsAlive() const ORTHANC_OVERRIDE
+    {
+      return false;
+    }
+
+    virtual void Cancel(const Scene2D& scene) ORTHANC_OVERRIDE
+    {
+    }
+  };
+
+
   // Dummy tracker that is only used for deletion, in order to warn
   // the caller that the mouse action was taken into consideration
   class AnnotationsSceneLayer::RemoveTracker : public IFlexiblePointerTracker
@@ -1386,7 +1547,7 @@
       return false;
     }
 
-    virtual void Cancel() ORTHANC_OVERRIDE
+    virtual void Cancel(const Scene2D& scene) ORTHANC_OVERRIDE
     {
     }
   };
@@ -1433,7 +1594,8 @@
     activeTool_(Tool_Edit),
     macroLayerIndex_(macroLayerIndex),
     polylineSubLayer_(0),  // dummy initialization
-    units_(Units_Pixels)
+    units_(Units_Pixels),
+    probedLayer_(0)
   {
   }
     
@@ -1631,6 +1793,9 @@
           case Tool_Angle:
             return new CreateAngleTracker(*this, units_, s, scene.GetCanvasToSceneTransform());
 
+          case Tool_PixelProbe:
+            return new CreatePixelProbeTracker(*this, units_, s, scene, probedLayer_);
+
           default:
             return NULL;
         }
@@ -1724,6 +1889,10 @@
       {
         SegmentAnnotation::Unserialize(*this, units_, annotations[i]);
       }
+      else if (type == VALUE_PIXEL_PROBE)
+      {
+        PixelProbeAnnotation::Unserialize(*this, units_, probedLayer_, annotations[i]);
+      }
       else
       {
         LOG(ERROR) << "Cannot unserialize unknown type of annotation: " << type;
--- a/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.h	Fri Oct 28 17:58:59 2022 +0200
+++ b/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.h	Sat Oct 29 11:57:00 2022 +0200
@@ -41,7 +41,8 @@
       Tool_Segment,
       Tool_Angle,
       Tool_Circle,
-      Tool_Remove
+      Tool_Remove,
+      Tool_PixelProbe
     };
 
   private:
@@ -53,6 +54,7 @@
     class Text;
 
     class Annotation;
+    class PixelProbeAnnotation;
     class SegmentAnnotation;
     class AngleAnnotation;
     class CircleAnnotation;
@@ -60,6 +62,7 @@
     class EditPrimitiveTracker;
     class CreateSegmentOrCircleTracker;
     class CreateAngleTracker;
+    class CreatePixelProbeTracker;
     class RemoveTracker;
 
     typedef std::set<GeometricPrimitive*>  GeometricPrimitives;
@@ -73,6 +76,7 @@
     Annotations          annotations_;
     SubLayers            subLayersToRemove_;
     Units                units_;
+    int                  probedLayer_;
 
     void AddAnnotation(Annotation* annotation);
     
@@ -132,5 +136,15 @@
     void Serialize(Json::Value& target) const;
     
     void Unserialize(const Json::Value& serialized);
+
+    void SetProbedLayer(int layer)
+    {
+      probedLayer_ = layer;
+    }
+
+    int GetProbedLayer() const
+    {
+      return probedLayer_;
+    }
   };
 }
--- a/OrthancStone/Sources/Scene2D/GrayscaleWindowingSceneTracker.cpp	Fri Oct 28 17:58:59 2022 +0200
+++ b/OrthancStone/Sources/Scene2D/GrayscaleWindowingSceneTracker.cpp	Sat Oct 29 11:57:00 2022 +0200
@@ -174,7 +174,7 @@
     }
   }
 
-  void GrayscaleWindowingSceneTracker::Cancel()
+  void GrayscaleWindowingSceneTracker::Cancel(const Scene2D& scene)
   {
     SetWindowing(originalCenter_, originalWidth_);
   }
--- a/OrthancStone/Sources/Scene2D/GrayscaleWindowingSceneTracker.h	Fri Oct 28 17:58:59 2022 +0200
+++ b/OrthancStone/Sources/Scene2D/GrayscaleWindowingSceneTracker.h	Sat Oct 29 11:57:00 2022 +0200
@@ -56,6 +56,6 @@
     virtual void PointerMove(const PointerEvent& event,
                              const Scene2D& scene) ORTHANC_OVERRIDE;
     
-    virtual void Cancel() ORTHANC_OVERRIDE;
+    virtual void Cancel(const Scene2D& scene) ORTHANC_OVERRIDE;
   };
 }
--- a/OrthancStone/Sources/Scene2D/PanSceneTracker.cpp	Fri Oct 28 17:58:59 2022 +0200
+++ b/OrthancStone/Sources/Scene2D/PanSceneTracker.cpp	Sat Oct 29 11:57:00 2022 +0200
@@ -61,7 +61,7 @@
     }
   }
 
-  void PanSceneTracker::Cancel()
+  void PanSceneTracker::Cancel(const Scene2D& scene)
   {
     ViewportLocker locker(viewport_);
     
--- a/OrthancStone/Sources/Scene2D/PanSceneTracker.h	Fri Oct 28 17:58:59 2022 +0200
+++ b/OrthancStone/Sources/Scene2D/PanSceneTracker.h	Sat Oct 29 11:57:00 2022 +0200
@@ -42,6 +42,6 @@
     virtual void PointerMove(const PointerEvent& event,
                              const Scene2D& scene) ORTHANC_OVERRIDE;
     
-    virtual void Cancel() ORTHANC_OVERRIDE;
+    virtual void Cancel(const Scene2D& scene) ORTHANC_OVERRIDE;
   };
 }
--- a/OrthancStone/Sources/Scene2D/RotateSceneTracker.cpp	Fri Oct 28 17:58:59 2022 +0200
+++ b/OrthancStone/Sources/Scene2D/RotateSceneTracker.cpp	Sat Oct 29 11:57:00 2022 +0200
@@ -80,7 +80,7 @@
   }
 
   
-  void RotateSceneTracker::Cancel()
+  void RotateSceneTracker::Cancel(const Scene2D& scene)
   {
     ViewportLocker locker(viewport_);
     
--- a/OrthancStone/Sources/Scene2D/RotateSceneTracker.h	Fri Oct 28 17:58:59 2022 +0200
+++ b/OrthancStone/Sources/Scene2D/RotateSceneTracker.h	Sat Oct 29 11:57:00 2022 +0200
@@ -47,6 +47,6 @@
     virtual void PointerMove(const PointerEvent& event,
                              const Scene2D& scene) ORTHANC_OVERRIDE;
     
-    virtual void Cancel() ORTHANC_OVERRIDE;
+    virtual void Cancel(const Scene2D& scene) ORTHANC_OVERRIDE;
   };
 }
--- a/OrthancStone/Sources/Scene2D/ZoomSceneTracker.cpp	Fri Oct 28 17:58:59 2022 +0200
+++ b/OrthancStone/Sources/Scene2D/ZoomSceneTracker.cpp	Sat Oct 29 11:57:00 2022 +0200
@@ -93,7 +93,7 @@
     }
   }
 
-  void ZoomSceneTracker::Cancel()
+  void ZoomSceneTracker::Cancel(const Scene2D& scene)
   {
     ViewportLocker locker(viewport_);
     
--- a/OrthancStone/Sources/Scene2D/ZoomSceneTracker.h	Fri Oct 28 17:58:59 2022 +0200
+++ b/OrthancStone/Sources/Scene2D/ZoomSceneTracker.h	Sat Oct 29 11:57:00 2022 +0200
@@ -50,6 +50,6 @@
     virtual void PointerMove(const PointerEvent& event,
                              const Scene2D& scene) ORTHANC_OVERRIDE;
     
-    virtual void Cancel() ORTHANC_OVERRIDE;
+    virtual void Cancel(const Scene2D& scene) ORTHANC_OVERRIDE;
   };
 }
--- a/OrthancStone/Sources/Scene2DViewport/IFlexiblePointerTracker.h	Fri Oct 28 17:58:59 2022 +0200
+++ b/OrthancStone/Sources/Scene2DViewport/IFlexiblePointerTracker.h	Sat Oct 29 11:57:00 2022 +0200
@@ -80,6 +80,6 @@
     its changes to the underlying model. If the model has been modified during
     tracker lifetime, it must be restored to its initial value
     */
-    virtual void Cancel() = 0;
+    virtual void Cancel(const Scene2D& scene) = 0;
   };
 }
--- a/OrthancStone/Sources/Scene2DViewport/MeasureTrackers.cpp	Fri Oct 28 17:58:59 2022 +0200
+++ b/OrthancStone/Sources/Scene2DViewport/MeasureTrackers.cpp	Sat Oct 29 11:57:00 2022 +0200
@@ -42,7 +42,7 @@
       return NULL;
   }
 
-  void CreateMeasureTracker::Cancel()
+  void CreateMeasureTracker::Cancel(const Scene2D& scene)
   {
     commitResult_ = false;
     alive_ = false;
@@ -93,7 +93,7 @@
       return NULL;
   }
 
-  void EditMeasureTracker::Cancel()
+  void EditMeasureTracker::Cancel(const Scene2D& scene)
   {
     commitResult_ = false;
     alive_ = false;
--- a/OrthancStone/Sources/Scene2DViewport/MeasureTrackers.h	Fri Oct 28 17:58:59 2022 +0200
+++ b/OrthancStone/Sources/Scene2DViewport/MeasureTrackers.h	Sat Oct 29 11:57:00 2022 +0200
@@ -57,7 +57,7 @@
     virtual ~CreateMeasureTracker();
     
   public:
-    virtual void Cancel() ORTHANC_OVERRIDE;
+    virtual void Cancel(const Scene2D& scene) ORTHANC_OVERRIDE;
     
     virtual bool IsAlive() const ORTHANC_OVERRIDE;
   };
@@ -91,7 +91,7 @@
     }
 
   public:
-    virtual void Cancel() ORTHANC_OVERRIDE;
+    virtual void Cancel(const Scene2D& scene) ORTHANC_OVERRIDE;
     
     virtual bool IsAlive() const ORTHANC_OVERRIDE;
   };