changeset 794:04f518ebd132

LoaderStateMachine
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 28 May 2019 12:26:16 +0200
parents f72b49954f62
children bc20e4c417ec
files Framework/Deprecated/Layers/DicomStructureSetSlicer.cpp Framework/Toolbox/DicomStructureSet.cpp Framework/Toolbox/DicomStructureSet.h Samples/Sdl/Loader.cpp
diffstat 4 files changed, 268 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Deprecated/Layers/DicomStructureSetSlicer.cpp	Tue May 28 10:39:42 2019 +0200
+++ b/Framework/Deprecated/Layers/DicomStructureSetSlicer.cpp	Tue May 28 12:26:16 2019 +0200
@@ -80,7 +80,7 @@
              const OrthancStone::CoordinateSystem3D& plane) :
       plane_(plane)
     {
-      for (size_t k = 0; k < structureSet.GetStructureCount(); k++)
+      for (size_t k = 0; k < structureSet.GetStructuresCount(); k++)
       {
         structures_.push_back(new Structure(structureSet, plane, k));
       }
--- a/Framework/Toolbox/DicomStructureSet.cpp	Tue May 28 10:39:42 2019 +0200
+++ b/Framework/Toolbox/DicomStructureSet.cpp	Tue May 28 12:26:16 2019 +0200
@@ -678,8 +678,8 @@
 
   
   bool DicomStructureSet::ProjectStructure(std::vector< std::vector<PolygonPoint> >& polygons,
-                                           Structure& structure,
-                                           const CoordinateSystem3D& slice)
+                                           const Structure& structure,
+                                           const CoordinateSystem3D& slice) const
   {
     polygons.clear();
 
@@ -690,7 +690,7 @@
     {
       // This is an axial projection
 
-      for (Polygons::iterator polygon = structure.polygons_.begin();
+      for (Polygons::const_iterator polygon = structure.polygons_.begin();
            polygon != structure.polygons_.end(); ++polygon)
       {
         if (polygon->IsOnSlice(slice))
@@ -716,7 +716,7 @@
       // Sagittal or coronal projection
       std::vector<BoostPolygon> projected;
   
-      for (Polygons::iterator polygon = structure.polygons_.begin();
+      for (Polygons::const_iterator polygon = structure.polygons_.begin();
            polygon != structure.polygons_.end(); ++polygon)
       {
         double x1, y1, x2, y2;
--- a/Framework/Toolbox/DicomStructureSet.h	Tue May 28 10:39:42 2019 +0200
+++ b/Framework/Toolbox/DicomStructureSet.h	Tue May 28 12:26:16 2019 +0200
@@ -135,13 +135,13 @@
     Structure& GetStructure(size_t index);
   
     bool ProjectStructure(std::vector< std::vector<PolygonPoint> >& polygons,
-                          Structure& structure,
-                          const CoordinateSystem3D& slice);
+                          const Structure& structure,
+                          const CoordinateSystem3D& slice) const;
 
   public:
     DicomStructureSet(const OrthancPlugins::FullOrthancDataset& instance);
 
-    size_t GetStructureCount() const
+    size_t GetStructuresCount() const
     {
       return structures_.size();
     }
@@ -172,7 +172,7 @@
 
     bool ProjectStructure(std::vector< std::vector<PolygonPoint> >& polygons,
                           size_t index,
-                          const CoordinateSystem3D& slice)
+                          const CoordinateSystem3D& slice) const
     {
       return ProjectStructure(polygons, GetStructure(index), slice);
     }
--- a/Samples/Sdl/Loader.cpp	Tue May 28 10:39:42 2019 +0200
+++ b/Samples/Sdl/Loader.cpp	Tue May 28 12:26:16 2019 +0200
@@ -975,6 +975,151 @@
 
 
 
+  class LoaderStateMachine : public IObserver
+  {
+  protected:
+    class State : public Orthanc::IDynamicObject
+    {
+    private:
+      LoaderStateMachine&  that_;
+
+    public:
+      State(LoaderStateMachine& that) :
+        that_(that)
+      {
+      }
+
+      void Schedule(OracleCommandWithPayload* command)
+      {
+        that_.Schedule(command);
+      }
+      
+      virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+      }
+      
+      virtual void Handle(const GetOrthancImageCommand::SuccessMessage& message) const
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+      }
+      
+      virtual void Handle(const GetOrthancWebViewerJpegCommand::SuccessMessage& message) const
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+      }
+    };
+
+    void Schedule(OracleCommandWithPayload* command)
+    {
+      std::auto_ptr<OracleCommandWithPayload> protection(command);
+      
+      if (!command->HasPayload())
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange,
+                                        "The payload must contain the next state");
+      }
+
+      pendingCommands_.push_back(protection.release());
+    }
+
+    void Start()
+    {
+      if (active_)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+      }
+
+      for (size_t i = 0; i < simultaneousDownloads_; i++)
+      {
+        Step();
+      }
+    }
+
+  private:
+    void Step()
+    {
+      if (!pendingCommands_.empty())
+      {
+        oracle_.Schedule(*this, pendingCommands_.front());
+        pendingCommands_.pop_front();
+      }
+    }
+    
+    void Handle(const OrthancRestApiCommand::SuccessMessage& message)
+    {
+      dynamic_cast<const State&>(message.GetOrigin().GetPayload()).Handle(message);
+      Step();
+    }
+
+    void Handle(const GetOrthancImageCommand::SuccessMessage& message)
+    {
+      dynamic_cast<const State&>(message.GetOrigin().GetPayload()).Handle(message);
+      Step();
+    }
+
+    void Handle(const GetOrthancWebViewerJpegCommand::SuccessMessage& message)
+    {
+      dynamic_cast<const State&>(message.GetOrigin().GetPayload()).Handle(message);
+      Step();
+    }
+
+    typedef std::list<IOracleCommand*>  PendingCommands;
+
+    IOracle&         oracle_;
+    bool             active_;
+    unsigned int     simultaneousDownloads_;
+    PendingCommands  pendingCommands_;
+
+  public:
+    LoaderStateMachine(IOracle& oracle,
+                       IObservable& oracleObservable) :
+      IObserver(oracleObservable.GetBroker()),
+      oracle_(oracle),
+      active_(false),
+      simultaneousDownloads_(4)
+    {
+      oracleObservable.RegisterObserverCallback(
+        new Callable<LoaderStateMachine, OrthancRestApiCommand::SuccessMessage>
+        (*this, &LoaderStateMachine::Handle));
+
+      oracleObservable.RegisterObserverCallback(
+        new Callable<LoaderStateMachine, GetOrthancImageCommand::SuccessMessage>
+        (*this, &LoaderStateMachine::Handle));
+
+      oracleObservable.RegisterObserverCallback(
+        new Callable<LoaderStateMachine, GetOrthancWebViewerJpegCommand::SuccessMessage>
+        (*this, &LoaderStateMachine::Handle));
+    }
+
+    virtual ~LoaderStateMachine()
+    {
+      for (PendingCommands::iterator it = pendingCommands_.begin();
+           it != pendingCommands_.end(); ++it)
+      {
+        delete *it;
+      }
+    }
+
+    virtual 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 IObserver,
     public IObservable
