Mercurial > hg > orthanc-stone
comparison Applications/Samples/SingleFrameEditorApplication.h @ 347:cd65103c9172 am-2
RotateBitmapTracker
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 23 Oct 2018 10:50:46 +0200 |
parents | c2e040ea8fbe |
children | dadee0f7f1b3 |
comparison
equal
deleted
inserted
replaced
346:c2e040ea8fbe | 347:cd65103c9172 |
---|---|
47 { | 47 { |
48 public: | 48 public: |
49 typedef OriginMessage<MessageType_Widget_GeometryChanged, BitmapStack> GeometryChangedMessage; | 49 typedef OriginMessage<MessageType_Widget_GeometryChanged, BitmapStack> GeometryChangedMessage; |
50 typedef OriginMessage<MessageType_Widget_ContentChanged, BitmapStack> ContentChangedMessage; | 50 typedef OriginMessage<MessageType_Widget_ContentChanged, BitmapStack> ContentChangedMessage; |
51 | 51 |
52 private: | |
53 class Bitmap : public boost::noncopyable | 52 class Bitmap : public boost::noncopyable |
54 { | 53 { |
55 private: | 54 private: |
56 bool visible_; | 55 bool visible_; |
57 bool hasSize_; | 56 bool hasSize_; |
124 | 123 |
125 void UpdateTransform() | 124 void UpdateTransform() |
126 { | 125 { |
127 transform_ = CreateScalingMatrix(pixelSpacingX_, pixelSpacingY_); | 126 transform_ = CreateScalingMatrix(pixelSpacingX_, pixelSpacingY_); |
128 | 127 |
129 double centerX = static_cast<double>(width_) / 2.0; | 128 double centerX, centerY; |
130 double centerY = static_cast<double>(height_) / 2.0; | 129 GetCenter(centerX, centerY); |
131 ApplyTransform(centerX, centerY, transform_); | |
132 | 130 |
133 transform_ = LinearAlgebra::Product( | 131 transform_ = LinearAlgebra::Product( |
134 CreateOffsetMatrix(panX_ + centerX, panY_ + centerY), | 132 CreateOffsetMatrix(panX_ + centerX, panY_ + centerY), |
135 CreateRotationMatrix(angle_), | 133 CreateRotationMatrix(angle_), |
136 CreateOffsetMatrix(-centerX, -centerY), transform_); | 134 CreateOffsetMatrix(-centerX, -centerY), |
135 transform_); | |
137 } | 136 } |
138 | 137 |
139 | 138 |
140 void AddToExtent(Extent2D& extent, | 139 void AddToExtent(Extent2D& extent, |
141 double x, | 140 double x, |
155 hasCrop_(false), | 154 hasCrop_(false), |
156 pixelSpacingX_(1), | 155 pixelSpacingX_(1), |
157 pixelSpacingY_(1), | 156 pixelSpacingY_(1), |
158 panX_(0), | 157 panX_(0), |
159 panY_(0), | 158 panY_(0), |
160 angle_(45.0 / 180.0 * boost::math::constants::pi<double>()) | 159 angle_(0) |
161 { | 160 { |
162 UpdateTransform(); | 161 UpdateTransform(); |
163 } | 162 } |
164 | 163 |
165 virtual ~Bitmap() | 164 virtual ~Bitmap() |
355 { | 354 { |
356 return transform_; | 355 return transform_; |
357 } | 356 } |
358 | 357 |
359 | 358 |
359 void GetCenter(double& centerX, | |
360 double& centerY) const | |
361 { | |
362 centerX = static_cast<double>(width_) / 2.0; | |
363 centerY = static_cast<double>(height_) / 2.0; | |
364 ApplyTransform(centerX, centerY, transform_); | |
365 } | |
366 | |
367 | |
360 void DrawBorders(CairoContext& context, | 368 void DrawBorders(CairoContext& context, |
361 double zoom) | 369 double zoom) |
362 { | 370 { |
363 unsigned int cx, cy, width, height; | 371 unsigned int cx, cy, width, height; |
364 GetCrop(cx, cy, width, height); | 372 GetCrop(cx, cy, width, height); |
400 cairo_stroke(cr); | 408 cairo_stroke(cr); |
401 } | 409 } |
402 }; | 410 }; |
403 | 411 |
404 | 412 |
405 | 413 class BitmapAccessor : public boost::noncopyable |
414 { | |
415 private: | |
416 size_t index_; | |
417 Bitmap* bitmap_; | |
418 | |
419 public: | |
420 BitmapAccessor(BitmapStack& stack, | |
421 size_t index) : | |
422 index_(index) | |
423 { | |
424 Bitmaps::iterator bitmap = stack.bitmaps_.find(index); | |
425 if (bitmap == stack.bitmaps_.end()) | |
426 { | |
427 bitmap_ = NULL; | |
428 } | |
429 else | |
430 { | |
431 assert(bitmap->second != NULL); | |
432 bitmap_ = bitmap->second; | |
433 } | |
434 } | |
435 | |
436 BitmapAccessor(BitmapStack& stack, | |
437 double x, | |
438 double y) : | |
439 index_(0) // Dummy initialization | |
440 { | |
441 if (stack.LookupBitmap(index_, x, y)) | |
442 { | |
443 Bitmaps::iterator bitmap = stack.bitmaps_.find(index_); | |
444 | |
445 if (bitmap == stack.bitmaps_.end()) | |
446 { | |
447 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
448 } | |
449 else | |
450 { | |
451 assert(bitmap->second != NULL); | |
452 bitmap_ = bitmap->second; | |
453 } | |
454 } | |
455 else | |
456 { | |
457 bitmap_ = NULL; | |
458 } | |
459 } | |
460 | |
461 void Invalidate() | |
462 { | |
463 bitmap_ = NULL; | |
464 } | |
465 | |
466 bool IsValid() const | |
467 { | |
468 return bitmap_ != NULL; | |
469 } | |
470 | |
471 size_t GetIndex() const | |
472 { | |
473 if (IsValid()) | |
474 { | |
475 return index_; | |
476 } | |
477 else | |
478 { | |
479 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
480 } | |
481 } | |
482 | |
483 Bitmap& GetBitmap() const | |
484 { | |
485 if (IsValid()) | |
486 { | |
487 return *bitmap_; | |
488 } | |
489 else | |
490 { | |
491 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
492 } | |
493 } | |
494 }; | |
495 | |
496 | |
497 private: | |
406 class DicomBitmap : public Bitmap | 498 class DicomBitmap : public Bitmap |
407 { | 499 { |
408 private: | 500 private: |
409 std::auto_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData | 501 std::auto_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData |
410 std::auto_ptr<DicomFrameConverter> converter_; | 502 std::auto_ptr<DicomFrameConverter> converter_; |
744 } | 836 } |
745 | 837 |
746 | 838 |
747 void OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) | 839 void OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) |
748 { | 840 { |
749 size_t index = dynamic_cast<Orthanc::SingleValueObject<size_t>*>(message.Payload.get())->GetValue(); | 841 size_t index = dynamic_cast<Orthanc::SingleValueObject<size_t>*>(message.Payload.get())->GetValue(); |
750 | 842 |
751 printf("JSON received: [%s] (%ld bytes) for bitmap %ld\n", | 843 printf("JSON received: [%s] (%ld bytes) for bitmap %ld\n", |
752 message.Uri.c_str(), message.AnswerSize, index); | 844 message.Uri.c_str(), message.AnswerSize, index); |
753 | 845 |
754 Bitmaps::iterator bitmap = bitmaps_.find(index); | 846 Bitmaps::iterator bitmap = bitmaps_.find(index); |
773 } | 865 } |
774 | 866 |
775 | 867 |
776 void OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) | 868 void OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) |
777 { | 869 { |
778 size_t index = dynamic_cast<Orthanc::SingleValueObject<size_t>*>(message.Payload.get())->GetValue(); | 870 size_t index = dynamic_cast<Orthanc::SingleValueObject<size_t>*>(message.Payload.get())->GetValue(); |
779 | 871 |
780 printf("Frame received: [%s] (%ld bytes) for bitmap %ld\n", | 872 printf("Frame received: [%s] (%ld bytes) for bitmap %ld\n", |
781 message.Uri.c_str(), message.AnswerSize, index); | 873 message.Uri.c_str(), message.AnswerSize, index); |
782 | 874 |
783 Bitmaps::iterator bitmap = bitmaps_.find(index); | 875 Bitmaps::iterator bitmap = bitmaps_.find(index); |
854 } | 946 } |
855 | 947 |
856 return false; | 948 return false; |
857 } | 949 } |
858 | 950 |
859 | |
860 void SetPan(size_t index, | |
861 double panX, | |
862 double panY) | |
863 { | |
864 Bitmaps::iterator bitmap = bitmaps_.find(index); | |
865 if (bitmap != bitmaps_.end()) | |
866 { | |
867 assert(bitmap->second != NULL); | |
868 bitmap->second->SetPan(panX, panY); | |
869 } | |
870 } | |
871 | |
872 | |
873 void GetPan(double& panX, | |
874 double& panY, | |
875 size_t index) const | |
876 { | |
877 Bitmaps::const_iterator bitmap = bitmaps_.find(index); | |
878 if (bitmap != bitmaps_.end()) | |
879 { | |
880 assert(bitmap->second != NULL); | |
881 panX = bitmap->second->GetPanX(); | |
882 panY = bitmap->second->GetPanY(); | |
883 } | |
884 else | |
885 { | |
886 panX = 0; | |
887 panY = 0; | |
888 } | |
889 } | |
890 | |
891 | |
892 void DrawControls(CairoSurface& surface, | 951 void DrawControls(CairoSurface& surface, |
893 const ViewportGeometry& view) | 952 const ViewportGeometry& view) |
894 { | 953 { |
895 if (hasSelection_) | 954 if (hasSelection_) |
896 { | 955 { |
910 | 969 |
911 | 970 |
912 class BitmapStackInteractor : public IWorldSceneInteractor | 971 class BitmapStackInteractor : public IWorldSceneInteractor |
913 { | 972 { |
914 private: | 973 private: |
974 enum Tool | |
975 { | |
976 Tool_Move, | |
977 Tool_Rotate | |
978 }; | |
979 | |
980 | |
915 BitmapStack& stack_; | 981 BitmapStack& stack_; |
916 | 982 Tool tool_; |
917 | 983 |
984 | |
985 class RotateBitmapTracker : public IWorldSceneMouseTracker | |
986 { | |
987 private: | |
988 BitmapStack::BitmapAccessor accessor_; | |
989 double centerX_; | |
990 double centerY_; | |
991 double originalAngle_; | |
992 double clickAngle_; | |
993 bool roundAngles_; | |
994 | |
995 bool ComputeAngle(double& angle /* out */, | |
996 double sceneX, | |
997 double sceneY) | |
998 { | |
999 Vector u; | |
1000 LinearAlgebra::AssignVector(u, sceneX - centerX_, sceneY - centerY_); | |
1001 | |
1002 double nu = boost::numeric::ublas::norm_2(u); | |
1003 | |
1004 if (!LinearAlgebra::IsCloseToZero(nu)) | |
1005 { | |
1006 u /= nu; | |
1007 angle = atan2(u[1], u[0]); | |
1008 return true; | |
1009 } | |
1010 else | |
1011 { | |
1012 return false; | |
1013 } | |
1014 } | |
1015 | |
1016 | |
1017 public: | |
1018 RotateBitmapTracker(BitmapStack& stack, | |
1019 const ViewportGeometry& view, | |
1020 size_t bitmap, | |
1021 double x, | |
1022 double y, | |
1023 bool roundAngles) : | |
1024 accessor_(stack, bitmap), | |
1025 roundAngles_(roundAngles) | |
1026 { | |
1027 if (accessor_.IsValid()) | |
1028 { | |
1029 accessor_.GetBitmap().GetCenter(centerX_, centerY_); | |
1030 originalAngle_ = accessor_.GetBitmap().GetAngle(); | |
1031 | |
1032 double sceneX, sceneY; | |
1033 view.MapDisplayToScene(sceneX, sceneY, x, y); | |
1034 | |
1035 if (!ComputeAngle(clickAngle_, x, y)) | |
1036 { | |
1037 accessor_.Invalidate(); | |
1038 } | |
1039 } | |
1040 } | |
1041 | |
1042 virtual bool HasRender() const | |
1043 { | |
1044 return false; | |
1045 } | |
1046 | |
1047 virtual void Render(CairoContext& context, | |
1048 double zoom) | |
1049 { | |
1050 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
1051 } | |
1052 | |
1053 virtual void MouseUp() | |
1054 { | |
1055 } | |
1056 | |
1057 virtual void MouseMove(int displayX, | |
1058 int displayY, | |
1059 double sceneX, | |
1060 double sceneY) | |
1061 { | |
1062 static const double ROUND_ANGLE = 15.0 / 180.0 * boost::math::constants::pi<double>(); | |
1063 | |
1064 double angle; | |
1065 | |
1066 if (accessor_.IsValid() && | |
1067 ComputeAngle(angle, sceneX, sceneY)) | |
1068 { | |
1069 angle = angle - clickAngle_ + originalAngle_; | |
1070 | |
1071 if (roundAngles_) | |
1072 { | |
1073 angle = round(angle / ROUND_ANGLE) * ROUND_ANGLE; | |
1074 } | |
1075 | |
1076 accessor_.GetBitmap().SetAngle(angle); | |
1077 } | |
1078 } | |
1079 }; | |
1080 | |
1081 | |
918 class MoveBitmapTracker : public IWorldSceneMouseTracker | 1082 class MoveBitmapTracker : public IWorldSceneMouseTracker |
919 { | 1083 { |
920 private: | 1084 private: |
921 BitmapStack& stack_; | 1085 BitmapStack::BitmapAccessor accessor_; |
922 size_t bitmap_; | 1086 double clickX_; |
923 double clickX_; | 1087 double clickY_; |
924 double clickY_; | 1088 double panX_; |
925 double panX_; | 1089 double panY_; |
926 double panY_; | 1090 bool oneAxis_; |
927 bool oneAxis_; | |
928 | 1091 |
929 public: | 1092 public: |
930 MoveBitmapTracker(BitmapStack& stack, | 1093 MoveBitmapTracker(BitmapStack& stack, |
931 size_t bitmap, | 1094 size_t bitmap, |
932 double x, | 1095 double x, |
933 double y, | 1096 double y, |
934 bool oneAxis) : | 1097 bool oneAxis) : |
935 stack_(stack), | 1098 accessor_(stack, bitmap), |
936 bitmap_(bitmap), | |
937 clickX_(x), | 1099 clickX_(x), |
938 clickY_(y), | 1100 clickY_(y), |
939 oneAxis_(oneAxis) | 1101 oneAxis_(oneAxis) |
940 { | 1102 { |
941 stack.GetPan(panX_, panY_, bitmap_); | 1103 if (accessor_.IsValid()) |
1104 { | |
1105 panX_ = accessor_.GetBitmap().GetPanX(); | |
1106 panY_ = accessor_.GetBitmap().GetPanY(); | |
1107 } | |
942 } | 1108 } |
943 | 1109 |
944 virtual bool HasRender() const | 1110 virtual bool HasRender() const |
945 { | 1111 { |
946 return false; | 1112 return false; |
959 virtual void MouseMove(int displayX, | 1125 virtual void MouseMove(int displayX, |
960 int displayY, | 1126 int displayY, |
961 double sceneX, | 1127 double sceneX, |
962 double sceneY) | 1128 double sceneY) |
963 { | 1129 { |
964 double dx = sceneX - clickX_; | 1130 if (accessor_.IsValid()) |
965 double dy = sceneY - clickY_; | 1131 { |
966 | 1132 double dx = sceneX - clickX_; |
967 if (oneAxis_) | 1133 double dy = sceneY - clickY_; |
968 { | 1134 |
969 if (fabs(dx) > fabs(dy)) | 1135 if (oneAxis_) |
970 { | 1136 { |
971 stack_.SetPan(bitmap_, dx + panX_, panY_); | 1137 if (fabs(dx) > fabs(dy)) |
1138 { | |
1139 accessor_.GetBitmap().SetPan(dx + panX_, panY_); | |
1140 } | |
1141 else | |
1142 { | |
1143 accessor_.GetBitmap().SetPan(panX_, dy + panY_); | |
1144 } | |
972 } | 1145 } |
973 else | 1146 else |
974 { | 1147 { |
975 stack_.SetPan(bitmap_, panX_, dy + panY_); | 1148 accessor_.GetBitmap().SetPan(dx + panX_, dy + panY_); |
976 } | 1149 } |
977 } | |
978 else | |
979 { | |
980 stack_.SetPan(bitmap_, dx + panX_, dy + panY_); | |
981 } | 1150 } |
982 } | 1151 } |
983 }; | 1152 }; |
984 | 1153 |
985 | 1154 |
986 public: | 1155 public: |
987 BitmapStackInteractor(BitmapStack& stack) : | 1156 BitmapStackInteractor(BitmapStack& stack) : |
988 stack_(stack) | 1157 stack_(stack), |
1158 tool_(Tool_Move) | |
989 { | 1159 { |
990 } | 1160 } |
991 | 1161 |
992 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, | 1162 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, |
993 const ViewportGeometry& view, | 1163 const ViewportGeometry& view, |
1007 | 1177 |
1008 size_t selected; | 1178 size_t selected; |
1009 if (stack_.GetSelectedBitmap(selected) && | 1179 if (stack_.GetSelectedBitmap(selected) && |
1010 bitmap == selected) | 1180 bitmap == selected) |
1011 { | 1181 { |
1012 return new MoveBitmapTracker(stack_, bitmap, x, y, | 1182 switch (tool_) |
1013 (modifiers & KeyboardModifiers_Shift)); | 1183 { |
1184 case Tool_Move: | |
1185 return new MoveBitmapTracker(stack_, bitmap, x, y, | |
1186 (modifiers & KeyboardModifiers_Shift)); | |
1187 | |
1188 case Tool_Rotate: | |
1189 return new RotateBitmapTracker(stack_, view, bitmap, x, y, | |
1190 (modifiers & KeyboardModifiers_Shift)); | |
1191 | |
1192 default: | |
1193 return NULL; | |
1194 } | |
1014 } | 1195 } |
1015 else | 1196 else |
1016 { | 1197 { |
1017 stack_.Select(bitmap); | 1198 stack_.Select(bitmap); |
1018 return NULL; | 1199 return NULL; |
1051 KeyboardKeys key, | 1232 KeyboardKeys key, |
1052 char keyChar, | 1233 char keyChar, |
1053 KeyboardModifiers modifiers, | 1234 KeyboardModifiers modifiers, |
1054 IStatusBar* statusBar) | 1235 IStatusBar* statusBar) |
1055 { | 1236 { |
1056 if (keyChar == 's') | 1237 switch (keyChar) |
1057 widget.FitContent(); | 1238 { |
1239 case 's': | |
1240 widget.FitContent(); | |
1241 break; | |
1242 | |
1243 case 'm': | |
1244 tool_ = Tool_Move; | |
1245 break; | |
1246 | |
1247 case 'r': | |
1248 tool_ = Tool_Rotate; | |
1249 break; | |
1250 | |
1251 default: | |
1252 break; | |
1253 } | |
1058 } | 1254 } |
1059 }; | 1255 }; |
1060 | 1256 |
1061 | 1257 |
1062 | 1258 |