diff OrthancStone/Sources/Scene2DViewport/AngleMeasureTool.cpp @ 1512:244ad1e4e76a

reorganization of folders
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 07 Jul 2020 16:21:02 +0200
parents Framework/Scene2DViewport/AngleMeasureTool.cpp@30deba7bc8e2
children 85e117739eca
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancStone/Sources/Scene2DViewport/AngleMeasureTool.cpp	Tue Jul 07 16:21:02 2020 +0200
@@ -0,0 +1,427 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 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 "AngleMeasureTool.h"
+#include "MeasureToolsToolbox.h"
+#include "EditAngleMeasureTracker.h"
+#include "LayerHolder.h"
+#include "../StoneException.h"
+
+#include <Logging.h>
+
+#include <boost/math/constants/constants.hpp>
+#include <boost/make_shared.hpp>
+
+//// <HACK>
+//// REMOVE THIS
+//#ifndef NDEBUG
+//extern void 
+//TrackerSample_SetInfoDisplayMessage(std::string key, std::string value);
+//#endif
+//// </HACK>
+
+namespace OrthancStone
+{
+  // the params in the LayerHolder ctor specify the number of polyline and text
+  // layers
+  AngleMeasureTool::AngleMeasureTool(
+    boost::shared_ptr<IViewport> viewport)
+    : MeasureTool(viewport)
+#if ORTHANC_STONE_ENABLE_OUTLINED_TEXT == 1
+    , layerHolder_(boost::shared_ptr<LayerHolder>(new LayerHolder(viewport,1,5)))
+#else
+    , layerHolder_(boost::shared_ptr<LayerHolder>(new LayerHolder(viewport,1,1)))
+#endif
+    , angleHighlightArea_(AngleHighlightArea_None)
+  {
+  }
+
+  boost::shared_ptr<AngleMeasureTool> AngleMeasureTool::Create(boost::shared_ptr<IViewport> viewport)
+  {
+    boost::shared_ptr<AngleMeasureTool> obj(new AngleMeasureTool(viewport));
+    obj->MeasureTool::PostConstructor();
+    obj->RefreshScene();
+    return obj;
+  }
+
+  AngleMeasureTool::~AngleMeasureTool()
+  {
+    // this measuring tool is a RABI for the corresponding visual layers
+    // stored in the 2D scene
+    Disable();
+    RemoveFromScene();
+  }
+
+  void AngleMeasureTool::RemoveFromScene()
+  {
+    if (layerHolder_->AreLayersCreated() && IsSceneAlive())
+    {
+      layerHolder_->DeleteLayers();
+    }
+  }
+
+  void AngleMeasureTool::SetSide1End(ScenePoint2D pt)
+  {
+    side1End_ = pt;
+    RefreshScene();
+  }
+
+  void AngleMeasureTool::SetSide2End(ScenePoint2D pt)
+  {
+    side2End_ = pt;
+    RefreshScene();
+  }
+
+  void AngleMeasureTool::SetAngleHighlightArea(AngleHighlightArea area)
+  {
+    if (angleHighlightArea_ != area)
+    {
+      angleHighlightArea_ = area;
+      RefreshScene();
+    }
+  }
+
+  void AngleMeasureTool::ResetHighlightState()
+  {
+    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();
+  }
+
+  std::string AngleMeasureTool::GetDescription()
+  {
+    std::stringstream ss;
+    ss << "AngleMeasureTool. Center = " << center_ << " Side1End = " 
+       << side1End_ << " Side2End = " << side2End_;
+    return ss.str();
+  }
+
+  void AngleMeasureTool::Highlight(ScenePoint2D p)
+  {
+    AngleHighlightArea angleHighlightArea = AngleHitTest(p);
+    SetAngleHighlightArea(angleHighlightArea);
+  }
+
+  AngleMeasureTool::AngleHighlightArea AngleMeasureTool::AngleHitTest(ScenePoint2D p) const
+  {
+    std::unique_ptr<IViewport::ILock> lock(viewport_->Lock());
+    ViewportController& controller = lock->GetController();
+    Scene2D& scene = controller.GetScene();
+
+    const double pixelToScene = scene.GetCanvasToSceneTransform().ComputeZoom();
+
+    const double SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD = 
+      pixelToScene * HIT_TEST_MAX_DISTANCE_CANVAS_COORD * 
+      pixelToScene * HIT_TEST_MAX_DISTANCE_CANVAS_COORD;
+
+    {
+      const double sqDistanceFromSide1End = 
+        ScenePoint2D::SquaredDistancePtPt(p, side1End_);
+
+      if (sqDistanceFromSide1End <= SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD)
+        return AngleHighlightArea_Side1End;
+    }
+
+    {
+      const double sqDistanceFromSide2End = 
+        ScenePoint2D::SquaredDistancePtPt(p, side2End_);
+
+      if (sqDistanceFromSide2End <= SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD)
+        return AngleHighlightArea_Side2End;
+    }
+
+    {
+      const double sqDistanceFromCenter = 
+        ScenePoint2D::SquaredDistancePtPt(p, center_);
+      if (sqDistanceFromCenter <= SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD)
+        return AngleHighlightArea_Center;
+    }
+
+    {
+      const double sqDistanceFromSide1 = 
+        ScenePoint2D::SquaredDistancePtSegment(center_, side1End_, p);
+
+      if (sqDistanceFromSide1 <= SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD)
+        return AngleHighlightArea_Side1;
+    }
+
+    {
+      const double sqDistanceFromSide2 = 
+        ScenePoint2D::SquaredDistancePtSegment(center_, side2End_, p);
+
+      if (sqDistanceFromSide2 <= SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD)
+        return AngleHighlightArea_Side2;
+    }
+
+    return AngleHighlightArea_None;
+  }
+
+  bool AngleMeasureTool::HitTest(ScenePoint2D p)
+  {
+    return AngleHitTest(p) != AngleHighlightArea_None;
+  }
+
+
+  boost::shared_ptr<IFlexiblePointerTracker> AngleMeasureTool::CreateEditionTracker(const PointerEvent& e)
+  {
+    std::unique_ptr<IViewport::ILock> lock(viewport_->Lock());
+    ViewportController& controller = lock->GetController();
+    Scene2D& scene = controller.GetScene();
+
+    ScenePoint2D scenePos = e.GetMainPosition().Apply(
+      scene.GetCanvasToSceneTransform());
+
+    if (!HitTest(scenePos))
+      return boost::shared_ptr<IFlexiblePointerTracker>();
+
+    /**
+      new EditLineMeasureTracker(
+        boost::shared_ptr<LineMeasureTool> measureTool;
+        MessageBroker & broker,
+        boost::shared_ptr<IViewport>          viewport,
+        const PointerEvent & e);
+    */
+
+    boost::shared_ptr<EditAngleMeasureTracker> editAngleMeasureTracker(
+      new EditAngleMeasureTracker(shared_from_this(), viewport_, e));
+    return editAngleMeasureTracker;
+  }
+
+  void AngleMeasureTool::SetCenter(ScenePoint2D pt)
+  {
+    center_ = pt;
+    RefreshScene();
+  }
+  
+  void AngleMeasureTool::RefreshScene()
+  {
+    if (IsSceneAlive())
+    {
+      std::unique_ptr<IViewport::ILock> lock(viewport_->Lock());
+      ViewportController& controller = lock->GetController();
+      Scene2D& scene = controller.GetScene();
+
+      if (IsEnabled())
+      {
+        layerHolder_->CreateLayersIfNeeded();
+
+        {
+          // Fill the polyline layer with the measurement lines
+          PolylineSceneLayer* polylineLayer = layerHolder_->GetPolylineLayer(0);
+          if (polylineLayer)
+          {
+            polylineLayer->ClearAllChains();
+
+            const Color color(TOOL_ANGLE_LINES_COLOR_RED, 
+                              TOOL_ANGLE_LINES_COLOR_GREEN, 
+                              TOOL_ANGLE_LINES_COLOR_BLUE);
+
+            const Color highlightColor(TOOL_ANGLE_LINES_HL_COLOR_RED, 
+                                       TOOL_ANGLE_LINES_HL_COLOR_GREEN, 
+                                       TOOL_ANGLE_LINES_HL_COLOR_BLUE);
+
+            // sides
+            {
+              {
+                PolylineSceneLayer::Chain chain;
+                chain.push_back(side1End_);
+                chain.push_back(center_);
+
+                if ((angleHighlightArea_ == AngleHighlightArea_Side1) ||
+                    (angleHighlightArea_ == AngleHighlightArea_Side2))
+                {
+                  polylineLayer->AddChain(chain, false, highlightColor);
+                } 
+                else
+                {
+                  polylineLayer->AddChain(chain, false, color);
+                }
+              }
+              {
+                PolylineSceneLayer::Chain chain;
+                chain.push_back(side2End_);
+                chain.push_back(center_);
+                if ((angleHighlightArea_ == AngleHighlightArea_Side1) ||
+                  (angleHighlightArea_ == AngleHighlightArea_Side2))
+                {
+                  polylineLayer->AddChain(chain, false, highlightColor);
+                }
+                else
+                {
+                  polylineLayer->AddChain(chain, false, color);
+                }
+              }
+            }
+
+            // Create the handles
+            {
+              {
+                PolylineSceneLayer::Chain chain;
+                //TODO: take DPI into account
+                AddSquare(chain, controller.GetScene(), side1End_, 
+                          controller.GetHandleSideLengthS());
+              
+                if (angleHighlightArea_ == AngleHighlightArea_Side1End)
+                  polylineLayer->AddChain(chain, true, highlightColor);
+                else
+                  polylineLayer->AddChain(chain, true, color);
+              
+              }
+              {
+                PolylineSceneLayer::Chain chain;
+                //TODO: take DPI into account
+                AddSquare(chain, controller.GetScene(), side2End_, 
+                          controller.GetHandleSideLengthS());
+
+                if (angleHighlightArea_ == AngleHighlightArea_Side2End)
+                  polylineLayer->AddChain(chain, true, highlightColor);
+                else
+                  polylineLayer->AddChain(chain, true, color);
+              }
+            }
+
+            // Create the arc
+            {
+              PolylineSceneLayer::Chain chain;
+
+              AddShortestArc(chain, side1End_, center_, side2End_,
+                             controller.GetAngleToolArcRadiusS());
+              if (angleHighlightArea_ == AngleHighlightArea_Center)
+                polylineLayer->AddChain(chain, false, highlightColor);
+              else
+                polylineLayer->AddChain(chain, false, color);
+            }
+          }
+        }
+        {
+          // Set the text layer
+
+          double p1cAngle = atan2(
+            side1End_.GetY() - center_.GetY(),
+            side1End_.GetX() - center_.GetX());
+
+          double p2cAngle = atan2(
+            side2End_.GetY() - center_.GetY(),
+            side2End_.GetX() - center_.GetX());
+
+          double delta = NormalizeAngle(p2cAngle - p1cAngle);
+          double theta = p1cAngle + delta / 2;
+
+          double ox = controller.GetAngleTopTextLabelDistanceS() * cos(theta);
+          double oy = controller.GetAngleTopTextLabelDistanceS() * sin(theta);
+
+          double pointX = center_.GetX() + ox;
+          double pointY = center_.GetY() + oy;
+
+          char buf[64];
+          double angleDeg = RadiansToDegrees(delta);
+
+          // http://www.ltg.ed.ac.uk/~richard/utf-8.cgi?input=00B0&mode=hex
+          sprintf(buf, "%0.02f\xc2\xb0", angleDeg);
+
+#if ORTHANC_STONE_ENABLE_OUTLINED_TEXT == 1
+          SetTextLayerOutlineProperties(
+            scene, layerHolder_, buf, ScenePoint2D(pointX, pointY), 0);
+#else
+          SetTextLayerProperties(
+            scene, layerHolder_, buf, ScenePoint2D(pointX, pointY) , 0);
+#endif
+
+#if 0
+          // TODO:make it togglable
+          bool enableInfoDisplay = true;
+          if (enableInfoDisplay)
+          {
+            TrackerSample_SetInfoDisplayMessage("center_.GetX()",
+              boost::lexical_cast<std::string>(center_.GetX()));
+
+            TrackerSample_SetInfoDisplayMessage("center_.GetY()",
+              boost::lexical_cast<std::string>(center_.GetY()));
+
+            TrackerSample_SetInfoDisplayMessage("side1End_.GetX()",
+              boost::lexical_cast<std::string>(side1End_.GetX()));
+
+            TrackerSample_SetInfoDisplayMessage("side1End_.GetY()",
+              boost::lexical_cast<std::string>(side1End_.GetY()));
+
+            TrackerSample_SetInfoDisplayMessage("side2End_.GetX()",
+              boost::lexical_cast<std::string>(side2End_.GetX()));
+
+            TrackerSample_SetInfoDisplayMessage("side2End_.GetY()",
+              boost::lexical_cast<std::string>(side2End_.GetY()));
+
+            TrackerSample_SetInfoDisplayMessage("p1cAngle (deg)",
+              boost::lexical_cast<std::string>(RadiansToDegrees(p1cAngle)));
+
+            TrackerSample_SetInfoDisplayMessage("delta (deg)",
+              boost::lexical_cast<std::string>(RadiansToDegrees(delta)));
+
+            TrackerSample_SetInfoDisplayMessage("theta (deg)",
+              boost::lexical_cast<std::string>(RadiansToDegrees(theta)));
+
+            TrackerSample_SetInfoDisplayMessage("p2cAngle (deg)",
+              boost::lexical_cast<std::string>(RadiansToDegrees(p2cAngle)));
+
+            TrackerSample_SetInfoDisplayMessage("ox (scene)",
+              boost::lexical_cast<std::string>(ox));
+
+            TrackerSample_SetInfoDisplayMessage("offsetY (scene)",
+              boost::lexical_cast<std::string>(oy));
+
+            TrackerSample_SetInfoDisplayMessage("pointX",
+              boost::lexical_cast<std::string>(pointX));
+
+            TrackerSample_SetInfoDisplayMessage("pointY",
+              boost::lexical_cast<std::string>(pointY));
+
+            TrackerSample_SetInfoDisplayMessage("angleDeg",
+              boost::lexical_cast<std::string>(angleDeg));
+          }
+#endif
+        }
+      }
+      else
+      {
+        RemoveFromScene();
+      }
+      lock->Invalidate();
+    }
+  }
+}