@@ -1476,46 +1621,108 @@
     public IVolumeSlicer
   {
   private:
-    enum State
-    {
-      State_Setup,
-      State_Loading,
-      State_Ready
-    };    
-    
-    
     std::auto_ptr<DicomStructureSet>  content_;
     IOracle&                          oracle_;
-    State                             state_;
+    bool                              active_;
+    uint64_t                          revision_;
     std::string                       instanceId_;
 
     void Handle(const OrthancRestApiCommand::SuccessMessage& message)
     {
-      if (state_ != State_Loading)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-      }
-
-      const boost::posix_time::ptime start = boost::posix_time::microsec_clock::local_time();
+      assert(active_);
 
       {
         OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer());
         content_.reset(new DicomStructureSet(dicom));
       }
 
-      const boost::posix_time::ptime end = boost::posix_time::microsec_clock::local_time();
+      std::set<std::string> instances;
+      content_->GetReferencedInstances(instances);
+
+      for (std::set<std::string>::const_iterator
+             it = instances.begin(); it != instances.end(); ++it)
+      {
+        printf("[%s]\n", it->c_str());
+      }
+    }
+
+    
+    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_;
+      }
 
-      printf("LOADED: %d\n", (end - start).total_milliseconds());
-      state_ = State_Ready;
-    }
-      
+      virtual uint64_t GetRevision()
+      {
+        return revision_;
+      }
+
+      virtual ISceneLayer* CreateSceneLayer(const ILayerStyleConfigurator* configurator,
+                                            const CoordinateSystem3D& cuttingPlane)
+      {
+        assert(isValid_);
+
+        std::auto_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer);
+
+        for (size_t i = 0; i < content_.GetStructuresCount(); i++)
+        {
+          std::vector< std::vector<DicomStructureSet::PolygonPoint> > polygons;
+          
+          if (content_.ProjectStructure(polygons, i, cuttingPlane))
+          {
+            printf(">> %d\n", polygons.size());
+            
+            for (size_t j = 0; j < polygons.size(); j++)
+            {
+              PolylineSceneLayer::Chain chain;
+              chain.resize(polygons[j].size());
+            
+              for (size_t k = 0; k < polygons[i].size(); k++)
+              {
+                chain[k] = ScenePoint2D(polygons[j][k].first, polygons[j][k].second);
+              }
+
+              layer->AddChain(chain, true /* closed */);
+            }
+          }
+        }
+
+        printf("OK\n");
+
+        return layer.release();
+      }
+    };
     
   public:
     DicomStructureSetLoader(IOracle& oracle,
                             IObservable& oracleObservable) :
       IObserver(oracleObservable.GetBroker()),
       oracle_(oracle),
