Mercurial > hg > orthanc-stone
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; |