comparison Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp @ 1798:41f3872bd7d2

create angle tool and delete measure tool
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 20 May 2021 11:10:14 +0200
parents 013dec434a84
children 1125793d82d3
comparison
equal deleted inserted replaced
1797:013dec434a84 1798:41f3872bd7d2
54 54
55 namespace OrthancStone 55 namespace OrthancStone
56 { 56 {
57 class AnnotationsOverlay : public boost::noncopyable 57 class AnnotationsOverlay : public boost::noncopyable
58 { 58 {
59 public:
60 enum Tool
61 {
62 Tool_Edit,
63 Tool_None,
64 Tool_Segment,
65 Tool_Angle,
66 Tool_Circle,
67 Tool_Erase
68 };
69
59 private: 70 private:
60 class Measure; 71 class Measure;
61 72
62 class Primitive : public boost::noncopyable 73 class Primitive : public boost::noncopyable
63 { 74 {
67 Color color_; 78 Color color_;
68 Color hoverColor_; 79 Color hoverColor_;
69 bool isHover_; 80 bool isHover_;
70 int depth_; 81 int depth_;
71 82
72 protected:
73 Measure& GetParentMeasure() const
74 {
75 return parentMeasure_;
76 }
77
78 public: 83 public:
79 Primitive(Measure& parentMeasure, 84 Primitive(Measure& parentMeasure,
80 int depth) : 85 int depth) :
81 modified_(true), 86 modified_(true),
82 parentMeasure_(parentMeasure), 87 parentMeasure_(parentMeasure),
89 94
90 virtual ~Primitive() 95 virtual ~Primitive()
91 { 96 {
92 } 97 }
93 98
99 Measure& GetParentMeasure() const
100 {
101 return parentMeasure_;
102 }
103
94 int GetDepth() const 104 int GetDepth() const
95 { 105 {
96 return depth_; 106 return depth_;
97 } 107 }
98 108
729 739
730 740
731 class SegmentMeasure : public Measure 741 class SegmentMeasure : public Measure
732 { 742 {
733 private: 743 private:
744 bool showLabel_;
734 Handle& handle1_; 745 Handle& handle1_;
735 Handle& handle2_; 746 Handle& handle2_;
736 Segment& segment_; 747 Segment& segment_;
737 Text& label_; 748 Text& label_;
738 749
739 void UpdateLabel() 750 void UpdateLabel()
740 { 751 {
741 TextSceneLayer content; 752 if (showLabel_)
742 753 {
743 double x1 = handle1_.GetCenter().GetX(); 754 TextSceneLayer content;
744 double y1 = handle1_.GetCenter().GetY(); 755
745 double x2 = handle2_.GetCenter().GetX(); 756 double x1 = handle1_.GetCenter().GetX();
746 double y2 = handle2_.GetCenter().GetY(); 757 double y1 = handle1_.GetCenter().GetY();
758 double x2 = handle2_.GetCenter().GetX();
759 double y2 = handle2_.GetCenter().GetY();
747 760
748 // Put the label to the right of the right-most handle 761 // Put the label to the right of the right-most handle
749 if (x1 < x2) 762 if (x1 < x2)
750 { 763 {
751 content.SetPosition(x2, y2); 764 content.SetPosition(x2, y2);
752 } 765 }
753 else 766 else
754 { 767 {
755 content.SetPosition(x1, y1); 768 content.SetPosition(x1, y1);
756 } 769 }
757 770
758 content.SetAnchor(BitmapAnchor_CenterLeft); 771 content.SetAnchor(BitmapAnchor_CenterLeft);
759 content.SetBorder(10); 772 content.SetBorder(10);
760 773
761 double dx = x1 - x2; 774 double dx = x1 - x2;
762 double dy = y1 - y2; 775 double dy = y1 - y2;
763 char buf[32]; 776 char buf[32];
764 sprintf(buf, "%0.2f cm", sqrt(dx * dx + dy * dy) / 10.0); 777 sprintf(buf, "%0.2f cm", sqrt(dx * dx + dy * dy) / 10.0);
765 content.SetText(buf); 778 content.SetText(buf);
766 779
767 label_.SetContent(content); 780 label_.SetContent(content);
781 }
768 } 782 }
769 783
770 public: 784 public:
771 SegmentMeasure(AnnotationsOverlay& that, 785 SegmentMeasure(AnnotationsOverlay& that,
786 bool showLabel,
772 const ScenePoint2D& p1, 787 const ScenePoint2D& p1,
773 const ScenePoint2D& p2) : 788 const ScenePoint2D& p2) :
774 Measure(that), 789 Measure(that),
790 showLabel_(showLabel),
775 handle1_(AddTypedPrimitive<Handle>(new Handle(*this, p1))), 791 handle1_(AddTypedPrimitive<Handle>(new Handle(*this, p1))),
776 handle2_(AddTypedPrimitive<Handle>(new Handle(*this, p2))), 792 handle2_(AddTypedPrimitive<Handle>(new Handle(*this, p2))),
777 segment_(AddTypedPrimitive<Segment>(new Segment(*this, p1, p2))), 793 segment_(AddTypedPrimitive<Segment>(new Segment(*this, p1, p2))),
778 label_(AddTypedPrimitive<Text>(new Text(that, *this))) 794 label_(AddTypedPrimitive<Text>(new Text(that, *this)))
779 { 795 {
780 label_.SetColor(Color(255, 0, 0)); 796 label_.SetColor(Color(255, 0, 0));
781 UpdateLabel(); 797 UpdateLabel();
798 }
799
800 Handle& GetHandle1() const
801 {
802 return handle1_;
782 } 803 }
783 804
784 Handle& GetHandle2() const 805 Handle& GetHandle2() const
785 { 806 {
786 return handle2_; 807 return handle2_;
861 { 882 {
862 label_.SetColor(Color(255, 0, 0)); 883 label_.SetColor(Color(255, 0, 0));
863 UpdateLabel(); 884 UpdateLabel();
864 } 885 }
865 886
887 Handle& GetEndHandle() const
888 {
889 return endHandle_;
890 }
891
866 virtual void SignalMove(Primitive& primitive) ORTHANC_OVERRIDE 892 virtual void SignalMove(Primitive& primitive) ORTHANC_OVERRIDE
867 { 893 {
868 if (&primitive == &startHandle_) 894 if (&primitive == &startHandle_)
869 { 895 {
870 segment1_.SetPosition(startHandle_.GetCenter(), middleHandle_.GetCenter()); 896 segment1_.SetPosition(startHandle_.GetCenter(), middleHandle_.GetCenter());
1013 measure_ = new CircleMeasure(that, sceneClick, sceneClick); 1039 measure_ = new CircleMeasure(that, sceneClick, sceneClick);
1014 handle2_ = &dynamic_cast<CircleMeasure*>(measure_)->GetHandle2(); 1040 handle2_ = &dynamic_cast<CircleMeasure*>(measure_)->GetHandle2();
1015 } 1041 }
1016 else 1042 else
1017 { 1043 {
1018 measure_ = new SegmentMeasure(that, sceneClick, sceneClick); 1044 measure_ = new SegmentMeasure(that, true /* show label */, sceneClick, sceneClick);
1019 handle2_ = &dynamic_cast<SegmentMeasure*>(measure_)->GetHandle2(); 1045 handle2_ = &dynamic_cast<SegmentMeasure*>(measure_)->GetHandle2();
1020 } 1046 }
1021 1047
1022 assert(measure_ != NULL && 1048 assert(measure_ != NULL &&
1023 handle2_ != NULL); 1049 handle2_ != NULL);
1056 } 1082 }
1057 } 1083 }
1058 }; 1084 };
1059 1085
1060 1086
1087 class CreateAngleTracker : public IFlexiblePointerTracker
1088 {
1089 private:
1090 AnnotationsOverlay& that_;
1091 SegmentMeasure* segment_;
1092 AngleMeasure* angle_;
1093 AffineTransform2D canvasToScene_;
1094
1095 public:
1096 CreateAngleTracker(AnnotationsOverlay& that,
1097 const ScenePoint2D& sceneClick,
1098 const AffineTransform2D& canvasToScene) :
1099 that_(that),
1100 segment_(NULL),
1101 angle_(NULL),
1102 canvasToScene_(canvasToScene)
1103 {
1104 segment_ = new SegmentMeasure(that, false /* no length label */, sceneClick, sceneClick);
1105 }
1106
1107 virtual void PointerMove(const PointerEvent& event) ORTHANC_OVERRIDE
1108 {
1109 if (segment_ != NULL)
1110 {
1111 segment_->GetHandle2().SetCenter(event.GetMainPosition().Apply(canvasToScene_));
1112 segment_->SignalMove(segment_->GetHandle2());
1113 }
1114
1115 if (angle_ != NULL)
1116 {
1117 angle_->GetEndHandle().SetCenter(event.GetMainPosition().Apply(canvasToScene_));
1118 angle_->SignalMove(angle_->GetEndHandle());
1119 }
1120 }
1121
1122 virtual void PointerUp(const PointerEvent& event) ORTHANC_OVERRIDE
1123 {
1124 if (segment_ != NULL)
1125 {
1126 // End of first step: The first segment is available, now create the angle
1127
1128 angle_ = new AngleMeasure(that_, segment_->GetHandle1().GetCenter(),
1129 segment_->GetHandle2().GetCenter(),
1130 segment_->GetHandle2().GetCenter());
1131
1132 that_.DeleteMeasure(segment_);
1133 segment_ = NULL;
1134 }
1135 else
1136 {
1137 angle_ = NULL; // IsAlive() becomes false
1138 }
1139 }
1140
1141 virtual void PointerDown(const PointerEvent& event) ORTHANC_OVERRIDE
1142 {
1143 }
1144
1145 virtual bool IsAlive() const ORTHANC_OVERRIDE
1146 {
1147 return (segment_ != NULL ||
1148 angle_ != NULL);
1149 }
1150
1151 virtual void Cancel() ORTHANC_OVERRIDE
1152 {
1153 if (segment_ != NULL)
1154 {
1155 that_.DeleteMeasure(segment_);
1156 segment_ = NULL;
1157 }
1158
1159 if (angle_ != NULL)
1160 {
1161 that_.DeleteMeasure(angle_);
1162 angle_ = NULL;
1163 }
1164 }
1165 };
1166
1167
1168 // Dummy tracker that is only used for deletion, in order to warn
1169 // the caller that the mouse action was taken into consideration
1170 class EraseTracker : public IFlexiblePointerTracker
1171 {
1172 public:
1173 EraseTracker()
1174 {
1175 }
1176
1177 virtual void PointerMove(const PointerEvent& event) ORTHANC_OVERRIDE
1178 {
1179 }
1180
1181 virtual void PointerUp(const PointerEvent& event) ORTHANC_OVERRIDE
1182 {
1183 }
1184
1185 virtual void PointerDown(const PointerEvent& event) ORTHANC_OVERRIDE
1186 {
1187 }
1188
1189 virtual bool IsAlive() const ORTHANC_OVERRIDE
1190 {
1191 return false;
1192 }
1193
1194 virtual void Cancel() ORTHANC_OVERRIDE
1195 {
1196 }
1197 };
1198
1199
1061 typedef std::set<Primitive*> Primitives; 1200 typedef std::set<Primitive*> Primitives;
1062 typedef std::set<Measure*> Measures; 1201 typedef std::set<Measure*> Measures;
1063 typedef std::set<size_t> SubLayers; 1202 typedef std::set<size_t> SubLayers;
1064 1203
1204 Tool activeTool_;
1065 size_t macroLayerIndex_; 1205 size_t macroLayerIndex_;
1066 size_t polylineSubLayer_; 1206 size_t polylineSubLayer_;
1067 Primitives primitives_; 1207 Primitives primitives_;
1068 Measures measures_; 1208 Measures measures_;
1069 SubLayers subLayersToRemove_; 1209 SubLayers subLayersToRemove_;
1101 subLayersToRemove_.insert(subLayerIndex); 1241 subLayersToRemove_.insert(subLayerIndex);
1102 } 1242 }
1103 1243
1104 public: 1244 public:
1105 AnnotationsOverlay(size_t macroLayerIndex) : 1245 AnnotationsOverlay(size_t macroLayerIndex) :
1246 activeTool_(Tool_Edit),
1106 macroLayerIndex_(macroLayerIndex), 1247 macroLayerIndex_(macroLayerIndex),
1107 polylineSubLayer_(0) // dummy initialization 1248 polylineSubLayer_(0) // dummy initialization
1108 { 1249 {
1109 measures_.insert(new SegmentMeasure(*this, ScenePoint2D(0, 0), ScenePoint2D(100, 100))); 1250 measures_.insert(new SegmentMeasure(*this, true /* show label */, ScenePoint2D(0, 0), ScenePoint2D(100, 100)));
1110 measures_.insert(new AngleMeasure(*this, ScenePoint2D(100, 50), ScenePoint2D(150, 40), ScenePoint2D(200, 50))); 1251 measures_.insert(new AngleMeasure(*this, ScenePoint2D(100, 50), ScenePoint2D(150, 40), ScenePoint2D(200, 50)));
1111 measures_.insert(new CircleMeasure(*this, ScenePoint2D(50, 200), ScenePoint2D(100, 250))); 1252 measures_.insert(new CircleMeasure(*this, ScenePoint2D(50, 200), ScenePoint2D(100, 250)));
1112 } 1253 }
1113 1254
1114 ~AnnotationsOverlay() 1255 ~AnnotationsOverlay()
1120 } 1261 }
1121 1262
1122 measures_.clear(); 1263 measures_.clear();
1123 } 1264 }
1124 1265
1266 void SetActiveTool(Tool tool)
1267 {
1268 activeTool_ = tool;
1269 }
1270
1271 Tool GetActiveTool() const
1272 {
1273 return activeTool_;
1274 }
1275
1125 void Render(Scene2D& scene) 1276 void Render(Scene2D& scene)
1126 { 1277 {
1127 MacroSceneLayer* macro = NULL; 1278 MacroSceneLayer* macro = NULL;
1128 1279
1129 if (scene.HasLayer(macroLayerIndex_)) 1280 if (scene.HasLayer(macroLayerIndex_))
1181 } 1332 }
1182 1333
1183 bool SetMouseHover(const ScenePoint2D& p /* expressed in canvas coordinates */, 1334 bool SetMouseHover(const ScenePoint2D& p /* expressed in canvas coordinates */,
1184 const Scene2D& scene) 1335 const Scene2D& scene)
1185 { 1336 {
1186 bool needsRefresh = false; 1337 if (activeTool_ == Tool_None)
1187 1338 {
1188 const ScenePoint2D s = p.Apply(scene.GetCanvasToSceneTransform()); 1339 return ClearHover();
1189 1340 }
1190 for (Primitives::iterator it = primitives_.begin(); it != primitives_.end(); ++it) 1341 else
1191 { 1342 {
1192 assert(*it != NULL); 1343 bool needsRefresh = false;
1193 bool hover = (*it)->IsHit(s, scene); 1344
1194 1345 const ScenePoint2D s = p.Apply(scene.GetCanvasToSceneTransform());
1195 if ((*it)->IsHover() != hover) 1346
1196 { 1347 for (Primitives::iterator it = primitives_.begin(); it != primitives_.end(); ++it)
1197 needsRefresh = true; 1348 {
1198 } 1349 assert(*it != NULL);
1350 bool hover = (*it)->IsHit(s, scene);
1351
1352 if ((*it)->IsHover() != hover)
1353 {
1354 needsRefresh = true;
1355 }
1199 1356
1200 (*it)->SetHover(hover); 1357 (*it)->SetHover(hover);
1201 } 1358 }
1202 1359
1203 return needsRefresh; 1360 return needsRefresh;
1361 }
1204 } 1362 }
1363
1205 1364
1206 IFlexiblePointerTracker* CreateTracker(const ScenePoint2D& p /* expressed in canvas coordinates */, 1365 IFlexiblePointerTracker* CreateTracker(const ScenePoint2D& p /* expressed in canvas coordinates */,
1207 const Scene2D& scene) 1366 const Scene2D& scene)
1208 { 1367 {
1209 const ScenePoint2D s = p.Apply(scene.GetCanvasToSceneTransform()); 1368 if (activeTool_ == Tool_None)
1210 1369 {
1211 int bestDepth; 1370 return NULL;
1212 std::unique_ptr<IFlexiblePointerTracker> tracker; 1371 }
1213 1372 else
1214 for (Primitives::iterator it = primitives_.begin(); it != primitives_.end(); ++it) 1373 {
1215 { 1374 const ScenePoint2D s = p.Apply(scene.GetCanvasToSceneTransform());
1216 assert(*it != NULL); 1375
1217 if ((*it)->IsHit(s, scene)) 1376 Primitive* bestHit = NULL;
1218 { 1377
1219 if (tracker.get() == NULL || 1378 for (Primitives::iterator it = primitives_.begin(); it != primitives_.end(); ++it)
1220 bestDepth > (*it)->GetDepth()) 1379 {
1380 assert(*it != NULL);
1381 if ((*it)->IsHit(s, scene))
1221 { 1382 {
1222 tracker.reset(new EditPrimitiveTracker(**it, s, scene.GetCanvasToSceneTransform())); 1383 if (bestHit == NULL ||
1223 bestDepth = (*it)->GetDepth(); 1384 bestHit->GetDepth() > (*it)->GetDepth())
1385 {
1386 bestHit = *it;
1387 }
1224 } 1388 }
1225 } 1389 }
1226 } 1390
1227 1391 if (bestHit != NULL)
1228 if (tracker.get() == NULL) 1392 {
1229 { 1393 if (activeTool_ == Tool_Erase)
1230 tracker.reset(new CreateSegmentOrCircleTracker(*this, true /* circle */, s, scene.GetCanvasToSceneTransform())); 1394 {
1231 } 1395 DeleteMeasure(&bestHit->GetParentMeasure());
1232 1396 return new EraseTracker;
1233 return tracker.release(); 1397 }
1398 else
1399 {
1400 return new EditPrimitiveTracker(*bestHit, s, scene.GetCanvasToSceneTransform());
1401 }
1402 }
1403 else
1404 {
1405 switch (activeTool_)
1406 {
1407 case Tool_Segment:
1408 return new CreateSegmentOrCircleTracker(*this, false /* segment */, s, scene.GetCanvasToSceneTransform());
1409
1410 case Tool_Circle:
1411 return new CreateSegmentOrCircleTracker(*this, true /* circle */, s, scene.GetCanvasToSceneTransform());
1412
1413 case Tool_Angle:
1414 return new CreateAngleTracker(*this, s, scene.GetCanvasToSceneTransform());
1415
1416 default:
1417 return NULL;
1418 }
1419 }
1420 }
1234 } 1421 }
1235 }; 1422 };
1236 } 1423 }
1237 #endif 1424 #endif
1238 1425
1377 boost::shared_ptr<OrthancStone::AngleMeasureTool> angleMeasureTool(OrthancStone::AngleMeasureTool::Create(viewport)); 1564 boost::shared_ptr<OrthancStone::AngleMeasureTool> angleMeasureTool(OrthancStone::AngleMeasureTool::Create(viewport));
1378 bool angleMeasureFirst = true; 1565 bool angleMeasureFirst = true;
1379 angleMeasureTool->Disable(); 1566 angleMeasureTool->Disable();
1380 1567
1381 OrthancStone::AnnotationsOverlay overlay(10); 1568 OrthancStone::AnnotationsOverlay overlay(10);
1569 overlay.SetActiveTool(OrthancStone::AnnotationsOverlay::Tool_Angle);
1382 1570
1383 boost::shared_ptr<SdlSimpleViewerApplication> application( 1571 boost::shared_ptr<SdlSimpleViewerApplication> application(
1384 SdlSimpleViewerApplication::Create(context, viewport)); 1572 SdlSimpleViewerApplication::Create(context, viewport));
1385 1573
1386 OrthancStone::DicomSource source; 1574 OrthancStone::DicomSource source;