Mercurial > hg > orthanc-stone
comparison Applications/Samples/SingleFrameEditorApplication.h @ 354:f806779bd40f am-2
UndoRedoStack
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 29 Oct 2018 13:53:02 +0100 |
parents | 92a159481900 |
children | d2468dd75b3f |
comparison
equal
deleted
inserted
replaced
353:92a159481900 | 354:f806779bd40f |
---|---|
55 Corner_TopLeft, | 55 Corner_TopLeft, |
56 Corner_TopRight, | 56 Corner_TopRight, |
57 Corner_BottomLeft, | 57 Corner_BottomLeft, |
58 Corner_BottomRight | 58 Corner_BottomRight |
59 }; | 59 }; |
60 | 60 |
61 | 61 |
62 class Bitmap : public boost::noncopyable | 62 class Bitmap : public boost::noncopyable |
63 { | 63 { |
64 private: | 64 private: |
65 size_t index_; | 65 size_t index_; |
596 | 596 |
597 | 597 |
598 class BitmapAccessor : public boost::noncopyable | 598 class BitmapAccessor : public boost::noncopyable |
599 { | 599 { |
600 private: | 600 private: |
601 size_t index_; | 601 BitmapStack& stack_; |
602 Bitmap* bitmap_; | 602 size_t index_; |
603 Bitmap* bitmap_; | |
603 | 604 |
604 public: | 605 public: |
605 BitmapAccessor(BitmapStack& stack, | 606 BitmapAccessor(BitmapStack& stack, |
606 size_t index) : | 607 size_t index) : |
608 stack_(stack), | |
607 index_(index) | 609 index_(index) |
608 { | 610 { |
609 Bitmaps::iterator bitmap = stack.bitmaps_.find(index); | 611 Bitmaps::iterator bitmap = stack.bitmaps_.find(index); |
610 if (bitmap == stack.bitmaps_.end()) | 612 if (bitmap == stack.bitmaps_.end()) |
611 { | 613 { |
619 } | 621 } |
620 | 622 |
621 BitmapAccessor(BitmapStack& stack, | 623 BitmapAccessor(BitmapStack& stack, |
622 double x, | 624 double x, |
623 double y) : | 625 double y) : |
626 stack_(stack), | |
624 index_(0) // Dummy initialization | 627 index_(0) // Dummy initialization |
625 { | 628 { |
626 if (stack.LookupBitmap(index_, x, y)) | 629 if (stack.LookupBitmap(index_, x, y)) |
627 { | 630 { |
628 Bitmaps::iterator bitmap = stack.bitmaps_.find(index_); | 631 Bitmaps::iterator bitmap = stack.bitmaps_.find(index_); |
651 bool IsValid() const | 654 bool IsValid() const |
652 { | 655 { |
653 return bitmap_ != NULL; | 656 return bitmap_ != NULL; |
654 } | 657 } |
655 | 658 |
659 BitmapStack& GetStack() const | |
660 { | |
661 if (IsValid()) | |
662 { | |
663 return stack_; | |
664 } | |
665 else | |
666 { | |
667 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
668 } | |
669 } | |
670 | |
656 size_t GetIndex() const | 671 size_t GetIndex() const |
657 { | 672 { |
658 if (IsValid()) | 673 if (IsValid()) |
659 { | 674 { |
660 return index_; | 675 return index_; |
675 { | 690 { |
676 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | 691 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); |
677 } | 692 } |
678 } | 693 } |
679 }; | 694 }; |
680 | |
681 | |
682 private: | |
683 class DicomBitmap : public Bitmap | |
684 { | |
685 private: | |
686 std::auto_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData | |
687 std::auto_ptr<DicomFrameConverter> converter_; | |
688 std::auto_ptr<Orthanc::ImageAccessor> converted_; // Float32 or RGB24 | |
689 | |
690 static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag) | |
691 { | |
692 return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement()); | |
693 } | |
694 | |
695 | |
696 void ApplyConverter() | |
697 { | |
698 if (source_.get() != NULL && | |
699 converter_.get() != NULL) | |
700 { | |
701 converted_.reset(converter_->ConvertFrame(*source_)); | |
702 } | |
703 } | |
704 | |
705 public: | |
706 DicomBitmap(size_t index) : | |
707 Bitmap(index) | |
708 { | |
709 } | |
710 | |
711 void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset) | |
712 { | |
713 converter_.reset(new DicomFrameConverter); | |
714 converter_->ReadParameters(dataset); | |
715 ApplyConverter(); | |
716 | |
717 std::string tmp; | |
718 Vector pixelSpacing; | |
719 | |
720 if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PIXEL_SPACING)) && | |
721 LinearAlgebra::ParseVector(pixelSpacing, tmp) && | |
722 pixelSpacing.size() == 2) | |
723 { | |
724 SetPixelSpacing(pixelSpacing[0], pixelSpacing[1]); | |
725 } | |
726 | |
727 //SetPan(-0.5 * GetPixelSpacingX(), -0.5 * GetPixelSpacingY()); | |
728 | |
729 static unsigned int c = 0; | |
730 if (c == 0) | |
731 { | |
732 SetPan(400, 0); | |
733 c ++; | |
734 } | |
735 | |
736 OrthancPlugins::DicomDatasetReader reader(dataset); | |
737 | |
738 unsigned int width, height; | |
739 if (!reader.GetUnsignedIntegerValue(width, ConvertTag(Orthanc::DICOM_TAG_COLUMNS)) || | |
740 !reader.GetUnsignedIntegerValue(height, ConvertTag(Orthanc::DICOM_TAG_ROWS))) | |
741 { | |
742 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
743 } | |
744 else | |
745 { | |
746 SetSize(width, height); | |
747 } | |
748 } | |
749 | |
750 | |
751 void SetSourceImage(Orthanc::ImageAccessor* image) // Takes ownership | |
752 { | |
753 std::auto_ptr<Orthanc::ImageAccessor> raii(image); | |
754 | |
755 if (image == NULL) | |
756 { | |
757 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | |
758 } | |
759 | |
760 SetSize(image->GetWidth(), image->GetHeight()); | |
761 | |
762 source_ = raii; | |
763 ApplyConverter(); | |
764 } | |
765 | |
766 | |
767 virtual void Render(Orthanc::ImageAccessor& buffer, | |
768 const ViewportGeometry& view, | |
769 ImageInterpolation interpolation) const | |
770 { | |
771 if (converted_.get() != NULL) | |
772 { | |
773 if (converted_->GetFormat() != Orthanc::PixelFormat_Float32) | |
774 { | |
775 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
776 } | |
777 | |
778 unsigned int cropX, cropY, cropWidth, cropHeight; | |
779 GetCrop(cropX, cropY, cropWidth, cropHeight); | |
780 | |
781 Matrix m = LinearAlgebra::Product(view.GetMatrix(), | |
782 GetTransform(), | |
783 CreateOffsetMatrix(cropX, cropY)); | |
784 | |
785 Orthanc::ImageAccessor cropped; | |
786 converted_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); | |
787 | |
788 ApplyProjectiveTransform(buffer, cropped, m, interpolation, false); | |
789 } | |
790 } | |
791 | |
792 | |
793 virtual bool GetDefaultWindowing(float& center, | |
794 float& width) const | |
795 { | |
796 if (converter_.get() != NULL && | |
797 converter_->HasDefaultWindow()) | |
798 { | |
799 center = static_cast<float>(converter_->GetDefaultWindowCenter()); | |
800 width = static_cast<float>(converter_->GetDefaultWindowWidth()); | |
801 return true; | |
802 } | |
803 else | |
804 { | |
805 return false; | |
806 } | |
807 } | |
808 }; | |
809 | |
810 | |
811 | 695 |
812 | 696 |
813 class AlphaBitmap : public Bitmap | 697 class AlphaBitmap : public Bitmap |
814 { | 698 { |
815 private: | 699 private: |
913 } | 797 } |
914 } | 798 } |
915 }; | 799 }; |
916 | 800 |
917 | 801 |
802 | |
803 private: | |
804 class DicomBitmap : public Bitmap | |
805 { | |
806 private: | |
807 std::auto_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData | |
808 std::auto_ptr<DicomFrameConverter> converter_; | |
809 std::auto_ptr<Orthanc::ImageAccessor> converted_; // Float32 or RGB24 | |
810 | |
811 static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag) | |
812 { | |
813 return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement()); | |
814 } | |
815 | |
816 | |
817 void ApplyConverter() | |
818 { | |
819 if (source_.get() != NULL && | |
820 converter_.get() != NULL) | |
821 { | |
822 converted_.reset(converter_->ConvertFrame(*source_)); | |
823 } | |
824 } | |
825 | |
826 public: | |
827 DicomBitmap(size_t index) : | |
828 Bitmap(index) | |
829 { | |
830 } | |
831 | |
832 void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset) | |
833 { | |
834 converter_.reset(new DicomFrameConverter); | |
835 converter_->ReadParameters(dataset); | |
836 ApplyConverter(); | |
837 | |
838 std::string tmp; | |
839 Vector pixelSpacing; | |
840 | |
841 if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PIXEL_SPACING)) && | |
842 LinearAlgebra::ParseVector(pixelSpacing, tmp) && | |
843 pixelSpacing.size() == 2) | |
844 { | |
845 SetPixelSpacing(pixelSpacing[0], pixelSpacing[1]); | |
846 } | |
847 | |
848 //SetPan(-0.5 * GetPixelSpacingX(), -0.5 * GetPixelSpacingY()); | |
849 | |
850 static unsigned int c = 0; | |
851 if (c == 0) | |
852 { | |
853 SetPan(400, 0); | |
854 c ++; | |
855 } | |
856 | |
857 OrthancPlugins::DicomDatasetReader reader(dataset); | |
858 | |
859 unsigned int width, height; | |
860 if (!reader.GetUnsignedIntegerValue(width, ConvertTag(Orthanc::DICOM_TAG_COLUMNS)) || | |
861 !reader.GetUnsignedIntegerValue(height, ConvertTag(Orthanc::DICOM_TAG_ROWS))) | |
862 { | |
863 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
864 } | |
865 else | |
866 { | |
867 SetSize(width, height); | |
868 } | |
869 } | |
870 | |
871 | |
872 void SetSourceImage(Orthanc::ImageAccessor* image) // Takes ownership | |
873 { | |
874 std::auto_ptr<Orthanc::ImageAccessor> raii(image); | |
875 | |
876 if (image == NULL) | |
877 { | |
878 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | |
879 } | |
880 | |
881 SetSize(image->GetWidth(), image->GetHeight()); | |
882 | |
883 source_ = raii; | |
884 ApplyConverter(); | |
885 } | |
886 | |
887 | |
888 virtual void Render(Orthanc::ImageAccessor& buffer, | |
889 const ViewportGeometry& view, | |
890 ImageInterpolation interpolation) const | |
891 { | |
892 if (converted_.get() != NULL) | |
893 { | |
894 if (converted_->GetFormat() != Orthanc::PixelFormat_Float32) | |
895 { | |
896 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
897 } | |
898 | |
899 unsigned int cropX, cropY, cropWidth, cropHeight; | |
900 GetCrop(cropX, cropY, cropWidth, cropHeight); | |
901 | |
902 Matrix m = LinearAlgebra::Product(view.GetMatrix(), | |
903 GetTransform(), | |
904 CreateOffsetMatrix(cropX, cropY)); | |
905 | |
906 Orthanc::ImageAccessor cropped; | |
907 converted_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); | |
908 | |
909 ApplyProjectiveTransform(buffer, cropped, m, interpolation, false); | |
910 } | |
911 } | |
912 | |
913 | |
914 virtual bool GetDefaultWindowing(float& center, | |
915 float& width) const | |
916 { | |
917 if (converter_.get() != NULL && | |
918 converter_->HasDefaultWindow()) | |
919 { | |
920 center = static_cast<float>(converter_->GetDefaultWindowCenter()); | |
921 width = static_cast<float>(converter_->GetDefaultWindowWidth()); | |
922 return true; | |
923 } | |
924 else | |
925 { | |
926 return false; | |
927 } | |
928 } | |
929 }; | |
930 | |
931 | |
932 | |
918 | 933 |
919 typedef std::map<size_t, Bitmap*> Bitmaps; | 934 typedef std::map<size_t, Bitmap*> Bitmaps; |
920 | 935 |
921 OrthancApiClient& orthanc_; | 936 OrthancApiClient& orthanc_; |
922 size_t countBitmaps_; | 937 size_t countBitmaps_; |
1058 | 1073 |
1059 return *ptr; | 1074 return *ptr; |
1060 } | 1075 } |
1061 | 1076 |
1062 | 1077 |
1063 size_t LoadFrame(const std::string& instance, | 1078 Bitmap& LoadFrame(const std::string& instance, |
1064 unsigned int frame, | 1079 unsigned int frame, |
1065 bool httpCompression) | 1080 bool httpCompression) |
1066 { | 1081 { |
1067 size_t bitmap = countBitmaps_++; | 1082 size_t bitmap = countBitmaps_++; |
1068 | 1083 |
1069 bitmaps_[bitmap] = new DicomBitmap(bitmap); | 1084 bitmaps_[bitmap] = new DicomBitmap(bitmap); |
1070 | 1085 |
1071 | |
1072 { | 1086 { |
1073 IWebService::Headers headers; | 1087 IWebService::Headers headers; |
1074 std::string uri = "/instances/" + instance + "/tags"; | 1088 std::string uri = "/instances/" + instance + "/tags"; |
1075 orthanc_.GetBinaryAsync(uri, headers, | 1089 orthanc_.GetBinaryAsync(uri, headers, |
1076 new Callable<BitmapStack, OrthancApiClient::BinaryResponseReadyMessage> | 1090 new Callable<BitmapStack, OrthancApiClient::BinaryResponseReadyMessage> |
1092 new Callable<BitmapStack, OrthancApiClient::BinaryResponseReadyMessage> | 1106 new Callable<BitmapStack, OrthancApiClient::BinaryResponseReadyMessage> |
1093 (*this, &BitmapStack::OnFrameReceived), NULL, | 1107 (*this, &BitmapStack::OnFrameReceived), NULL, |
1094 new Orthanc::SingleValueObject<size_t>(bitmap)); | 1108 new Orthanc::SingleValueObject<size_t>(bitmap)); |
1095 } | 1109 } |
1096 | 1110 |
1097 return bitmap; | 1111 return *bitmaps_[bitmap]; |
1098 } | 1112 } |
1099 | 1113 |
1100 | 1114 |
1101 void OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) | 1115 void OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) |
1102 { | 1116 { |
1223 | 1237 |
1224 context.SetSourceColor(255, 0, 0); | 1238 context.SetSourceColor(255, 0, 0); |
1225 view.ApplyTransform(context); | 1239 view.ApplyTransform(context); |
1226 bitmap->second->DrawBorders(context, view.GetZoom()); | 1240 bitmap->second->DrawBorders(context, view.GetZoom()); |
1227 } | 1241 } |
1242 } | |
1243 } | |
1244 }; | |
1245 | |
1246 | |
1247 class UndoRedoStack : public boost::noncopyable | |
1248 { | |
1249 public: | |
1250 class ICommand : public boost::noncopyable | |
1251 { | |
1252 public: | |
1253 virtual ~ICommand() | |
1254 { | |
1255 } | |
1256 | |
1257 virtual void Undo() const = 0; | |
1258 | |
1259 virtual void Redo() const = 0; | |
1260 }; | |
1261 | |
1262 private: | |
1263 typedef std::list<ICommand*> Stack; | |
1264 | |
1265 Stack stack_; | |
1266 Stack::iterator current_; | |
1267 | |
1268 void Clear(Stack::iterator from) | |
1269 { | |
1270 for (Stack::iterator it = from; it != stack_.end(); ++it) | |
1271 { | |
1272 assert(*it != NULL); | |
1273 delete *it; | |
1274 } | |
1275 | |
1276 stack_.erase(from, stack_.end()); | |
1277 } | |
1278 | |
1279 public: | |
1280 UndoRedoStack() : | |
1281 current_(stack_.end()) | |
1282 { | |
1283 } | |
1284 | |
1285 ~UndoRedoStack() | |
1286 { | |
1287 Clear(stack_.begin()); | |
1288 } | |
1289 | |
1290 void Add(ICommand* command) | |
1291 { | |
1292 if (command == NULL) | |
1293 { | |
1294 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | |
1295 } | |
1296 | |
1297 Clear(current_); | |
1298 | |
1299 stack_.push_back(command); | |
1300 current_ = stack_.end(); | |
1301 } | |
1302 | |
1303 void Undo() | |
1304 { | |
1305 if (current_ != stack_.begin()) | |
1306 { | |
1307 --current_; | |
1308 | |
1309 assert(*current_ != NULL); | |
1310 (*current_)->Undo(); | |
1311 } | |
1312 } | |
1313 | |
1314 void Redo() | |
1315 { | |
1316 if (current_ != stack_.end()) | |
1317 { | |
1318 assert(*current_ != NULL); | |
1319 (*current_)->Redo(); | |
1320 | |
1321 ++current_; | |
1322 } | |
1323 } | |
1324 }; | |
1325 | |
1326 | |
1327 class BitmapCommandBase : public UndoRedoStack::ICommand | |
1328 { | |
1329 private: | |
1330 BitmapStack& stack_; | |
1331 size_t bitmap_; | |
1332 | |
1333 protected: | |
1334 virtual void UndoInternal(BitmapStack::Bitmap& bitmap) const = 0; | |
1335 | |
1336 virtual void RedoInternal(BitmapStack::Bitmap& bitmap) const = 0; | |
1337 | |
1338 public: | |
1339 BitmapCommandBase(BitmapStack& stack, | |
1340 size_t bitmap) : | |
1341 stack_(stack), | |
1342 bitmap_(bitmap) | |
1343 { | |
1344 } | |
1345 | |
1346 BitmapCommandBase(BitmapStack::BitmapAccessor& accessor) : | |
1347 stack_(accessor.GetStack()), | |
1348 bitmap_(accessor.GetIndex()) | |
1349 { | |
1350 } | |
1351 | |
1352 virtual void Undo() const | |
1353 { | |
1354 BitmapStack::BitmapAccessor accessor(stack_, bitmap_); | |
1355 | |
1356 if (accessor.IsValid()) | |
1357 { | |
1358 UndoInternal(accessor.GetBitmap()); | |
1359 } | |
1360 } | |
1361 | |
1362 virtual void Redo() const | |
1363 { | |
1364 BitmapStack::BitmapAccessor accessor(stack_, bitmap_); | |
1365 | |
1366 if (accessor.IsValid()) | |
1367 { | |
1368 RedoInternal(accessor.GetBitmap()); | |
1369 } | |
1370 } | |
1371 }; | |
1372 | |
1373 | |
1374 class RotateBitmapTracker : public IWorldSceneMouseTracker | |
1375 { | |
1376 private: | |
1377 UndoRedoStack& undoRedoStack_; | |
1378 BitmapStack::BitmapAccessor accessor_; | |
1379 double centerX_; | |
1380 double centerY_; | |
1381 double originalAngle_; | |
1382 double clickAngle_; | |
1383 bool roundAngles_; | |
1384 | |
1385 bool ComputeAngle(double& angle /* out */, | |
1386 double sceneX, | |
1387 double sceneY) const | |
1388 { | |
1389 Vector u; | |
1390 LinearAlgebra::AssignVector(u, sceneX - centerX_, sceneY - centerY_); | |
1391 | |
1392 double nu = boost::numeric::ublas::norm_2(u); | |
1393 | |
1394 if (!LinearAlgebra::IsCloseToZero(nu)) | |
1395 { | |
1396 u /= nu; | |
1397 angle = atan2(u[1], u[0]); | |
1398 return true; | |
1399 } | |
1400 else | |
1401 { | |
1402 return false; | |
1403 } | |
1404 } | |
1405 | |
1406 | |
1407 class UndoRedoCommand : public BitmapCommandBase | |
1408 { | |
1409 private: | |
1410 double sourceAngle_; | |
1411 double targetAngle_; | |
1412 | |
1413 static int ToDegrees(double angle) | |
1414 { | |
1415 return static_cast<int>(round(angle * 180.0 / boost::math::constants::pi<double>())); | |
1416 } | |
1417 | |
1418 protected: | |
1419 virtual void UndoInternal(BitmapStack::Bitmap& bitmap) const | |
1420 { | |
1421 LOG(INFO) << "Undo - Set angle to " << ToDegrees(sourceAngle_) << " degrees"; | |
1422 bitmap.SetAngle(sourceAngle_); | |
1423 } | |
1424 | |
1425 virtual void RedoInternal(BitmapStack::Bitmap& bitmap) const | |
1426 { | |
1427 LOG(INFO) << "Redo - Set angle to " << ToDegrees(sourceAngle_) << " degrees"; | |
1428 bitmap.SetAngle(targetAngle_); | |
1429 } | |
1430 | |
1431 public: | |
1432 UndoRedoCommand(BitmapStack::BitmapAccessor& accessor, | |
1433 double sourceAngle, | |
1434 double targetAngle) : | |
1435 BitmapCommandBase(accessor), | |
1436 sourceAngle_(sourceAngle), | |
1437 targetAngle_(targetAngle) | |
1438 { | |
1439 } | |
1440 }; | |
1441 | |
1442 | |
1443 public: | |
1444 RotateBitmapTracker(UndoRedoStack& undoRedoStack, | |
1445 BitmapStack& stack, | |
1446 const ViewportGeometry& view, | |
1447 size_t bitmap, | |
1448 double x, | |
1449 double y, | |
1450 bool roundAngles) : | |
1451 undoRedoStack_(undoRedoStack), | |
1452 accessor_(stack, bitmap), | |
1453 roundAngles_(roundAngles) | |
1454 { | |
1455 if (accessor_.IsValid()) | |
1456 { | |
1457 accessor_.GetBitmap().GetCenter(centerX_, centerY_); | |
1458 originalAngle_ = accessor_.GetBitmap().GetAngle(); | |
1459 | |
1460 double sceneX, sceneY; | |
1461 view.MapDisplayToScene(sceneX, sceneY, x, y); | |
1462 | |
1463 if (!ComputeAngle(clickAngle_, x, y)) | |
1464 { | |
1465 accessor_.Invalidate(); | |
1466 } | |
1467 } | |
1468 } | |
1469 | |
1470 virtual bool HasRender() const | |
1471 { | |
1472 return false; | |
1473 } | |
1474 | |
1475 virtual void Render(CairoContext& context, | |
1476 double zoom) | |
1477 { | |
1478 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
1479 } | |
1480 | |
1481 virtual void MouseUp() | |
1482 { | |
1483 if (accessor_.IsValid()) | |
1484 { | |
1485 undoRedoStack_.Add(new UndoRedoCommand(accessor_, originalAngle_, | |
1486 accessor_.GetBitmap().GetAngle())); | |
1487 } | |
1488 } | |
1489 | |
1490 virtual void MouseMove(int displayX, | |
1491 int displayY, | |
1492 double sceneX, | |
1493 double sceneY) | |
1494 { | |
1495 static const double ROUND_ANGLE = 15.0 / 180.0 * boost::math::constants::pi<double>(); | |
1496 | |
1497 double angle; | |
1498 | |
1499 if (accessor_.IsValid() && | |
1500 ComputeAngle(angle, sceneX, sceneY)) | |
1501 { | |
1502 angle = angle - clickAngle_ + originalAngle_; | |
1503 | |
1504 if (roundAngles_) | |
1505 { | |
1506 angle = round(angle / ROUND_ANGLE) * ROUND_ANGLE; | |
1507 } | |
1508 | |
1509 accessor_.GetBitmap().SetAngle(angle); | |
1510 } | |
1511 } | |
1512 }; | |
1513 | |
1514 | |
1515 class MoveBitmapTracker : public IWorldSceneMouseTracker | |
1516 { | |
1517 private: | |
1518 UndoRedoStack& undoRedoStack_; | |
1519 BitmapStack::BitmapAccessor accessor_; | |
1520 double clickX_; | |
1521 double clickY_; | |
1522 double panX_; | |
1523 double panY_; | |
1524 bool oneAxis_; | |
1525 | |
1526 class UndoRedoCommand : public BitmapCommandBase | |
1527 { | |
1528 private: | |
1529 double sourceX_; | |
1530 double sourceY_; | |
1531 double targetX_; | |
1532 double targetY_; | |
1533 | |
1534 protected: | |
1535 virtual void UndoInternal(BitmapStack::Bitmap& bitmap) const | |
1536 { | |
1537 bitmap.SetPan(sourceX_, sourceY_); | |
1538 } | |
1539 | |
1540 virtual void RedoInternal(BitmapStack::Bitmap& bitmap) const | |
1541 { | |
1542 bitmap.SetPan(targetX_, targetY_); | |
1543 } | |
1544 | |
1545 public: | |
1546 UndoRedoCommand(BitmapStack::BitmapAccessor& accessor, | |
1547 double sourceX, | |
1548 double sourceY, | |
1549 double targetX, | |
1550 double targetY) : | |
1551 BitmapCommandBase(accessor), | |
1552 sourceX_(sourceX), | |
1553 sourceY_(sourceY), | |
1554 targetX_(targetX), | |
1555 targetY_(targetY) | |
1556 { | |
1557 } | |
1558 }; | |
1559 | |
1560 | |
1561 public: | |
1562 MoveBitmapTracker(UndoRedoStack& undoRedoStack, | |
1563 BitmapStack& stack, | |
1564 size_t bitmap, | |
1565 double x, | |
1566 double y, | |
1567 bool oneAxis) : | |
1568 undoRedoStack_(undoRedoStack), | |
1569 accessor_(stack, bitmap), | |
1570 clickX_(x), | |
1571 clickY_(y), | |
1572 oneAxis_(oneAxis) | |
1573 { | |
1574 if (accessor_.IsValid()) | |
1575 { | |
1576 panX_ = accessor_.GetBitmap().GetPanX(); | |
1577 panY_ = accessor_.GetBitmap().GetPanY(); | |
1578 } | |
1579 } | |
1580 | |
1581 virtual bool HasRender() const | |
1582 { | |
1583 return false; | |
1584 } | |
1585 | |
1586 virtual void Render(CairoContext& context, | |
1587 double zoom) | |
1588 { | |
1589 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
1590 } | |
1591 | |
1592 virtual void MouseUp() | |
1593 { | |
1594 if (accessor_.IsValid()) | |
1595 { | |
1596 undoRedoStack_.Add(new UndoRedoCommand(accessor_, panX_, panY_, | |
1597 accessor_.GetBitmap().GetPanX(), | |
1598 accessor_.GetBitmap().GetPanY())); | |
1599 } | |
1600 } | |
1601 | |
1602 virtual void MouseMove(int displayX, | |
1603 int displayY, | |
1604 double sceneX, | |
1605 double sceneY) | |
1606 { | |
1607 if (accessor_.IsValid()) | |
1608 { | |
1609 double dx = sceneX - clickX_; | |
1610 double dy = sceneY - clickY_; | |
1611 | |
1612 if (oneAxis_) | |
1613 { | |
1614 if (fabs(dx) > fabs(dy)) | |
1615 { | |
1616 accessor_.GetBitmap().SetPan(dx + panX_, panY_); | |
1617 } | |
1618 else | |
1619 { | |
1620 accessor_.GetBitmap().SetPan(panX_, dy + panY_); | |
1621 } | |
1622 } | |
1623 else | |
1624 { | |
1625 accessor_.GetBitmap().SetPan(dx + panX_, dy + panY_); | |
1626 } | |
1627 } | |
1628 } | |
1629 }; | |
1630 | |
1631 | |
1632 class CropBitmapTracker : public IWorldSceneMouseTracker | |
1633 { | |
1634 private: | |
1635 BitmapStack::BitmapAccessor accessor_; | |
1636 BitmapStack::Corner corner_; | |
1637 unsigned int cropX_; | |
1638 unsigned int cropY_; | |
1639 unsigned int cropWidth_; | |
1640 unsigned int cropHeight_; | |
1641 | |
1642 public: | |
1643 CropBitmapTracker(BitmapStack& stack, | |
1644 const ViewportGeometry& view, | |
1645 size_t bitmap, | |
1646 double x, | |
1647 double y, | |
1648 BitmapStack::Corner corner) : | |
1649 accessor_(stack, bitmap), | |
1650 corner_(corner) | |
1651 { | |
1652 if (accessor_.IsValid()) | |
1653 { | |
1654 accessor_.GetBitmap().GetCrop(cropX_, cropY_, cropWidth_, cropHeight_); | |
1655 } | |
1656 } | |
1657 | |
1658 virtual bool HasRender() const | |
1659 { | |
1660 return false; | |
1661 } | |
1662 | |
1663 virtual void Render(CairoContext& context, | |
1664 double zoom) | |
1665 { | |
1666 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
1667 } | |
1668 | |
1669 virtual void MouseUp() | |
1670 { | |
1671 } | |
1672 | |
1673 virtual void MouseMove(int displayX, | |
1674 int displayY, | |
1675 double sceneX, | |
1676 double sceneY) | |
1677 { | |
1678 if (accessor_.IsValid()) | |
1679 { | |
1680 unsigned int x, y; | |
1681 | |
1682 BitmapStack::Bitmap& bitmap = accessor_.GetBitmap(); | |
1683 if (bitmap.GetPixel(x, y, sceneX, sceneY)) | |
1684 { | |
1685 unsigned int targetX, targetWidth; | |
1686 | |
1687 if (corner_ == BitmapStack::Corner_TopLeft || | |
1688 corner_ == BitmapStack::Corner_BottomLeft) | |
1689 { | |
1690 targetX = std::min(x, cropX_ + cropWidth_); | |
1691 targetWidth = cropX_ + cropWidth_ - targetX; | |
1692 } | |
1693 else | |
1694 { | |
1695 targetX = cropX_; | |
1696 targetWidth = std::max(x, cropX_) - cropX_; | |
1697 } | |
1698 | |
1699 unsigned int targetY, targetHeight; | |
1700 | |
1701 if (corner_ == BitmapStack::Corner_TopLeft || | |
1702 corner_ == BitmapStack::Corner_TopRight) | |
1703 { | |
1704 targetY = std::min(y, cropY_ + cropHeight_); | |
1705 targetHeight = cropY_ + cropHeight_ - targetY; | |
1706 } | |
1707 else | |
1708 { | |
1709 targetY = cropY_; | |
1710 targetHeight = std::max(y, cropY_) - cropY_; | |
1711 } | |
1712 | |
1713 bitmap.SetCrop(targetX, targetY, targetWidth, targetHeight); | |
1714 } | |
1715 } | |
1716 } | |
1717 }; | |
1718 | |
1719 | |
1720 class ResizeBitmapTracker : public IWorldSceneMouseTracker | |
1721 { | |
1722 private: | |
1723 BitmapStack::BitmapAccessor accessor_; | |
1724 bool roundScaling_; | |
1725 double originalSpacingX_; | |
1726 double originalSpacingY_; | |
1727 BitmapStack::Corner oppositeCorner_; | |
1728 double oppositeX_; | |
1729 double oppositeY_; | |
1730 double baseScaling_; | |
1731 | |
1732 static double ComputeDistance(double x1, | |
1733 double y1, | |
1734 double x2, | |
1735 double y2) | |
1736 { | |
1737 double dx = x1 - x2; | |
1738 double dy = y1 - y2; | |
1739 return sqrt(dx * dx + dy * dy); | |
1740 } | |
1741 | |
1742 public: | |
1743 ResizeBitmapTracker(BitmapStack& stack, | |
1744 size_t bitmap, | |
1745 double x, | |
1746 double y, | |
1747 BitmapStack::Corner corner, | |
1748 bool roundScaling) : | |
1749 accessor_(stack, bitmap), | |
1750 roundScaling_(roundScaling) | |
1751 { | |
1752 if (accessor_.IsValid() && | |
1753 accessor_.GetBitmap().IsResizeable()) | |
1754 { | |
1755 originalSpacingX_ = accessor_.GetBitmap().GetPixelSpacingX(); | |
1756 originalSpacingY_ = accessor_.GetBitmap().GetPixelSpacingY(); | |
1757 | |
1758 switch (corner) | |
1759 { | |
1760 case BitmapStack::Corner_TopLeft: | |
1761 oppositeCorner_ = BitmapStack::Corner_BottomRight; | |
1762 break; | |
1763 | |
1764 case BitmapStack::Corner_TopRight: | |
1765 oppositeCorner_ = BitmapStack::Corner_BottomLeft; | |
1766 break; | |
1767 | |
1768 case BitmapStack::Corner_BottomLeft: | |
1769 oppositeCorner_ = BitmapStack::Corner_TopRight; | |
1770 break; | |
1771 | |
1772 case BitmapStack::Corner_BottomRight: | |
1773 oppositeCorner_ = BitmapStack::Corner_TopLeft; | |
1774 break; | |
1775 | |
1776 default: | |
1777 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
1778 } | |
1779 | |
1780 accessor_.GetBitmap().GetCorner(oppositeX_, oppositeY_, oppositeCorner_); | |
1781 | |
1782 double d = ComputeDistance(x, y, oppositeX_, oppositeY_); | |
1783 if (d >= std::numeric_limits<float>::epsilon()) | |
1784 { | |
1785 baseScaling_ = 1.0 / d; | |
1786 } | |
1787 else | |
1788 { | |
1789 // Avoid division by zero in extreme cases | |
1790 accessor_.Invalidate(); | |
1791 } | |
1792 } | |
1793 } | |
1794 | |
1795 virtual bool HasRender() const | |
1796 { | |
1797 return false; | |
1798 } | |
1799 | |
1800 virtual void Render(CairoContext& context, | |
1801 double zoom) | |
1802 { | |
1803 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
1804 } | |
1805 | |
1806 virtual void MouseUp() | |
1807 { | |
1808 } | |
1809 | |
1810 virtual void MouseMove(int displayX, | |
1811 int displayY, | |
1812 double sceneX, | |
1813 double sceneY) | |
1814 { | |
1815 static const double ROUND_SCALING = 0.1; | |
1816 | |
1817 if (accessor_.IsValid() && | |
1818 accessor_.GetBitmap().IsResizeable()) | |
1819 { | |
1820 double scaling = ComputeDistance(oppositeX_, oppositeY_, sceneX, sceneY) * baseScaling_; | |
1821 | |
1822 if (roundScaling_) | |
1823 { | |
1824 scaling = round(scaling / ROUND_SCALING) * ROUND_SCALING; | |
1825 } | |
1826 | |
1827 BitmapStack::Bitmap& bitmap = accessor_.GetBitmap(); | |
1828 bitmap.SetPixelSpacing(scaling * originalSpacingX_, | |
1829 scaling * originalSpacingY_); | |
1830 | |
1831 // Keep the opposite corner at a fixed location | |
1832 double ox, oy; | |
1833 bitmap.GetCorner(ox, oy, oppositeCorner_); | |
1834 bitmap.SetPan(bitmap.GetPanX() + oppositeX_ - ox, | |
1835 bitmap.GetPanY() + oppositeY_ - oy); | |
1228 } | 1836 } |
1229 } | 1837 } |
1230 }; | 1838 }; |
1231 | 1839 |
1232 | 1840 |
1245 { | 1853 { |
1246 return 10.0; | 1854 return 10.0; |
1247 } | 1855 } |
1248 | 1856 |
1249 | 1857 |
1250 BitmapStack& stack_; | 1858 BitmapStack& stack_; |
1251 Tool tool_; | 1859 UndoRedoStack undoRedoStack_; |
1252 | 1860 Tool tool_; |
1253 | 1861 |
1254 class RotateBitmapTracker : public IWorldSceneMouseTracker | |
1255 { | |
1256 private: | |
1257 BitmapStack::BitmapAccessor accessor_; | |
1258 double centerX_; | |
1259 double centerY_; | |
1260 double originalAngle_; | |
1261 double clickAngle_; | |
1262 bool roundAngles_; | |
1263 | |
1264 bool ComputeAngle(double& angle /* out */, | |
1265 double sceneX, | |
1266 double sceneY) const | |
1267 { | |
1268 Vector u; | |
1269 LinearAlgebra::AssignVector(u, sceneX - centerX_, sceneY - centerY_); | |
1270 | |
1271 double nu = boost::numeric::ublas::norm_2(u); | |
1272 | |
1273 if (!LinearAlgebra::IsCloseToZero(nu)) | |
1274 { | |
1275 u /= nu; | |
1276 angle = atan2(u[1], u[0]); | |
1277 return true; | |
1278 } | |
1279 else | |
1280 { | |
1281 return false; | |
1282 } | |
1283 } | |
1284 | |
1285 | |
1286 public: | |
1287 RotateBitmapTracker(BitmapStack& stack, | |
1288 const ViewportGeometry& view, | |
1289 size_t bitmap, | |
1290 double x, | |
1291 double y, | |
1292 bool roundAngles) : | |
1293 accessor_(stack, bitmap), | |
1294 roundAngles_(roundAngles) | |
1295 { | |
1296 if (accessor_.IsValid()) | |
1297 { | |
1298 accessor_.GetBitmap().GetCenter(centerX_, centerY_); | |
1299 originalAngle_ = accessor_.GetBitmap().GetAngle(); | |
1300 | |
1301 double sceneX, sceneY; | |
1302 view.MapDisplayToScene(sceneX, sceneY, x, y); | |
1303 | |
1304 if (!ComputeAngle(clickAngle_, x, y)) | |
1305 { | |
1306 accessor_.Invalidate(); | |
1307 } | |
1308 } | |
1309 } | |
1310 | |
1311 virtual bool HasRender() const | |
1312 { | |
1313 return false; | |
1314 } | |
1315 | |
1316 virtual void Render(CairoContext& context, | |
1317 double zoom) | |
1318 { | |
1319 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
1320 } | |
1321 | |
1322 virtual void MouseUp() | |
1323 { | |
1324 } | |
1325 | |
1326 virtual void MouseMove(int displayX, | |
1327 int displayY, | |
1328 double sceneX, | |
1329 double sceneY) | |
1330 { | |
1331 static const double ROUND_ANGLE = 15.0 / 180.0 * boost::math::constants::pi<double>(); | |
1332 | |
1333 double angle; | |
1334 | |
1335 if (accessor_.IsValid() && | |
1336 ComputeAngle(angle, sceneX, sceneY)) | |
1337 { | |
1338 angle = angle - clickAngle_ + originalAngle_; | |
1339 | |
1340 if (roundAngles_) | |
1341 { | |
1342 angle = round(angle / ROUND_ANGLE) * ROUND_ANGLE; | |
1343 } | |
1344 | |
1345 accessor_.GetBitmap().SetAngle(angle); | |
1346 } | |
1347 } | |
1348 }; | |
1349 | |
1350 | |
1351 class MoveBitmapTracker : public IWorldSceneMouseTracker | |
1352 { | |
1353 private: | |
1354 BitmapStack::BitmapAccessor accessor_; | |
1355 double clickX_; | |
1356 double clickY_; | |
1357 double panX_; | |
1358 double panY_; | |
1359 bool oneAxis_; | |
1360 | |
1361 public: | |
1362 MoveBitmapTracker(BitmapStack& stack, | |
1363 size_t bitmap, | |
1364 double x, | |
1365 double y, | |
1366 bool oneAxis) : | |
1367 accessor_(stack, bitmap), | |
1368 clickX_(x), | |
1369 clickY_(y), | |
1370 oneAxis_(oneAxis) | |
1371 { | |
1372 if (accessor_.IsValid()) | |
1373 { | |
1374 panX_ = accessor_.GetBitmap().GetPanX(); | |
1375 panY_ = accessor_.GetBitmap().GetPanY(); | |
1376 } | |
1377 } | |
1378 | |
1379 virtual bool HasRender() const | |
1380 { | |
1381 return false; | |
1382 } | |
1383 | |
1384 virtual void Render(CairoContext& context, | |
1385 double zoom) | |
1386 { | |
1387 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
1388 } | |
1389 | |
1390 virtual void MouseUp() | |
1391 { | |
1392 } | |
1393 | |
1394 virtual void MouseMove(int displayX, | |
1395 int displayY, | |
1396 double sceneX, | |
1397 double sceneY) | |
1398 { | |
1399 if (accessor_.IsValid()) | |
1400 { | |
1401 double dx = sceneX - clickX_; | |
1402 double dy = sceneY - clickY_; | |
1403 | |
1404 if (oneAxis_) | |
1405 { | |
1406 if (fabs(dx) > fabs(dy)) | |
1407 { | |
1408 accessor_.GetBitmap().SetPan(dx + panX_, panY_); | |
1409 } | |
1410 else | |
1411 { | |
1412 accessor_.GetBitmap().SetPan(panX_, dy + panY_); | |
1413 } | |
1414 } | |
1415 else | |
1416 { | |
1417 accessor_.GetBitmap().SetPan(dx + panX_, dy + panY_); | |
1418 } | |
1419 } | |
1420 } | |
1421 }; | |
1422 | |
1423 | |
1424 class CropBitmapTracker : public IWorldSceneMouseTracker | |
1425 { | |
1426 private: | |
1427 BitmapStack::BitmapAccessor accessor_; | |
1428 BitmapStack::Corner corner_; | |
1429 unsigned int cropX_; | |
1430 unsigned int cropY_; | |
1431 unsigned int cropWidth_; | |
1432 unsigned int cropHeight_; | |
1433 | |
1434 public: | |
1435 CropBitmapTracker(BitmapStack& stack, | |
1436 const ViewportGeometry& view, | |
1437 size_t bitmap, | |
1438 double x, | |
1439 double y, | |
1440 BitmapStack::Corner corner) : | |
1441 accessor_(stack, bitmap), | |
1442 corner_(corner) | |
1443 { | |
1444 if (accessor_.IsValid()) | |
1445 { | |
1446 accessor_.GetBitmap().GetCrop(cropX_, cropY_, cropWidth_, cropHeight_); | |
1447 } | |
1448 } | |
1449 | |
1450 virtual bool HasRender() const | |
1451 { | |
1452 return false; | |
1453 } | |
1454 | |
1455 virtual void Render(CairoContext& context, | |
1456 double zoom) | |
1457 { | |
1458 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
1459 } | |
1460 | |
1461 virtual void MouseUp() | |
1462 { | |
1463 } | |
1464 | |
1465 virtual void MouseMove(int displayX, | |
1466 int displayY, | |
1467 double sceneX, | |
1468 double sceneY) | |
1469 { | |
1470 if (accessor_.IsValid()) | |
1471 { | |
1472 unsigned int x, y; | |
1473 | |
1474 BitmapStack::Bitmap& bitmap = accessor_.GetBitmap(); | |
1475 if (bitmap.GetPixel(x, y, sceneX, sceneY)) | |
1476 { | |
1477 unsigned int targetX, targetWidth; | |
1478 | |
1479 if (corner_ == BitmapStack::Corner_TopLeft || | |
1480 corner_ == BitmapStack::Corner_BottomLeft) | |
1481 { | |
1482 targetX = std::min(x, cropX_ + cropWidth_); | |
1483 targetWidth = cropX_ + cropWidth_ - targetX; | |
1484 } | |
1485 else | |
1486 { | |
1487 targetX = cropX_; | |
1488 targetWidth = std::max(x, cropX_) - cropX_; | |
1489 } | |
1490 | |
1491 unsigned int targetY, targetHeight; | |
1492 | |
1493 if (corner_ == BitmapStack::Corner_TopLeft || | |
1494 corner_ == BitmapStack::Corner_TopRight) | |
1495 { | |
1496 targetY = std::min(y, cropY_ + cropHeight_); | |
1497 targetHeight = cropY_ + cropHeight_ - targetY; | |
1498 } | |
1499 else | |
1500 { | |
1501 targetY = cropY_; | |
1502 targetHeight = std::max(y, cropY_) - cropY_; | |
1503 } | |
1504 | |
1505 bitmap.SetCrop(targetX, targetY, targetWidth, targetHeight); | |
1506 } | |
1507 } | |
1508 } | |
1509 }; | |
1510 | |
1511 | |
1512 class ResizeBitmapTracker : public IWorldSceneMouseTracker | |
1513 { | |
1514 private: | |
1515 BitmapStack::BitmapAccessor accessor_; | |
1516 bool roundScaling_; | |
1517 double originalSpacingX_; | |
1518 double originalSpacingY_; | |
1519 BitmapStack::Corner oppositeCorner_; | |
1520 double oppositeX_; | |
1521 double oppositeY_; | |
1522 double baseScaling_; | |
1523 | |
1524 static double ComputeDistance(double x1, | |
1525 double y1, | |
1526 double x2, | |
1527 double y2) | |
1528 { | |
1529 double dx = x1 - x2; | |
1530 double dy = y1 - y2; | |
1531 return sqrt(dx * dx + dy * dy); | |
1532 } | |
1533 | |
1534 public: | |
1535 ResizeBitmapTracker(BitmapStack& stack, | |
1536 size_t bitmap, | |
1537 double x, | |
1538 double y, | |
1539 BitmapStack::Corner corner, | |
1540 bool roundScaling) : | |
1541 accessor_(stack, bitmap), | |
1542 roundScaling_(roundScaling) | |
1543 { | |
1544 if (accessor_.IsValid() && | |
1545 accessor_.GetBitmap().IsResizeable()) | |
1546 { | |
1547 originalSpacingX_ = accessor_.GetBitmap().GetPixelSpacingX(); | |
1548 originalSpacingY_ = accessor_.GetBitmap().GetPixelSpacingY(); | |
1549 | |
1550 switch (corner) | |
1551 { | |
1552 case BitmapStack::Corner_TopLeft: | |
1553 oppositeCorner_ = BitmapStack::Corner_BottomRight; | |
1554 break; | |
1555 | |
1556 case BitmapStack::Corner_TopRight: | |
1557 oppositeCorner_ = BitmapStack::Corner_BottomLeft; | |
1558 break; | |
1559 | |
1560 case BitmapStack::Corner_BottomLeft: | |
1561 oppositeCorner_ = BitmapStack::Corner_TopRight; | |
1562 break; | |
1563 | |
1564 case BitmapStack::Corner_BottomRight: | |
1565 oppositeCorner_ = BitmapStack::Corner_TopLeft; | |
1566 break; | |
1567 | |
1568 default: | |
1569 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
1570 } | |
1571 | |
1572 accessor_.GetBitmap().GetCorner(oppositeX_, oppositeY_, oppositeCorner_); | |
1573 | |
1574 double d = ComputeDistance(x, y, oppositeX_, oppositeY_); | |
1575 if (d >= std::numeric_limits<float>::epsilon()) | |
1576 { | |
1577 baseScaling_ = 1.0 / d; | |
1578 } | |
1579 else | |
1580 { | |
1581 // Avoid division by zero in extreme cases | |
1582 accessor_.Invalidate(); | |
1583 } | |
1584 } | |
1585 } | |
1586 | |
1587 virtual bool HasRender() const | |
1588 { | |
1589 return false; | |
1590 } | |
1591 | |
1592 virtual void Render(CairoContext& context, | |
1593 double zoom) | |
1594 { | |
1595 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
1596 } | |
1597 | |
1598 virtual void MouseUp() | |
1599 { | |
1600 } | |
1601 | |
1602 virtual void MouseMove(int displayX, | |
1603 int displayY, | |
1604 double sceneX, | |
1605 double sceneY) | |
1606 { | |
1607 static const double ROUND_SCALING = 0.1; | |
1608 | |
1609 if (accessor_.IsValid() && | |
1610 accessor_.GetBitmap().IsResizeable()) | |
1611 { | |
1612 double scaling = ComputeDistance(oppositeX_, oppositeY_, sceneX, sceneY) * baseScaling_; | |
1613 | |
1614 if (roundScaling_) | |
1615 { | |
1616 scaling = round(scaling / ROUND_SCALING) * ROUND_SCALING; | |
1617 } | |
1618 | |
1619 BitmapStack::Bitmap& bitmap = accessor_.GetBitmap(); | |
1620 bitmap.SetPixelSpacing(scaling * originalSpacingX_, | |
1621 scaling * originalSpacingY_); | |
1622 | |
1623 // Keep the opposite corner at a fixed location | |
1624 double ox, oy; | |
1625 bitmap.GetCorner(ox, oy, oppositeCorner_); | |
1626 bitmap.SetPan(bitmap.GetPanX() + oppositeX_ - ox, | |
1627 bitmap.GetPanY() + oppositeY_ - oy); | |
1628 } | |
1629 } | |
1630 }; | |
1631 | |
1632 | 1862 |
1633 public: | 1863 public: |
1634 BitmapStackInteractor(BitmapStack& stack) : | 1864 BitmapStackInteractor(BitmapStack& stack) : |
1635 stack_(stack), | 1865 stack_(stack), |
1636 tool_(Tool_Move) | 1866 tool_(Tool_Move) |
1701 bitmap == selected) | 1931 bitmap == selected) |
1702 { | 1932 { |
1703 switch (tool_) | 1933 switch (tool_) |
1704 { | 1934 { |
1705 case Tool_Move: | 1935 case Tool_Move: |
1706 return new MoveBitmapTracker(stack_, bitmap, x, y, | 1936 return new MoveBitmapTracker(undoRedoStack_, stack_, bitmap, x, y, |
1707 (modifiers & KeyboardModifiers_Shift)); | 1937 (modifiers & KeyboardModifiers_Shift)); |
1708 | 1938 |
1709 case Tool_Rotate: | 1939 case Tool_Rotate: |
1710 return new RotateBitmapTracker(stack_, view, bitmap, x, y, | 1940 return new RotateBitmapTracker(undoRedoStack_, stack_, view, bitmap, x, y, |
1711 (modifiers & KeyboardModifiers_Shift)); | 1941 (modifiers & KeyboardModifiers_Shift)); |
1712 | 1942 |
1713 default: | 1943 default: |
1714 break; | 1944 break; |
1715 } | 1945 } |
1795 tool_ = Tool_Rotate; | 2025 tool_ = Tool_Rotate; |
1796 break; | 2026 break; |
1797 | 2027 |
1798 case 's': | 2028 case 's': |
1799 tool_ = Tool_Resize; | 2029 tool_ = Tool_Resize; |
2030 break; | |
2031 | |
2032 case 'z': | |
2033 if (modifiers & KeyboardModifiers_Control) | |
2034 { | |
2035 undoRedoStack_.Undo(); | |
2036 widget.NotifyContentChanged(); | |
2037 } | |
2038 break; | |
2039 | |
2040 case 'y': | |
2041 if (modifiers & KeyboardModifiers_Control) | |
2042 { | |
2043 undoRedoStack_.Redo(); | |
2044 widget.NotifyContentChanged(); | |
2045 } | |
1800 break; | 2046 break; |
1801 | 2047 |
1802 default: | 2048 default: |
1803 break; | 2049 break; |
1804 } | 2050 } |
2105 | 2351 |
2106 Orthanc::FontRegistry fonts; | 2352 Orthanc::FontRegistry fonts; |
2107 fonts.AddFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16); | 2353 fonts.AddFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16); |
2108 | 2354 |
2109 stack_.reset(new BitmapStack(IObserver::broker_, *orthancApiClient_)); | 2355 stack_.reset(new BitmapStack(IObserver::broker_, *orthancApiClient_)); |
2110 stack_->LoadFrame(instance, frame, false); | 2356 //stack_->LoadFrame(instance, frame, false); |
2111 stack_->LoadFrame("61f3143e-96f34791-ad6bbb8d-62559e75-45943e1b", frame, false); | 2357 //stack_->LoadFrame("61f3143e-96f34791-ad6bbb8d-62559e75-45943e1b", frame, false); |
2112 stack_->LoadText(fonts.GetFont(0), "Hello\nworld\nBonjour, Alain").SetResizeable(true); | 2358 |
2113 stack_->LoadTestBlock(100, 50).SetResizeable(true); | 2359 { |
2360 BitmapStack::Bitmap& bitmap = stack_->LoadText(fonts.GetFont(0), "Hello\nworld\nBonjour, Alain"); | |
2361 dynamic_cast<BitmapStack::AlphaBitmap&>(bitmap).SetForegroundValue(256); | |
2362 dynamic_cast<BitmapStack::AlphaBitmap&>(bitmap).SetResizeable(true); | |
2363 } | |
2364 | |
2365 { | |
2366 BitmapStack::Bitmap& bitmap = stack_->LoadTestBlock(100, 50); | |
2367 dynamic_cast<BitmapStack::AlphaBitmap&>(bitmap).SetForegroundValue(256); | |
2368 dynamic_cast<BitmapStack::AlphaBitmap&>(bitmap).SetResizeable(true); | |
2369 } | |
2370 | |
2114 | 2371 |
2115 mainWidget_ = new BitmapStackWidget(IObserver::broker_, *stack_, "main-widget"); | 2372 mainWidget_ = new BitmapStackWidget(IObserver::broker_, *stack_, "main-widget"); |
2116 mainWidget_->SetTransmitMouseOver(true); | 2373 mainWidget_->SetTransmitMouseOver(true); |
2117 | 2374 |
2118 //stack_->SetWindowing(128, 256); | 2375 //stack_->SetWindowing(128, 256); |