# HG changeset patch # User Sebastien Jodogne # Date 1580826008 -3600 # Node ID 7ec8fea061b91001cb4c5c61558d988a685545fd # Parent 0ca50d275b9a6e0f534a75b6c8401d07b411fc0a# Parent 398ea4259e65ce0fc737cbbe879809ebf4a13db8 integration mainline->broker diff -r 0ca50d275b9a -r 7ec8fea061b9 .hgtags --- a/.hgtags Fri Jan 31 17:34:57 2020 +0100 +++ b/.hgtags Tue Feb 04 15:20:08 2020 +0100 @@ -40,3 +40,6 @@ 33b0a762e98ade4e1f00e26bf865b417269443fb toa2019110401 76705b430c78d4ca0a718c6164ac4b73ce007fde toa2019110801 ce3052f28f2e6e7b29cfad4efae4e3881e70b056 toa2019122001 +5a2d5380148d4068af55ca3e895f5fa18c778bc1 toa2020012701 +ca2058bd74eff429033df3de7a6785ae08a92922 toa2020012702 +5e45322c77249573661bb4f45f035d000db446fc toa2020012703 diff -r 0ca50d275b9a -r 7ec8fea061b9 Applications/Generic/GuiAdapter.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Applications/Generic/GuiAdapter.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Applications/Generic/NativeStoneApplicationContext.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Applications/Generic/NativeStoneApplicationContext.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Applications/Generic/NativeStoneApplicationRunner.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Applications/Generic/NativeStoneApplicationRunner.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Applications/IStoneApplication.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Applications/Qt/QCairoWidget.cpp --- a/Applications/Qt/QCairoWidget.cpp Fri Jan 31 17:34:57 2020 +0100 +++ b/Applications/Qt/QCairoWidget.cpp Tue Feb 04 15:20:08 2020 +0100 @@ -181,6 +181,18 @@ CASE_QT_KEY_TO_ORTHANC(Qt::Key_Down, KeyboardKeys_Down); CASE_QT_KEY_TO_ORTHANC(Qt::Key_Left, KeyboardKeys_Left); CASE_QT_KEY_TO_ORTHANC(Qt::Key_Right, KeyboardKeys_Right); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F1, KeyboardKeys_F1); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F2, KeyboardKeys_F2); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F3, KeyboardKeys_F3); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F4, KeyboardKeys_F4); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F5, KeyboardKeys_F5); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F6, KeyboardKeys_F6); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F7, KeyboardKeys_F7); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F8, KeyboardKeys_F8); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F9, KeyboardKeys_F9); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F10, KeyboardKeys_F10); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F11, KeyboardKeys_F11); + CASE_QT_KEY_TO_ORTHANC(Qt::Key_F12, KeyboardKeys_F12); default: break; } diff -r 0ca50d275b9a -r 7ec8fea061b9 Applications/Samples/SampleApplicationBase.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Applications/Samples/SampleMainNative.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Applications/Samples/SimpleViewerApplicationSingleFile.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Applications/Samples/SingleFrameApplication.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Applications/Samples/SingleFrameEditorApplication.h --- a/Applications/Samples/SingleFrameEditorApplication.h Fri Jan 31 17:34:57 2020 +0100 +++ b/Applications/Samples/SingleFrameEditorApplication.h Tue Feb 04 15:20:08 2020 +0100 @@ -315,7 +315,7 @@ << snapshot.toStyledString(); boost::shared_ptr scene(new RadiographyScene); - RadiographySceneReader reader(*scene, context_->GetOrthancApiClient()); + RadiographySceneReader reader(*scene, *context_->GetOrthancApiClient()); reader.Read(snapshot); widget.SetScene(scene); diff -r 0ca50d275b9a -r 7ec8fea061b9 Applications/Sdl/SdlCairoSurface.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Applications/Sdl/SdlEngine.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Applications/Sdl/SdlEngine.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Applications/Sdl/SdlOrthancSurface.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Applications/Sdl/SdlStoneApplicationRunner.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Applications/Sdl/SdlStoneApplicationRunner.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Applications/StoneApplicationContext.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Applications/StoneApplicationContext.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Layers/DicomSeriesVolumeSlicer.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Layers/DicomSeriesVolumeSlicer.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Layers/DicomStructureSetSlicer.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Layers/DicomStructureSetSlicer.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Layers/IVolumeSlicer.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Loaders/DicomStructureSetLoader.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Loaders/DicomStructureSetLoader.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Loaders/DicomStructureSetLoader2.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Loaders/DicomStructureSetLoader2.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Loaders/LoaderCache.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Loaders/LoaderCache.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Loaders/LoaderStateMachine.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Loaders/LoaderStateMachine.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Loaders/OrthancMultiframeVolumeLoader.cpp --- a/Framework/Deprecated/Loaders/OrthancMultiframeVolumeLoader.cpp Fri Jan 31 17:34:57 2020 +0100 +++ b/Framework/Deprecated/Loaders/OrthancMultiframeVolumeLoader.cpp Tue Feb 04 15:20:08 2020 +0100 @@ -263,7 +263,8 @@ } template - void OrthancMultiframeVolumeLoader::CopyPixelData(const std::string& pixelData) + void OrthancMultiframeVolumeLoader::CopyPixelDataAndComputeDistribution( + const std::string& pixelData, std::map& distribution) { OrthancStone::ImageBuffer3D& target = volume_->GetPixelData(); @@ -283,43 +284,209 @@ return; } - const uint8_t* source = reinterpret_cast(pixelData.c_str()); - - for (unsigned int z = 0; z < depth; z++) + // first pass to initialize map { - OrthancStone::ImageBuffer3D::SliceWriter writer(target, OrthancStone::VolumeProjection_Axial, z); + const uint8_t* source = reinterpret_cast(pixelData.c_str()); - assert (writer.GetAccessor().GetWidth() == width && - writer.GetAccessor().GetHeight() == height); + for (unsigned int z = 0; z < depth; z++) + { + for (unsigned int y = 0; y < height; y++) + { + for (unsigned int x = 0; x < width; x++) + { + T value; + CopyPixel(value, source); + distribution[value] = 0; + source += bpp; + } + } + } + } + + { + const uint8_t* source = reinterpret_cast(pixelData.c_str()); - for (unsigned int y = 0; y < height; y++) + for (unsigned int z = 0; z < depth; z++) { - assert(sizeof(T) == Orthanc::GetBytesPerPixel(target.GetFormat())); + OrthancStone::ImageBuffer3D::SliceWriter writer(target, OrthancStone::VolumeProjection_Axial, z); - T* target = reinterpret_cast(writer.GetAccessor().GetRow(y)); + assert(writer.GetAccessor().GetWidth() == width && + writer.GetAccessor().GetHeight() == height); + + for (unsigned int y = 0; y < height; y++) + { + assert(sizeof(T) == Orthanc::GetBytesPerPixel(target.GetFormat())); - for (unsigned int x = 0; x < width; x++) - { - CopyPixel(*target, source); - target ++; - source += bpp; + T* target = reinterpret_cast(writer.GetAccessor().GetRow(y)); + + for (unsigned int x = 0; x < width; x++) + { + CopyPixel(*target, source); + + distribution[*target] += 1; + + target++; + source += bpp; + } } } } } + template + void OrthancMultiframeVolumeLoader::ComputeMinMaxWithOutlierRejection( + const std::map& distribution) + { + if (distribution.size() == 0) + { + LOG(ERROR) << "ComputeMinMaxWithOutlierRejection -- Volume image empty."; + } + else + { + OrthancStone::ImageBuffer3D& target = volume_->GetPixelData(); + + const uint64_t bpp = target.GetBytesPerPixel(); + const uint64_t width = target.GetWidth(); + const uint64_t height = target.GetHeight(); + const uint64_t depth = target.GetDepth(); + const uint64_t voxelCount = width * height * depth; + + // now that we have distribution[pixelValue] == numberOfPixelsWithValue + // compute number of values and check (assertion) that it is equal to + // width * height * depth + { + typename std::map::const_iterator it = distribution.begin(); + uint64_t totalCount = 0; + distributionRawMin_ = static_cast(it->first); + + while (it != distribution.end()) + { + T pixelValue = it->first; + uint64_t count = it->second; + totalCount += count; + it++; + if (it == distribution.end()) + distributionRawMax_ = static_cast(pixelValue); + } + LOG(INFO) << "Volume image. First distribution value = " + << static_cast(distributionRawMin_) + << " | Last distribution value = " + << static_cast(distributionRawMax_); + + if (totalCount != voxelCount) + { + LOG(ERROR) << "Internal error in dose distribution computation. TC (" + << totalCount << ") != VoxC (" << voxelCount; + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + // compute the number of voxels to reject at each end of the distribution + uint64_t endRejectionCount = static_cast( + outliersHalfRejectionRate_ * voxelCount); + + if (endRejectionCount > voxelCount) + { + LOG(ERROR) << "Internal error in dose distribution computation." + << " endRejectionCount = " << endRejectionCount + << " | voxelCount = " << voxelCount; + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + // this will contain the actual distribution minimum after outlier + // rejection + T resultMin = 0; + + // then start from start and remove pixel values up to + // endRejectionCount voxels rejected + { + typename std::map::const_iterator it = distribution.begin(); + + uint64_t currentCount = 0; + + while (it != distribution.end()) + { + T pixelValue = it->first; + uint64_t count = it->second; + + // if this pixelValue crosses the rejection threshold, let's set it + // and exit the loop + if ((currentCount <= endRejectionCount) && + (currentCount + count > endRejectionCount)) + { + resultMin = pixelValue; + break; + } + else + { + currentCount += count; + } + // and continue walking along the distribution + it++; + } + } + + // this will contain the actual distribution maximum after outlier + // rejection + T resultMax = 0; + // now start from END and remove pixel values up to + // endRejectionCount voxels rejected + { + typename std::map::const_reverse_iterator it = distribution.rbegin(); + + uint64_t currentCount = 0; + + while (it != distribution.rend()) + { + T pixelValue = it->first; + uint64_t count = it->second; + + if ((currentCount <= endRejectionCount) && + (currentCount + count > endRejectionCount)) + { + resultMax = pixelValue; + break; + } + else + { + currentCount += count; + } + // and continue walking along the distribution + it++; + } + } + if (resultMin > resultMax) + { + LOG(ERROR) << "Internal error in dose distribution computation! " << + "resultMin (" << resultMin << ") > resultMax (" << resultMax << ")"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + computedDistributionMin_ = static_cast(resultMin); + computedDistributionMax_ = static_cast(resultMax); + } + } + + template + void OrthancMultiframeVolumeLoader::CopyPixelDataAndComputeMinMax( + const std::string& pixelData) + { + std::map distribution; + CopyPixelDataAndComputeDistribution(pixelData, distribution); + ComputeMinMaxWithOutlierRejection(distribution); + } + void OrthancMultiframeVolumeLoader::SetUncompressedPixelData(const std::string& pixelData) { switch (volume_->GetPixelData().GetFormat()) { case Orthanc::PixelFormat_Grayscale32: - CopyPixelData(pixelData); + CopyPixelDataAndComputeMinMax(pixelData); break; case Orthanc::PixelFormat_Grayscale16: - CopyPixelData(pixelData); + CopyPixelDataAndComputeMinMax(pixelData); break; case Orthanc::PixelFormat_SignedGrayscale16: - CopyPixelData(pixelData); + CopyPixelDataAndComputeMinMax(pixelData); break; default: throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); @@ -341,12 +508,19 @@ return volume_->GetGeometry(); } - OrthancMultiframeVolumeLoader::OrthancMultiframeVolumeLoader(boost::shared_ptr volume, - OrthancStone::IOracle& oracle, - OrthancStone::IObservable& oracleObservable) : + OrthancMultiframeVolumeLoader::OrthancMultiframeVolumeLoader( + boost::shared_ptr volume, + OrthancStone::IOracle& oracle, + OrthancStone::IObservable& oracleObservable, + float outliersHalfRejectionRate) : LoaderStateMachine(oracle, oracleObservable), volume_(volume), - pixelDataLoaded_(false) + pixelDataLoaded_(false), + outliersHalfRejectionRate_(outliersHalfRejectionRate), + distributionRawMin_(0), + distributionRawMax_(0), + computedDistributionMin_(0), + computedDistributionMax_(0) { if (volume.get() == NULL) { @@ -359,6 +533,29 @@ LOG(TRACE) << "OrthancMultiframeVolumeLoader::~OrthancMultiframeVolumeLoader()"; } + + void OrthancMultiframeVolumeLoader::GetDistributionMinMax + (float& minValue, float& maxValue) const + { + if (distributionRawMin_ == 0 && distributionRawMax_ == 0) + { + LOG(WARNING) << "GetDistributionMinMaxWithOutliersRejection called before computation!"; + } + minValue = distributionRawMin_; + maxValue = distributionRawMax_; + } + + void OrthancMultiframeVolumeLoader::GetDistributionMinMaxWithOutliersRejection + (float& minValue, float& maxValue) const + { + if (computedDistributionMin_ == 0 && computedDistributionMax_ == 0) + { + LOG(WARNING) << "GetDistributionMinMaxWithOutliersRejection called before computation!"; + } + minValue = computedDistributionMin_; + maxValue = computedDistributionMax_; + } + void OrthancMultiframeVolumeLoader::LoadInstance(const std::string& instanceId) { Start(); diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Loaders/OrthancMultiframeVolumeLoader.h --- a/Framework/Deprecated/Loaders/OrthancMultiframeVolumeLoader.h Fri Jan 31 17:34:57 2020 +0100 +++ b/Framework/Deprecated/Loaders/OrthancMultiframeVolumeLoader.h Tue Feb 04 15:20:08 2020 +0100 @@ -42,6 +42,11 @@ std::string instanceId_; std::string transferSyntaxUid_; bool pixelDataLoaded_; + float outliersHalfRejectionRate_; + float distributionRawMin_; + float distributionRawMax_; + float computedDistributionMin_; + float computedDistributionMax_; const std::string& GetInstanceId() const; @@ -51,8 +56,35 @@ void SetGeometry(const Orthanc::DicomMap& dicom); + + /** + This method will : + + - copy the pixel values from the response to the volume image + - compute the maximum and minimum value while discarding the + outliersHalfRejectionRate_ fraction of the outliers from both the start + and the end of the distribution. + + In English, this means that, if the volume dataset contains a few extreme + values very different from the rest (outliers) that we want to get rid of, + this method allows to do so. + + If you supply 0.005, for instance, it means 1% of the extreme values will + be rejected (0.5% on each side of the distribution) + */ template - void CopyPixelData(const std::string& pixelData); + void CopyPixelDataAndComputeMinMax(const std::string& pixelData); + + /** Service method for CopyPixelDataAndComputeMinMax*/ + template + void CopyPixelDataAndComputeDistribution( + const std::string& pixelData, + std::map& distribution); + + /** Service method for CopyPixelDataAndComputeMinMax*/ + template + void ComputeMinMaxWithOutlierRejection( + const std::map& distribution); void SetUncompressedPixelData(const std::string& pixelData); @@ -62,7 +94,8 @@ public: OrthancMultiframeVolumeLoader(boost::shared_ptr volume, OrthancStone::IOracle& oracle, - OrthancStone::IObservable& oracleObservable); + OrthancStone::IObservable& oracleObservable, + float outliersHalfRejectionRate = 0.0005); virtual ~OrthancMultiframeVolumeLoader(); @@ -71,6 +104,12 @@ return pixelDataLoaded_; } + void GetDistributionMinMax + (float& minValue, float& maxValue) const; + + void GetDistributionMinMaxWithOutliersRejection + (float& minValue, float& maxValue) const; + void LoadInstance(const std::string& instanceId); }; } diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Loaders/OrthancSeriesVolumeProgressiveLoader.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Loaders/OrthancSeriesVolumeProgressiveLoader.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Messages/LockingEmitter.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/SmartLoader.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/SmartLoader.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Toolbox/IWebService.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Toolbox/OrthancApiClient.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Toolbox/OrthancApiClient.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Toolbox/OrthancSlicesLoader.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Toolbox/OrthancSlicesLoader.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Viewport/IViewport.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Viewport/WidgetViewport.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Viewport/WidgetViewport.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Volumes/ISlicedVolume.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Volumes/IVolumeLoader.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Volumes/StructureSetLoader.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Volumes/StructureSetLoader.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Widgets/LayoutWidget.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Widgets/LayoutWidget.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Widgets/SliceViewerWidget.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Deprecated/Widgets/SliceViewerWidget.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Fonts/GlyphTextureAlphabet.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Messages/ICallable.h --- a/Framework/Messages/ICallable.h Fri Jan 31 17:34:57 2020 +0100 +++ b/Framework/Messages/ICallable.h Tue Feb 04 15:20:08 2020 +0100 @@ -30,6 +30,7 @@ #include #include +#include namespace OrthancStone { diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Messages/IMessage.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Messages/IMessageEmitter.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Messages/IObservable.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Messages/IObservable.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Messages/IObserver.h --- a/Framework/Messages/IObserver.h Fri Jan 31 17:34:57 2020 +0100 +++ b/Framework/Messages/IObserver.h Tue Feb 04 15:20:08 2020 +0100 @@ -23,6 +23,8 @@ #include +#include + namespace OrthancStone { class IObserver : public boost::noncopyable diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/OpenGL/OpenGLIncludes.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/OpenGL/SdlOpenGLContext.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/OpenGL/SdlOpenGLContext.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Oracle/GetOrthancImageCommand.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Oracle/GetOrthancImageCommand.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Oracle/GetOrthancWebViewerJpegCommand.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Oracle/GetOrthancWebViewerJpegCommand.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Oracle/HttpCommand.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Oracle/HttpCommand.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Oracle/IOracle.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Oracle/IOracleCommand.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Oracle/OracleCommandBase.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Oracle/OracleCommandBase.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Oracle/OracleCommandExceptionMessage.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Oracle/OrthancRestApiCommand.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Oracle/OrthancRestApiCommand.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Oracle/SleepOracleCommand.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Oracle/ThreadedOracle.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Oracle/ThreadedOracle.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Oracle/WebAssemblyOracle.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Oracle/WebAssemblyOracle.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Radiography/RadiographyDicomLayer.cpp --- a/Framework/Radiography/RadiographyDicomLayer.cpp Fri Jan 31 17:34:57 2020 +0100 +++ b/Framework/Radiography/RadiographyDicomLayer.cpp Tue Feb 04 15:20:08 2020 +0100 @@ -113,7 +113,7 @@ BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); } - void RadiographyDicomLayer::SetSourceImage(Orthanc::ImageAccessor* image, double newPixelSpacingX, double newPixelSpacingY) // Takes ownership + void RadiographyDicomLayer::SetSourceImage(Orthanc::ImageAccessor* image, double newPixelSpacingX, double newPixelSpacingY, bool emitLayerEditedEvent) // Takes ownership { std::auto_ptr raii(image); @@ -122,14 +122,17 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); } - SetSize(image->GetWidth(), image->GetHeight()); + SetSize(image->GetWidth(), image->GetHeight(), false); source_ = raii; ApplyConverter(); SetPixelSpacing(newPixelSpacingX, newPixelSpacingY, false); - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + if (emitLayerEditedEvent) + { + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } } diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Radiography/RadiographyDicomLayer.h --- a/Framework/Radiography/RadiographyDicomLayer.h Fri Jan 31 17:34:57 2020 +0100 +++ b/Framework/Radiography/RadiographyDicomLayer.h Tue Feb 04 15:20:08 2020 +0100 @@ -80,7 +80,7 @@ void SetSourceImage(Orthanc::ImageAccessor* image); // Takes ownership - void SetSourceImage(Orthanc::ImageAccessor* image, double newPixelSpacingX, double newPixelSpacingY); // Takes ownership + void SetSourceImage(Orthanc::ImageAccessor* image, double newPixelSpacingX, double newPixelSpacingY, bool emitLayerEditedEvent = true); // Takes ownership const Orthanc::ImageAccessor* GetSourceImage() const {return source_.get();} // currently need this access to serialize scene in plain old data to send to a WASM worker diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Radiography/RadiographyLayer.cpp --- a/Framework/Radiography/RadiographyLayer.cpp Fri Jan 31 17:34:57 2020 +0100 +++ b/Framework/Radiography/RadiographyLayer.cpp Tue Feb 04 15:20:08 2020 +0100 @@ -59,15 +59,15 @@ void RadiographyLayer::UpdateTransform() { + // important to update transform_ before getting the center to use the right scaling !!! transform_ = AffineTransform2D::CreateScaling(geometry_.GetScalingX(), geometry_.GetScalingY()); double centerX, centerY; GetCenter(centerX, centerY); transform_ = AffineTransform2D::Combine( - AffineTransform2D::CreateOffset(geometry_.GetPanX() + centerX, geometry_.GetPanY() + centerY), - AffineTransform2D::CreateRotation(geometry_.GetAngle()), - AffineTransform2D::CreateOffset(-centerX, -centerY), + AffineTransform2D::CreateOffset(geometry_.GetPanX(), geometry_.GetPanY()), + AffineTransform2D::CreateRotation(geometry_.GetAngle(), centerX, centerY), transform_); transformInverse_ = AffineTransform2D::Invert(transform_); @@ -222,14 +222,19 @@ } void RadiographyLayer::SetSize(unsigned int width, - unsigned int height) + unsigned int height, + bool emitLayerEditedEvent) { hasSize_ = true; width_ = width; height_ = height; UpdateTransform(); - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + + if (emitLayerEditedEvent) + { + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } } diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Radiography/RadiographyLayer.h --- a/Framework/Radiography/RadiographyLayer.h Fri Jan 31 17:34:57 2020 +0100 +++ b/Framework/Radiography/RadiographyLayer.h Tue Feb 04 15:20:08 2020 +0100 @@ -217,16 +217,6 @@ const RadiographyScene& scene_; protected: - virtual const AffineTransform2D& GetTransform() const - { - return transform_; - } - - virtual const AffineTransform2D& GetTransformInverse() const - { - return transformInverse_; - } - void SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode prefferedPhotometricDisplayMode); private: @@ -254,6 +244,16 @@ { } + virtual const AffineTransform2D& GetTransform() const + { + return transform_; + } + + virtual const AffineTransform2D& GetTransformInverse() const + { + return transformInverse_; + } + size_t GetIndex() const { return index_; @@ -298,7 +298,8 @@ } void SetSize(unsigned int width, - unsigned int height); + unsigned int height, + bool emitLayerEditedEvent = true); bool HasSize() const { @@ -358,8 +359,6 @@ virtual bool GetRange(float& minValue, float& maxValue) const = 0; - friend class RadiographyMaskLayer; // because it needs to GetTransform on the dicomLayer it relates to - virtual size_t GetApproximateMemoryUsage() const // this is used to limit the number of scenes loaded in RAM when resources are limited (we actually only count the size used by the images, not the C structs) { return 0; diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Radiography/RadiographyMaskLayer.cpp --- a/Framework/Radiography/RadiographyMaskLayer.cpp Fri Jan 31 17:34:57 2020 +0100 +++ b/Framework/Radiography/RadiographyMaskLayer.cpp Tue Feb 04 15:20:08 2020 +0100 @@ -82,7 +82,7 @@ float windowWidth, bool applyWindowing) const { - if (dicomLayer_.GetWidth() == 0) // nothing to do if the DICOM layer is not displayed (or not loaded) + if (dicomLayer_.GetWidth() == 0 || dicomLayer_.GetSourceImage() == NULL) // nothing to do if the DICOM layer is not displayed (or not loaded) return; if (invalidated_) diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Radiography/RadiographyScene.cpp --- a/Framework/Radiography/RadiographyScene.cpp Fri Jan 31 17:34:57 2020 +0100 +++ b/Framework/Radiography/RadiographyScene.cpp Tue Feb 04 15:20:08 2020 +0100 @@ -125,6 +125,17 @@ } } + void RadiographyScene::_RegisterLayer(RadiographyLayer* layer) + { + std::auto_ptr raii(layer); + + // LOG(INFO) << "Registering layer: " << countLayers_; + + size_t index = nextLayerIndex_++; + raii->SetIndex(index); + layers_[index] = raii.release(); + } + RadiographyLayer& RadiographyScene::RegisterLayer(RadiographyLayer* layer) { if (layer == NULL) @@ -132,11 +143,7 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); } - std::auto_ptr raii(layer); - - size_t index = nextLayerIndex_++; - raii->SetIndex(index); - layers_[index] = raii.release(); + _RegisterLayer(layer); BroadcastMessage(GeometryChangedMessage(*this, *layer)); BroadcastMessage(ContentChangedMessage(*this, *layer)); @@ -221,6 +228,8 @@ LOG(INFO) << "Removing layer, there are now : " << layers_.size() << " layers"; + _OnLayerRemoved(); + BroadcastMessage(RadiographyScene::LayerRemovedMessage(*this, layerIndex)); } } @@ -556,11 +565,24 @@ // Render layers in the background-to-foreground order for (size_t index = 0; index < nextLayerIndex_; index++) { - Layers::const_iterator it = layers_.find(index); - if (it != layers_.end()) + try { - assert(it->second != NULL); - it->second->Render(buffer, viewTransform, interpolation, windowingCenter_, windowingWidth_, applyWindowing); + Layers::const_iterator it = layers_.find(index); + if (it != layers_.end()) + { + assert(it->second != NULL); + it->second->Render(buffer, viewTransform, interpolation, windowingCenter_, windowingWidth_, applyWindowing); + } + } + catch (Orthanc::OrthancException& ex) + { + LOG(ERROR) << "RadiographyScene::Render: " << index << ", OrthancException: " << ex.GetDetails(); + throw ex; // rethrow because we want it to crash to see there's a problem ! + } + catch (...) + { + LOG(ERROR) << "RadiographyScene::Render: " << index << ", unkown exception: "; + throw; // rethrow because we want it to crash to see there's a problem ! } } } @@ -637,6 +659,28 @@ } } + void RadiographyScene::ExtractLayerFromRenderedScene(Orthanc::ImageAccessor& layer, + const Orthanc::ImageAccessor& renderedScene, + size_t layerIndex, + ImageInterpolation interpolation) + { + Extent2D sceneExtent = GetSceneExtent(); + + double pixelSpacingX = sceneExtent.GetWidth() / renderedScene.GetWidth(); + double pixelSpacingY = sceneExtent.GetHeight() / renderedScene.GetHeight(); + + AffineTransform2D view = AffineTransform2D::Combine( + AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY), + AffineTransform2D::CreateOffset(-sceneExtent.GetX1(), -sceneExtent.GetY1())); + + AffineTransform2D layerToSceneTransform = AffineTransform2D::Combine( + view, + GetLayer(layerIndex).GetTransform()); + + AffineTransform2D sceneToLayerTransform = AffineTransform2D::Invert(layerToSceneTransform); + sceneToLayerTransform.Apply(layer, renderedScene, interpolation, false); + } + Orthanc::Image* RadiographyScene::ExportToImage(double pixelSpacingX, double pixelSpacingY, ImageInterpolation interpolation, @@ -669,7 +713,14 @@ AffineTransform2D::CreateOffset(-extent.GetX1(), -extent.GetY1())); // wipe background before rendering - Orthanc::ImageProcessing::Set(layers, 0); + if (GetPreferredPhotomotricDisplayMode() == RadiographyPhotometricDisplayMode_Monochrome1) + { + Orthanc::ImageProcessing::Set(layers, 65535.0f); + } + else + { + Orthanc::ImageProcessing::Set(layers, 0); + } Render(layers, view, interpolation, applyWindowing); diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Radiography/RadiographyScene.h --- a/Framework/Radiography/RadiographyScene.h Fri Jan 31 17:34:57 2020 +0100 +++ b/Framework/Radiography/RadiographyScene.h Tue Feb 04 15:20:08 2020 +0100 @@ -37,6 +37,7 @@ public ObserverBase, public IObservable { + friend class RadiographySceneGeometryReader; public: class GeometryChangedMessage : public OriginMessage { @@ -168,8 +169,17 @@ float windowingWidth_; Layers layers_; + public: + RadiographyLayer& RegisterLayer(RadiographyLayer* layer); + protected: - RadiographyLayer& RegisterLayer(RadiographyLayer* layer); + virtual void _RegisterLayer(RadiographyLayer* layer); + virtual void _OnLayerRemoved() {} + + void SetLayerIndex(RadiographyLayer* layer, size_t index) + { + layer->SetIndex(index); + } virtual void OnTagsReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message); @@ -341,5 +351,9 @@ int64_t maxValue /* for inversion */, bool applyWindowing); + void ExtractLayerFromRenderedScene(Orthanc::ImageAccessor& layer, + const Orthanc::ImageAccessor& renderedScene, + size_t layerIndex, + ImageInterpolation interpolation); }; } diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Radiography/RadiographySceneReader.cpp --- a/Framework/Radiography/RadiographySceneReader.cpp Fri Jan 31 17:34:57 2020 +0100 +++ b/Framework/Radiography/RadiographySceneReader.cpp Tue Feb 04 15:20:08 2020 +0100 @@ -50,7 +50,17 @@ RadiographyDicomLayer* RadiographySceneReader::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry) { - return dynamic_cast(&(scene_.LoadDicomFrame(*orthancApiClient_, instanceId, frame, false, geometry))); + return dynamic_cast(&(scene_.LoadDicomFrame(orthancApiClient_, instanceId, frame, false, geometry))); + } + + RadiographyDicomLayer* RadiographySceneGeometryReader::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry) + { + std::auto_ptr layer(new RadiographyPlaceholderLayer(scene_)); + layer->SetGeometry(*geometry); + layer->SetSize(dicomImageWidth_, dicomImageHeight_); + scene_.RegisterLayer(layer.get()); + + return layer.release(); } void RadiographySceneBuilder::Read(const Json::Value& input) @@ -130,82 +140,8 @@ } } - void RadiographySceneReader::Read(const Json::Value& input) - { - unsigned int version = input["version"].asUInt(); - - if (version != 1) - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - - if (input.isMember("hasWindowing") && input["hasWindowing"].asBool()) - { - scene_.SetWindowing(input["windowCenter"].asFloat(), input["windowWidth"].asFloat()); - } - - RadiographyDicomLayer* dicomLayer = NULL; - for(size_t layerIndex = 0; layerIndex < input["layers"].size(); layerIndex++) - { - const Json::Value& jsonLayer = input["layers"][(int)layerIndex]; - RadiographyLayer::Geometry geometry; - - if (jsonLayer["type"].asString() == "dicom") - { - ReadLayerGeometry(geometry, jsonLayer); - dicomLayer = dynamic_cast(&(scene_.LoadDicomFrame(*orthancApiClient_, jsonLayer["instanceId"].asString(), jsonLayer["frame"].asUInt(), false, &geometry))); - } - else if (jsonLayer["type"].asString() == "mask") - { - if (dicomLayer == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); // we always assumed the dicom layer was read before the mask - } - ReadLayerGeometry(geometry, jsonLayer); - float foreground = jsonLayer["foreground"].asFloat(); - std::vector corners; - for (size_t i = 0; i < jsonLayer["corners"].size(); i++) - { - Orthanc::ImageProcessing::ImagePoint corner(jsonLayer["corners"][(int)i]["x"].asInt(), - jsonLayer["corners"][(int)i]["y"].asInt()); - corners.push_back(corner); - } - scene_.LoadMask(corners, *dicomLayer, foreground, &geometry); - } - else if (jsonLayer["type"].asString() == "text") - { - ReadLayerGeometry(geometry, jsonLayer); - scene_.LoadText(jsonLayer["text"].asString(), jsonLayer["font"].asString(), jsonLayer["fontSize"].asUInt(), static_cast(jsonLayer["foreground"].asUInt()), &geometry, false); - } - else if (jsonLayer["type"].asString() == "alpha") - { - ReadLayerGeometry(geometry, jsonLayer); - - const std::string& pngContentBase64 = jsonLayer["content"].asString(); - std::string pngContent; - std::string mimeType; - Orthanc::Toolbox::DecodeDataUriScheme(mimeType, pngContent, pngContentBase64); - - std::auto_ptr image; - if (mimeType == "image/png") - { - image.reset(new Orthanc::PngReader()); - dynamic_cast(image.get())->ReadFromMemory(pngContent); - } - else - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - - RadiographyAlphaLayer& layer = dynamic_cast(scene_.LoadAlphaBitmap(image.release(), &geometry)); - - if (!jsonLayer["isUsingWindowing"].asBool()) - { - layer.SetForegroundValue((float)(jsonLayer["foreground"].asDouble())); - } - } - else - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - } void RadiographySceneBuilder::ReadDicomLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& input) { diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Radiography/RadiographySceneReader.h --- a/Framework/Radiography/RadiographySceneReader.h Fri Jan 31 17:34:57 2020 +0100 +++ b/Framework/Radiography/RadiographySceneReader.h Tue Feb 04 15:20:08 2020 +0100 @@ -33,6 +33,18 @@ namespace OrthancStone { + // a layer containing only the geometry of a DICOM layer (bit hacky !) + class RadiographyPlaceholderLayer : public RadiographyDicomLayer + { + public: + RadiographyPlaceholderLayer(const RadiographyScene& scene) : + RadiographyDicomLayer(scene) + { + } + + }; + + // HACK: I had to introduce this builder class in order to be able to recreate a RadiographyScene // from a serialized scene that is passed to web-workers. // It needs some architecturing... @@ -68,18 +80,32 @@ class RadiographySceneReader : public RadiographySceneBuilder { - private: - boost::shared_ptr orthancApiClient_; + Deprecated::OrthancApiClient& orthancApiClient_; public: - RadiographySceneReader(RadiographyScene& scene, - boost::shared_ptr orthancApiClient) : + RadiographySceneReader(RadiographyScene& scene, Deprecated::OrthancApiClient& orthancApiClient) : RadiographySceneBuilder(scene), orthancApiClient_(orthancApiClient) { } - void Read(const Json::Value& input); + protected: + virtual RadiographyDicomLayer* LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry); + }; + + // reads the whole scene but the DICOM image such that we have the full geometry + class RadiographySceneGeometryReader : public RadiographySceneBuilder + { + unsigned int dicomImageWidth_; + unsigned int dicomImageHeight_; + + public: + RadiographySceneGeometryReader(RadiographyScene& scene, unsigned int dicomImageWidth, unsigned int dicomImageHeight) : + RadiographySceneBuilder(scene), + dicomImageWidth_(dicomImageWidth), + dicomImageHeight_(dicomImageHeight) + { + } protected: virtual RadiographyDicomLayer* LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry); diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Radiography/RadiographyWidget.cpp --- a/Framework/Radiography/RadiographyWidget.cpp Fri Jan 31 17:34:57 2020 +0100 +++ b/Framework/Radiography/RadiographyWidget.cpp Tue Feb 04 15:20:08 2020 +0100 @@ -76,6 +76,11 @@ { floatBuffer_.reset(new Orthanc::Image( Orthanc::PixelFormat_Float32, width, height, false)); + + if (floatBuffer_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory, "RadiographyWidget::RenderInternal: unable to allocate float buffer"); + } } if (cairoBuffer_.get() == NULL || @@ -83,6 +88,11 @@ cairoBuffer_->GetHeight() != height) { cairoBuffer_.reset(new CairoSurface(width, height, false /* no alpha */)); + + if (cairoBuffer_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory, "RadiographyWidget::RenderInternal: unable to allocate cairo buffer"); + } } RenderBackground(*floatBuffer_, 0.0, 65535.0); @@ -188,6 +198,8 @@ void RadiographyWidget::Unselect() { hasSelection_ = false; + + NotifyContentChanged(); BroadcastMessage(SelectionChangedMessage(*this)); } diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Radiography/RadiographyWidget.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/CairoCompositor.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/CairoCompositor.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/FloatTextureSceneLayer.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/FloatTextureSceneLayer.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/GrayscaleStyleConfigurator.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/GrayscaleStyleConfigurator.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/Internals/CairoFloatTextureRenderer.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/Internals/CairoLookupTableTextureRenderer.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/Internals/CompositorHelper.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/Internals/CompositorHelper.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/Internals/OpenGLFloatTextureRenderer.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/Internals/OpenGLLookupTableTextureRenderer.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/LookupTableStyleConfigurator.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/LookupTableStyleConfigurator.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/LookupTableTextureSceneLayer.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/LookupTableTextureSceneLayer.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/OpenGLCompositor.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/OpenGLCompositor.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/PointerEvent.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/PointerEvent.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/Scene2D.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/Scene2D.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2D/ScenePoint2D.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/AngleMeasureTool.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/AngleMeasureTool.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/CreateAngleMeasureCommand.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/CreateAngleMeasureCommand.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/CreateAngleMeasureTracker.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/CreateAngleMeasureTracker.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/CreateLineMeasureCommand.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/CreateLineMeasureCommand.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/CreateLineMeasureTracker.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/CreateLineMeasureTracker.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/EditAngleMeasureCommand.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/EditAngleMeasureCommand.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/EditAngleMeasureTracker.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/EditAngleMeasureTracker.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/EditLineMeasureCommand.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/EditLineMeasureCommand.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/EditLineMeasureTracker.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/EditLineMeasureTracker.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/LayerHolder.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/LayerHolder.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/LineMeasureTool.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/LineMeasureTool.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/MeasureCommands.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/MeasureTool.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/MeasureTool.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/MeasureToolsToolbox.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/MeasureTrackers.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/MeasureTrackers.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/OneGesturePointerTracker.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/ViewportController.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Scene2DViewport/ViewportController.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/StoneEnumerations.h --- a/Framework/StoneEnumerations.h Fri Jan 31 17:34:57 2020 +0100 +++ b/Framework/StoneEnumerations.h Tue Feb 04 15:20:08 2020 +0100 @@ -86,7 +86,20 @@ KeyboardKeys_Up = 38, KeyboardKeys_Right = 39, KeyboardKeys_Down = 40, - KeyboardKeys_Delete = 46 + KeyboardKeys_Delete = 46, + + KeyboardKeys_F1 = 112, + KeyboardKeys_F2 = 113, + KeyboardKeys_F3 = 114, + KeyboardKeys_F4 = 115, + KeyboardKeys_F5 = 116, + KeyboardKeys_F6 = 117, + KeyboardKeys_F7 = 118, + KeyboardKeys_F8 = 119, + KeyboardKeys_F9 = 120, + KeyboardKeys_F10 = 121, + KeyboardKeys_F11 = 122, + KeyboardKeys_F12 = 123, }; enum SopClassUid diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/StoneInitialization.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/StoneInitialization.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Toolbox/AffineTransform2D.cpp --- a/Framework/Toolbox/AffineTransform2D.cpp Fri Jan 31 17:34:57 2020 +0100 +++ b/Framework/Toolbox/AffineTransform2D.cpp Tue Feb 04 15:20:08 2020 +0100 @@ -246,6 +246,16 @@ return t; } + AffineTransform2D AffineTransform2D::CreateRotation(double angle, // CW rotation + double cx, // rotation center + double cy) // rotation center + { + return Combine( + CreateOffset(cx, cy), + CreateRotation(angle), + CreateOffset(-cx, -cy) + ); + } AffineTransform2D AffineTransform2D::CreateOpenGLClipspace(unsigned int canvasWidth, unsigned int canvasHeight) diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Toolbox/AffineTransform2D.h --- a/Framework/Toolbox/AffineTransform2D.h Fri Jan 31 17:34:57 2020 +0100 +++ b/Framework/Toolbox/AffineTransform2D.h Tue Feb 04 15:20:08 2020 +0100 @@ -90,8 +90,12 @@ static AffineTransform2D CreateScaling(double sx, double sy); - - static AffineTransform2D CreateRotation(double angle); + + static AffineTransform2D CreateRotation(double angle); // CW rotation in radians + + static AffineTransform2D CreateRotation(double angle, // CW rotation in radians + double cx, // rotation center + double cy); // rotation center static AffineTransform2D CreateOpenGLClipspace(unsigned int canvasWidth, unsigned int canvasHeight); diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Toolbox/CoordinateSystem3D.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Toolbox/CoordinateSystem3D.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Toolbox/DicomInstanceParameters.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Toolbox/DicomInstanceParameters.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Toolbox/DicomStructureSet.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Toolbox/DicomStructureSet.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Toolbox/GenericToolbox.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Toolbox/LinearAlgebra.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Toolbox/SlicesSorter.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Toolbox/SlicesSorter.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Viewport/IViewport.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Viewport/SdlViewport.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Viewport/SdlViewport.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Viewport/SdlWindow.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Viewport/SdlWindow.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Viewport/WebAssemblyViewport.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Viewport/WebAssemblyViewport.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Volumes/DicomVolumeImage.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Volumes/DicomVolumeImageMPRSlicer.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Framework/Volumes/VolumeImageGeometry.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Platforms/Generic/OracleWebService.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Platforms/Generic/WebServiceCommandBase.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Platforms/Generic/WebServiceCommandBase.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Platforms/Generic/WebServiceDeleteCommand.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Platforms/Generic/WebServiceDeleteCommand.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Platforms/Generic/WebServiceGetCommand.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Platforms/Generic/WebServiceGetCommand.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Platforms/Generic/WebServicePostCommand.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 Platforms/Generic/WebServicePostCommand.h diff -r 0ca50d275b9a -r 7ec8fea061b9 Resources/CMake/OrthancStoneConfiguration.cmake diff -r 0ca50d275b9a -r 7ec8fea061b9 Resources/CMake/QtConfiguration.cmake diff -r 0ca50d275b9a -r 7ec8fea061b9 UnitTestsSources/GenericToolboxTests.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 UnitTestsSources/TestMessageBroker.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 UnitTestsSources/TestStructureSet.cpp diff -r 0ca50d275b9a -r 7ec8fea061b9 UnitTestsSources/UnitTestsMain.cpp