-      state_(State_Setup)
+      active_(false),
+      revision_(0)
     {
       oracleObservable.RegisterObserverCallback(
         new Callable<DicomStructureSetLoader, OrthancRestApiCommand::SuccessMessage>
@@ -1525,13 +1732,13 @@
     
     void LoadInstance(const std::string& instanceId)
     {
-      if (state_ != State_Setup)
+      if (active_)
       {
         throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
       }
       else
       {
-        state_ = State_Loading;
+        active_ = true;
         instanceId_ = instanceId;
 
         {
@@ -1545,7 +1752,15 @@
 
     virtual IExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane)
     {
-      return NULL;
+      if (content_.get() == NULL)
+      {
+        // Geometry is not available yet
+        return new IVolumeSlicer::InvalidSlice;
+      }
+      else
+      {
+        return new Slice(*content_, revision_, cuttingPlane);
+      }
     }
   };
 
@@ -1766,7 +1981,7 @@
   OrthancStone::CoordinateSystem3D  plane_;
   OrthancStone::IOracle&            oracle_;
   OrthancStone::Scene2D             scene_;
-  std::auto_ptr<OrthancStone::VolumeSceneLayerSource>  source1_, source2_;
+  std::auto_ptr<OrthancStone::VolumeSceneLayerSource>  source1_, source2_, source3_;
 
 
   void Refresh()
@@ -1781,6 +1996,11 @@
       source2_->Update(plane_);
     }
 
+    if (source3_.get() != NULL)
+    {
+      source3_->Update(plane_);
+    }
+
     scene_.FitContent(1024, 768);
       
     {
@@ -1808,8 +2028,8 @@
     printf("Geometry ready\n");
     
     //plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry();
-    //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry();
-    plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry();
+    plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry();
+    //plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry();
     plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f));
 
     Refresh();
@@ -1930,6 +2150,13 @@
       source2_->SetConfigurator(style);
     }
   }
+
+  void SetStructureSet(int depth,
+                       const boost::shared_ptr<OrthancStone::DicomStructureSetLoader>& volume)
+  {
+    source3_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume));
+  }
+                       
 };
 
 
@@ -1954,7 +2181,8 @@
   }
 
 
-  toto->SetReferenceLoader(*ctLoader);
+  //toto->SetReferenceLoader(*ctLoader);
+  toto->SetReferenceLoader(*doseLoader);
 
 
 #if 1
@@ -1975,6 +2203,8 @@
     toto->SetVolume2(1, tmp, config.release());
   }
 
+  toto->SetStructureSet(2, rtstructLoader);
+  
   oracle.Schedule(*toto, new OrthancStone::SleepOracleCommand(100));
 
   if (0)
@@ -2054,7 +2284,7 @@
 
   
   // 2017-11-17-Anonymized
-  ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618");  // CT
+  //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618");  // CT
   doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6");  // RT-DOSE
   rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6");  // RT-STRUCT