Mercurial > hg > orthanc-stone
diff Samples/Sdl/Loader.cpp @ 815:df442f1ba0c6
reorganization
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 28 May 2019 21:59:20 +0200 |
parents | aead999345e0 |
children | 270c31978df1 2fd96a637a59 |
line wrap: on
line diff
--- a/Samples/Sdl/Loader.cpp Tue May 28 21:16:39 2019 +0200 +++ b/Samples/Sdl/Loader.cpp Tue May 28 21:59:20 2019 +0200 @@ -19,1091 +19,35 @@ **/ +#include "../../Framework/Loaders/DicomStructureSetLoader.h" +#include "../../Framework/Loaders/OrthancMultiframeVolumeLoader.h" #include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h" -#include "../../Framework/Volumes/DicomVolumeImageMPRSlicer.h" +#include "../../Framework/Oracle/SleepOracleCommand.h" +#include "../../Framework/Oracle/ThreadedOracle.h" +#include "../../Framework/Scene2D/CairoCompositor.h" #include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h" #include "../../Framework/Scene2D/LookupTableStyleConfigurator.h" -#include "../../Framework/Oracle/ThreadedOracle.h" -#include "../../Framework/Oracle/GetOrthancWebViewerJpegCommand.h" -#include "../../Framework/Oracle/GetOrthancImageCommand.h" -#include "../../Framework/Oracle/OrthancRestApiCommand.h" -#include "../../Framework/Oracle/SleepOracleCommand.h" -#include "../../Framework/Oracle/OracleCommandExceptionMessage.h" - -// From Stone -#include "../../Framework/Loaders/BasicFetchingItemsSorter.h" -#include "../../Framework/Loaders/BasicFetchingStrategy.h" -#include "../../Framework/Scene2D/CairoCompositor.h" -#include "../../Framework/Scene2D/Scene2D.h" -#include "../../Framework/Scene2D/PolylineSceneLayer.h" -#include "../../Framework/Scene2D/LookupTableTextureSceneLayer.h" #include "../../Framework/StoneInitialization.h" -#include "../../Framework/Toolbox/GeometryToolbox.h" -#include "../../Framework/Toolbox/SlicesSorter.h" -#include "../../Framework/Toolbox/DicomStructureSet.h" -#include "../../Framework/Volumes/ImageBuffer3D.h" -#include "../../Framework/Volumes/VolumeImageGeometry.h" -#include "../../Framework/Volumes/VolumeReslicer.h" +#include "../../Framework/Volumes/VolumeSceneLayerSource.h" +#include "../../Framework/Volumes/DicomVolumeImageMPRSlicer.h" +#include "../../Framework/Volumes/DicomVolumeImageReslicer.h" // From Orthanc framework -#include <Core/DicomFormat/DicomArray.h> -#include <Core/Images/Image.h> #include <Core/Images/ImageProcessing.h> #include <Core/Images/PngWriter.h> -#include <Core/Endianness.h> #include <Core/Logging.h> #include <Core/OrthancException.h> #include <Core/SystemToolbox.h> -#include <Core/Toolbox.h> - - -#include <EmbeddedResources.h> namespace OrthancStone { - /** - This class is supplied with Oracle commands and will schedule up to - simultaneousDownloads_ of them at the same time, then will schedule the - rest once slots become available. It is used, a.o., by the - OrtancMultiframeVolumeLoader class. - */ - class LoaderStateMachine : public IObserver - { - protected: - class State : public Orthanc::IDynamicObject - { - private: - LoaderStateMachine& that_; - - public: - State(LoaderStateMachine& that) : - that_(that) - { - } - - State(const State& currentState) : - that_(currentState.that_) - { - } - - void Schedule(OracleCommandWithPayload* command) const - { - that_.Schedule(command); - } - - template <typename T> - T& GetLoader() const - { - return dynamic_cast<T&>(that_); - } - - virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - - virtual void Handle(const GetOrthancImageCommand::SuccessMessage& message) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - - virtual void Handle(const GetOrthancWebViewerJpegCommand::SuccessMessage& message) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - }; - - void Schedule(OracleCommandWithPayload* command) - { - std::auto_ptr<OracleCommandWithPayload> protection(command); - - if (command == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - - if (!command->HasPayload()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, - "The payload must contain the next state"); - } - - pendingCommands_.push_back(protection.release()); - Step(); - } - - void Start() - { - if (active_) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - active_ = true; - - for (size_t i = 0; i < simultaneousDownloads_; i++) - { - Step(); - } - } - - private: - void Step() - { - if (!pendingCommands_.empty() && - activeCommands_ < simultaneousDownloads_) - { - oracle_.Schedule(*this, pendingCommands_.front()); - pendingCommands_.pop_front(); - - activeCommands_++; - } - } - - void Clear() - { - for (PendingCommands::iterator it = pendingCommands_.begin(); - it != pendingCommands_.end(); ++it) - { - delete *it; - } - - pendingCommands_.clear(); - } - - void HandleExceptionMessage(const OracleCommandExceptionMessage& message) - { - LOG(ERROR) << "Error in the state machine, stopping all processing"; - Clear(); - } - - template <typename T> - void HandleSuccessMessage(const T& message) - { - assert(activeCommands_ > 0); - activeCommands_--; - - try - { - dynamic_cast<State&>(message.GetOrigin().GetPayload()).Handle(message); - Step(); - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "Error in the state machine, stopping all processing: " << e.What(); - Clear(); - } - } - - typedef std::list<IOracleCommand*> PendingCommands; - - IOracle& oracle_; - bool active_; - unsigned int simultaneousDownloads_; - PendingCommands pendingCommands_; - unsigned int activeCommands_; - - public: - LoaderStateMachine(IOracle& oracle, - IObservable& oracleObservable) : - IObserver(oracleObservable.GetBroker()), - oracle_(oracle), - active_(false), - simultaneousDownloads_(4), - activeCommands_(0) - { - oracleObservable.RegisterObserverCallback( - new Callable<LoaderStateMachine, OrthancRestApiCommand::SuccessMessage> - (*this, &LoaderStateMachine::HandleSuccessMessage)); - - oracleObservable.RegisterObserverCallback( - new Callable<LoaderStateMachine, GetOrthancImageCommand::SuccessMessage> - (*this, &LoaderStateMachine::HandleSuccessMessage)); - - oracleObservable.RegisterObserverCallback( - new Callable<LoaderStateMachine, GetOrthancWebViewerJpegCommand::SuccessMessage> - (*this, &LoaderStateMachine::HandleSuccessMessage)); - - oracleObservable.RegisterObserverCallback( - new Callable<LoaderStateMachine, OracleCommandExceptionMessage> - (*this, &LoaderStateMachine::HandleExceptionMessage)); - } - - virtual ~LoaderStateMachine() - { - Clear(); - } - - bool IsActive() const - { - return active_; - } - - void SetSimultaneousDownloads(unsigned int count) - { - if (active_) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else if (count == 0) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - else - { - simultaneousDownloads_ = count; - } - } - }; - - - - class OrthancMultiframeVolumeLoader : - public LoaderStateMachine, - public IObservable - { - private: - class LoadRTDoseGeometry : public State - { - private: - std::auto_ptr<Orthanc::DicomMap> dicom_; - - public: - LoadRTDoseGeometry(OrthancMultiframeVolumeLoader& that, - Orthanc::DicomMap* dicom) : - State(that), - dicom_(dicom) - { - if (dicom == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - - } - - virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) - { - // Complete the DICOM tags with just-received "Grid Frame Offset Vector" - std::string s = Orthanc::Toolbox::StripSpaces(message.GetAnswer()); - dicom_->SetValue(Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR, s, false); - - GetLoader<OrthancMultiframeVolumeLoader>().SetGeometry(*dicom_); - } - }; - - - static std::string GetSopClassUid(const Orthanc::DicomMap& dicom) - { - std::string s; - if (!dicom.CopyToString(s, Orthanc::DICOM_TAG_SOP_CLASS_UID, false)) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, - "DICOM file without SOP class UID"); - } - else - { - return s; - } - } - - - class LoadGeometry : public State - { - public: - LoadGeometry(OrthancMultiframeVolumeLoader& that) : - State(that) - { - } - - virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) - { - OrthancMultiframeVolumeLoader& loader = GetLoader<OrthancMultiframeVolumeLoader>(); - - Json::Value body; - message.ParseJsonBody(body); - - if (body.type() != Json::objectValue) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); - } - - std::auto_ptr<Orthanc::DicomMap> dicom(new Orthanc::DicomMap); - dicom->FromDicomAsJson(body); - - if (StringToSopClassUid(GetSopClassUid(*dicom)) == SopClassUid_RTDose) - { - // Download the "Grid Frame Offset Vector" DICOM tag, that is - // mandatory for RT-DOSE, but is too long to be returned by default - - std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); - command->SetUri("/instances/" + loader.GetInstanceId() + "/content/" + - Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR.Format()); - command->SetPayload(new LoadRTDoseGeometry(loader, dicom.release())); - - Schedule(command.release()); - } - else - { - loader.SetGeometry(*dicom); - } - } - }; - - - - class LoadTransferSyntax : public State - { - public: - LoadTransferSyntax(OrthancMultiframeVolumeLoader& that) : - State(that) - { - } - - virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) - { - GetLoader<OrthancMultiframeVolumeLoader>().SetTransferSyntax(message.GetAnswer()); - } - }; - - - class LoadUncompressedPixelData : public State - { - public: - LoadUncompressedPixelData(OrthancMultiframeVolumeLoader& that) : - State(that) - { - } - - virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) - { - GetLoader<OrthancMultiframeVolumeLoader>().SetUncompressedPixelData(message.GetAnswer()); - } - }; - - - - boost::shared_ptr<DicomVolumeImage> volume_; - std::string instanceId_; - std::string transferSyntaxUid_; - - - const std::string& GetInstanceId() const - { - if (IsActive()) - { - return instanceId_; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - } - - - void ScheduleFrameDownloads() - { - if (transferSyntaxUid_.empty() || - !volume_->HasGeometry()) - { - return; - } - /* - 1.2.840.10008.1.2 Implicit VR Endian: Default Transfer Syntax for DICOM - 1.2.840.10008.1.2.1 Explicit VR Little Endian - 1.2.840.10008.1.2.2 Explicit VR Big Endian - - See https://www.dicomlibrary.com/dicom/transfer-syntax/ - */ - if (transferSyntaxUid_ == "1.2.840.10008.1.2" || - transferSyntaxUid_ == "1.2.840.10008.1.2.1" || - transferSyntaxUid_ == "1.2.840.10008.1.2.2") - { - std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); - command->SetHttpHeader("Accept-Encoding", "gzip"); - command->SetUri("/instances/" + instanceId_ + "/content/" + - Orthanc::DICOM_TAG_PIXEL_DATA.Format() + "/0"); - command->SetPayload(new LoadUncompressedPixelData(*this)); - Schedule(command.release()); - } - else - { - throw Orthanc::OrthancException( - Orthanc::ErrorCode_NotImplemented, - "No support for multiframe instances with transfer syntax: " + transferSyntaxUid_); - } - } - - - void SetTransferSyntax(const std::string& transferSyntax) - { - transferSyntaxUid_ = Orthanc::Toolbox::StripSpaces(transferSyntax); - ScheduleFrameDownloads(); - } - - - void SetGeometry(const Orthanc::DicomMap& dicom) - { - DicomInstanceParameters parameters(dicom); - volume_->SetDicomParameters(parameters); - - Orthanc::PixelFormat format; - if (!parameters.GetImageInformation().ExtractPixelFormat(format, true)) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - - double spacingZ; - switch (parameters.GetSopClassUid()) - { - case SopClassUid_RTDose: - spacingZ = parameters.GetThickness(); - break; - - default: - throw Orthanc::OrthancException( - Orthanc::ErrorCode_NotImplemented, - "No support for multiframe instances with SOP class UID: " + GetSopClassUid(dicom)); - } - - const unsigned int width = parameters.GetImageInformation().GetWidth(); - const unsigned int height = parameters.GetImageInformation().GetHeight(); - const unsigned int depth = parameters.GetImageInformation().GetNumberOfFrames(); - - { - VolumeImageGeometry geometry; - geometry.SetSize(width, height, depth); - geometry.SetAxialGeometry(parameters.GetGeometry()); - geometry.SetVoxelDimensions(parameters.GetPixelSpacingX(), - parameters.GetPixelSpacingY(), spacingZ); - volume_->Initialize(geometry, format); - } - - volume_->GetPixelData().Clear(); - - ScheduleFrameDownloads(); - - BroadcastMessage(DicomVolumeImage::GeometryReadyMessage(*volume_)); - } - - - ORTHANC_FORCE_INLINE - static void CopyPixel(uint32_t& target, - const void* source) - { - // TODO - check alignement? - target = le32toh(*reinterpret_cast<const uint32_t*>(source)); - } - - - template <typename T> - void CopyPixelData(const std::string& pixelData) - { - ImageBuffer3D& target = volume_->GetPixelData(); - - const unsigned int bpp = target.GetBytesPerPixel(); - const unsigned int width = target.GetWidth(); - const unsigned int height = target.GetHeight(); - const unsigned int depth = target.GetDepth(); - - if (pixelData.size() != bpp * width * height * depth) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, - "The pixel data has not the proper size"); - } - - if (pixelData.empty()) - { - return; - } - - const uint8_t* source = reinterpret_cast<const uint8_t*>(pixelData.c_str()); - - for (unsigned int z = 0; z < depth; z++) - { - ImageBuffer3D::SliceWriter writer(target, VolumeProjection_Axial, z); - - assert (writer.GetAccessor().GetWidth() == width && - writer.GetAccessor().GetHeight() == height); - - for (unsigned int y = 0; y < height; y++) - { - assert(sizeof(T) == Orthanc::GetBytesPerPixel(target.GetFormat())); - - T* target = reinterpret_cast<T*>(writer.GetAccessor().GetRow(y)); - - for (unsigned int x = 0; x < width; x++) - { - CopyPixel(*target, source); - - target ++; - source += bpp; - } - } - } - } - - - void SetUncompressedPixelData(const std::string& pixelData) - { - switch (volume_->GetPixelData().GetFormat()) - { - case Orthanc::PixelFormat_Grayscale32: - CopyPixelData<uint32_t>(pixelData); - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - - volume_->IncrementRevision(); - - BroadcastMessage(DicomVolumeImage::ContentUpdatedMessage(*volume_)); - } - - - public: - OrthancMultiframeVolumeLoader(const boost::shared_ptr<DicomVolumeImage>& volume, - IOracle& oracle, - IObservable& oracleObservable) : - LoaderStateMachine(oracle, oracleObservable), - IObservable(oracleObservable.GetBroker()), - volume_(volume) - { - if (volume.get() == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - } - - - void LoadInstance(const std::string& instanceId) - { - Start(); - - instanceId_ = instanceId; - - { - std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); - command->SetHttpHeader("Accept-Encoding", "gzip"); - command->SetUri("/instances/" + instanceId + "/tags"); - command->SetPayload(new LoadGeometry(*this)); - Schedule(command.release()); - } - - { - std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); - command->SetUri("/instances/" + instanceId + "/metadata/TransferSyntax"); - command->SetPayload(new LoadTransferSyntax(*this)); - Schedule(command.release()); - } - } - }; - - - /** - This class is able to supply an extract slice for an arbitrary cutting - plane through a volume image - */ - class DicomVolumeImageReslicer : public IVolumeSlicer - { - private: - class Slice : public IExtractedSlice - { - private: - DicomVolumeImageReslicer& that_; - CoordinateSystem3D cuttingPlane_; - - public: - Slice(DicomVolumeImageReslicer& that, - const CoordinateSystem3D& cuttingPlane) : - that_(that), - cuttingPlane_(cuttingPlane) - { - } - - virtual bool IsValid() - { - return true; - } - - virtual uint64_t GetRevision() - { - return that_.volume_->GetRevision(); - } - - virtual ISceneLayer* CreateSceneLayer(const ILayerStyleConfigurator* configurator, - const CoordinateSystem3D& cuttingPlane) - { - VolumeReslicer& reslicer = that_.reslicer_; - - if (configurator == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, - "Must provide a layer style configurator"); - } - - reslicer.SetOutputFormat(that_.volume_->GetPixelData().GetFormat()); - reslicer.Apply(that_.volume_->GetPixelData(), - that_.volume_->GetGeometry(), - cuttingPlane); - - if (reslicer.IsSuccess()) - { - std::auto_ptr<TextureBaseSceneLayer> layer - (configurator->CreateTextureFromDicom(reslicer.GetOutputSlice(), - that_.volume_->GetDicomParameters())); - if (layer.get() == NULL) - { - return NULL; - } - - double s = reslicer.GetPixelSpacing(); - layer->SetPixelSpacing(s, s); - layer->SetOrigin(reslicer.GetOutputExtent().GetX1() + 0.5 * s, - reslicer.GetOutputExtent().GetY1() + 0.5 * s); - - // TODO - Angle!! - - return layer.release(); - } - else - { - return NULL; - } - } - }; - - boost::shared_ptr<DicomVolumeImage> volume_; - VolumeReslicer reslicer_; - - public: - DicomVolumeImageReslicer(const boost::shared_ptr<DicomVolumeImage>& volume) : - volume_(volume) - { - if (volume.get() == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - } - - ImageInterpolation GetInterpolation() const - { - return reslicer_.GetInterpolation(); - } - - void SetInterpolation(ImageInterpolation interpolation) - { - reslicer_.SetInterpolation(interpolation); - } - - bool IsFastMode() const - { - return reslicer_.IsFastMode(); - } - - void SetFastMode(bool fast) - { - reslicer_.EnableFastMode(fast); - } - - virtual IExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) - { - if (volume_->HasGeometry()) - { - return new Slice(*this, cuttingPlane); - } - else - { - return new IVolumeSlicer::InvalidSlice; - } - } - }; - - - - class DicomStructureSetLoader : - public LoaderStateMachine, - public IVolumeSlicer - { - private: - class AddReferencedInstance : public State - { - private: - std::string instanceId_; - - public: - AddReferencedInstance(DicomStructureSetLoader& that, - const std::string& instanceId) : - State(that), - instanceId_(instanceId) - { - } - - virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) - { - Json::Value tags; - message.ParseJsonBody(tags); - - Orthanc::DicomMap dicom; - dicom.FromDicomAsJson(tags); - - DicomStructureSetLoader& loader = GetLoader<DicomStructureSetLoader>(); - loader.content_->AddReferencedSlice(dicom); - - loader.countProcessedInstances_ ++; - assert(loader.countProcessedInstances_ <= loader.countReferencedInstances_); - - if (loader.countProcessedInstances_ == loader.countReferencedInstances_) - { - // All the referenced instances have been loaded, finalize the RT-STRUCT - loader.content_->CheckReferencedSlices(); - loader.revision_++; - } - } - }; - - // State that converts a "SOP Instance UID" to an Orthanc identifier - class LookupInstance : public State - { - private: - std::string sopInstanceUid_; - - public: - LookupInstance(DicomStructureSetLoader& that, - const std::string& sopInstanceUid) : - State(that), - sopInstanceUid_(sopInstanceUid) - { - } - - virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) - { - DicomStructureSetLoader& loader = GetLoader<DicomStructureSetLoader>(); - - Json::Value lookup; - message.ParseJsonBody(lookup); - - if (lookup.type() != Json::arrayValue || - lookup.size() != 1 || - !lookup[0].isMember("Type") || - !lookup[0].isMember("Path") || - lookup[0]["Type"].type() != Json::stringValue || - lookup[0]["ID"].type() != Json::stringValue || - lookup[0]["Type"].asString() != "Instance") - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); - } - - const std::string instanceId = lookup[0]["ID"].asString(); - - { - std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); - command->SetHttpHeader("Accept-Encoding", "gzip"); - command->SetUri("/instances/" + instanceId + "/tags"); - command->SetPayload(new AddReferencedInstance(loader, instanceId)); - Schedule(command.release()); - } - } - }; - - class LoadStructure : public State - { - public: - LoadStructure(DicomStructureSetLoader& that) : - State(that) - { - } - - virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) - { - DicomStructureSetLoader& loader = GetLoader<DicomStructureSetLoader>(); - - { - OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer()); - loader.content_.reset(new DicomStructureSet(dicom)); - } - - std::set<std::string> instances; - loader.content_->GetReferencedInstances(instances); - - loader.countReferencedInstances_ = instances.size(); - - for (std::set<std::string>::const_iterator - it = instances.begin(); it != instances.end(); ++it) - { - std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); - command->SetUri("/tools/lookup"); - command->SetMethod(Orthanc::HttpMethod_Post); - command->SetBody(*it); - command->SetPayload(new LookupInstance(loader, *it)); - Schedule(command.release()); - } - } - }; - - - - std::auto_ptr<DicomStructureSet> content_; - uint64_t revision_; - std::string instanceId_; - unsigned int countProcessedInstances_; - unsigned int countReferencedInstances_; - - - class Slice : public IExtractedSlice - { - private: - const DicomStructureSet& content_; - uint64_t revision_; - bool isValid_; - - public: - Slice(const DicomStructureSet& content, - uint64_t revision, - const CoordinateSystem3D& cuttingPlane) : - content_(content), - revision_(revision) - { - bool opposite; - - const Vector normal = content.GetNormal(); - isValid_ = ( - GeometryToolbox::IsParallelOrOpposite(opposite, normal, cuttingPlane.GetNormal()) || - GeometryToolbox::IsParallelOrOpposite(opposite, normal, cuttingPlane.GetAxisX()) || - GeometryToolbox::IsParallelOrOpposite(opposite, normal, cuttingPlane.GetAxisY())); - } - - virtual bool IsValid() - { - return isValid_; - } - - virtual uint64_t GetRevision() - { - return revision_; - } - - virtual ISceneLayer* CreateSceneLayer(const ILayerStyleConfigurator* configurator, - const CoordinateSystem3D& cuttingPlane) - { - assert(isValid_); - - std::auto_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); - layer->SetThickness(2); - - for (size_t i = 0; i < content_.GetStructuresCount(); i++) - { - const Color& color = content_.GetStructureColor(i); - - std::vector< std::vector<DicomStructureSet::PolygonPoint> > polygons; - - if (content_.ProjectStructure(polygons, i, cuttingPlane)) - { - for (size_t j = 0; j < polygons.size(); j++) - { - PolylineSceneLayer::Chain chain; - chain.resize(polygons[j].size()); - - for (size_t k = 0; k < polygons[j].size(); k++) - { - chain[k] = ScenePoint2D(polygons[j][k].first, polygons[j][k].second); - } - - layer->AddChain(chain, true /* closed */, color); - } - } - } - - return layer.release(); - } - }; - - public: - DicomStructureSetLoader(IOracle& oracle, - IObservable& oracleObservable) : - LoaderStateMachine(oracle, oracleObservable), - revision_(0), - countProcessedInstances_(0), - countReferencedInstances_(0) - { - } - - - void LoadInstance(const std::string& instanceId) - { - Start(); - - instanceId_ = instanceId; - - { - std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); - command->SetHttpHeader("Accept-Encoding", "gzip"); - command->SetUri("/instances/" + instanceId + "/tags?ignore-length=3006-0050"); - command->SetPayload(new LoadStructure(*this)); - Schedule(command.release()); - } - } - - virtual IExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) - { - if (content_.get() == NULL) - { - // Geometry is not available yet - return new IVolumeSlicer::InvalidSlice; - } - else - { - return new Slice(*content_, revision_, cuttingPlane); - } - } - }; - - - - class VolumeSceneLayerSource : public boost::noncopyable - { - private: - Scene2D& scene_; - int layerDepth_; - boost::shared_ptr<IVolumeSlicer> slicer_; - std::auto_ptr<ILayerStyleConfigurator> configurator_; - std::auto_ptr<CoordinateSystem3D> lastPlane_; - uint64_t lastRevision_; - uint64_t lastConfiguratorRevision_; - - static bool IsSameCuttingPlane(const CoordinateSystem3D& a, - const CoordinateSystem3D& b) - { - // TODO - What if the normal is reversed? - double distance; - return (CoordinateSystem3D::ComputeDistance(distance, a, b) && - LinearAlgebra::IsCloseToZero(distance)); - } - - void ClearLayer() - { - scene_.DeleteLayer(layerDepth_); - lastPlane_.reset(NULL); - } - - public: - VolumeSceneLayerSource(Scene2D& scene, - int layerDepth, - const boost::shared_ptr<IVolumeSlicer>& slicer) : - scene_(scene), - layerDepth_(layerDepth), - slicer_(slicer) - { - if (slicer == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - } - - const IVolumeSlicer& GetSlicer() const - { - return *slicer_; - } - - void RemoveConfigurator() - { - configurator_.reset(); - lastPlane_.reset(); - } - - void SetConfigurator(ILayerStyleConfigurator* configurator) // Takes ownership - { - if (configurator == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - - configurator_.reset(configurator); - - // Invalidate the layer - lastPlane_.reset(NULL); - } - - bool HasConfigurator() const - { - return configurator_.get() != NULL; - } - - ILayerStyleConfigurator& GetConfigurator() const - { - if (configurator_.get() == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - return *configurator_; - } - - void Update(const CoordinateSystem3D& plane) - { - assert(slicer_.get() != NULL); - std::auto_ptr<IVolumeSlicer::IExtractedSlice> slice(slicer_->ExtractSlice(plane)); - - if (slice.get() == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - if (!slice->IsValid()) - { - // The slicer cannot handle this cutting plane: Clear the layer - ClearLayer(); - } - else if (lastPlane_.get() != NULL && - IsSameCuttingPlane(*lastPlane_, plane) && - lastRevision_ == slice->GetRevision()) - { - // The content of the slice has not changed: Don't update the - // layer content, but possibly update its style - - if (configurator_.get() != NULL && - configurator_->GetRevision() != lastConfiguratorRevision_ && - scene_.HasLayer(layerDepth_)) - { - configurator_->ApplyStyle(scene_.GetLayer(layerDepth_)); - } - } - else - { - // Content has changed: An update is needed - lastPlane_.reset(new CoordinateSystem3D(plane)); - lastRevision_ = slice->GetRevision(); - - std::auto_ptr<ISceneLayer> layer(slice->CreateSceneLayer(configurator_.get(), plane)); - if (layer.get() == NULL) - { - ClearLayer(); - } - else - { - if (configurator_.get() != NULL) - { - lastConfiguratorRevision_ = configurator_->GetRevision(); - configurator_->ApplyStyle(*layer); - } - - scene_.SetLayer(layerDepth_, layer.release()); - } - } - } - }; - - - - - class NativeApplicationContext : public IMessageEmitter { private: - boost::shared_mutex mutex_; - MessageBroker broker_; - IObservable oracleObservable_; + boost::shared_mutex mutex_; + MessageBroker broker_; + IObservable oracleObservable_; public: NativeApplicationContext() :