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() :