changeset 776:0387485f048b

ILayerStyleConfigurator
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 24 May 2019 18:29:27 +0200
parents b8dfd966b5f4
children 7b404c853e66
files Framework/Scene2D/FloatTextureSceneLayer.cpp Samples/Sdl/Loader.cpp
diffstat 2 files changed, 233 insertions(+), 53 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Scene2D/FloatTextureSceneLayer.cpp	Fri May 24 13:44:34 2019 +0200
+++ b/Framework/Scene2D/FloatTextureSceneLayer.cpp	Fri May 24 18:29:27 2019 +0200
@@ -27,7 +27,8 @@
 
 namespace OrthancStone
 {
-  FloatTextureSceneLayer::FloatTextureSceneLayer(const Orthanc::ImageAccessor& texture)
+  FloatTextureSceneLayer::FloatTextureSceneLayer(const Orthanc::ImageAccessor& texture) :
+    inverted_(false)
   {
     {
       std::auto_ptr<Orthanc::ImageAccessor> t(
--- a/Samples/Sdl/Loader.cpp	Fri May 24 13:44:34 2019 +0200
+++ b/Samples/Sdl/Loader.cpp	Fri May 24 18:29:27 2019 +0200
@@ -56,6 +56,142 @@
 
 namespace OrthancStone
 {
+  // Application-configurable, can be shared between 3D/2D
+  class ILayerStyleConfigurator
+  {
+  public:
+    virtual ~ILayerStyleConfigurator()
+    {
+    }
+    
+    virtual uint64_t GetRevision() const = 0;
+    
+    virtual ISceneLayer* CreateFromImage(const Orthanc::ImageAccessor& image) const = 0;
+
+    virtual ISceneLayer* CreateFromDicomImage(const Orthanc::ImageAccessor& frame,
+                                              const DicomInstanceParameters& parameters) const = 0;
+
+    virtual void ApplyStyle(ISceneLayer& layer) const = 0;
+  };
+
+
+
+  class LookupTableStyleConfigurator : public ILayerStyleConfigurator
+  {
+  private:
+    uint64_t     revision_;
+    bool         hasLut_;
+    std::string  lut_;
+    bool         hasRange_;
+    float        minValue_;
+    float        maxValue_;
+    
+  public:
+    LookupTableStyleConfigurator() :
+      revision_(0),
+      hasLut_(false),
+      hasRange_(false)
+    {
+      SetLookupTable(Orthanc::EmbeddedResources::COLORMAP_HOT);   // TODO - test
+    }
+
+    void SetLookupTable(Orthanc::EmbeddedResources::FileResourceId resource)
+    {
+      hasLut_ = true;
+      Orthanc::EmbeddedResources::GetFileResource(lut_, resource);
+    }
+
+    void SetLookupTable(const std::string& lut)
+    {
+      hasLut_ = true;
+      lut_ = lut;
+    }
+
+    void SetRange(float minValue,
+                  float maxValue)
+    {
+      if (minValue > maxValue)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+      }
+      else
+      {
+        hasRange_ = true;
+        minValue_ = minValue;
+        maxValue_ = maxValue;
+      }
+    }
+
+    virtual uint64_t GetRevision() const
+    {
+      return revision_;
+    }
+    
+    virtual ISceneLayer* CreateFromImage(const Orthanc::ImageAccessor& image) const
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+    }
+
+    virtual ISceneLayer* CreateFromDicomImage(const Orthanc::ImageAccessor& frame,
+                                              const DicomInstanceParameters& parameters) const
+    {
+      return parameters.CreateLookupTableTexture(frame);
+    }
+
+    virtual void ApplyStyle(ISceneLayer& layer) const
+    {
+      LookupTableTextureSceneLayer& l = dynamic_cast<LookupTableTextureSceneLayer&>(layer);
+      
+      if (hasLut_)
+      {
+        l.SetLookupTable(lut_);
+      }
+
+      if (hasRange_)
+      {
+        l.SetRange(minValue_, maxValue_);
+      }
+      else
+      {
+        l.FitRange();
+      }
+    }
+  };
+
+
+  class GrayscaleStyleConfigurator : public ILayerStyleConfigurator
+  {
+  private:
+    uint64_t revision_;
+    
+  public:
+    GrayscaleStyleConfigurator() :
+      revision_(0)
+    {
+    }
+
+    virtual uint64_t GetRevision() const
+    {
+      return revision_;
+    }
+    
+    virtual ISceneLayer* CreateFromImage(const Orthanc::ImageAccessor& image) const
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+    }
+
+    virtual ISceneLayer* CreateFromDicomImage(const Orthanc::ImageAccessor& frame,
+                                              const DicomInstanceParameters& parameters) const
+    {
+      return parameters.CreateTexture(frame);
+    }
+
+    virtual void ApplyStyle(ISceneLayer& layer) const
+    {
+    }
+  };
+
+
   class IVolumeSlicer : public boost::noncopyable
   {
   public:
@@ -72,7 +208,8 @@
       virtual uint64_t GetRevision() = 0;
 
       // This call can take some time
-      virtual ISceneLayer* CreateSceneLayer(const CoordinateSystem3D& cuttingPlane) = 0;
+      virtual ISceneLayer* CreateSceneLayer(const ILayerStyleConfigurator* configurator,  // possibly absent
+                                            const CoordinateSystem3D& cuttingPlane) = 0;
     };
 
     virtual ~IVolumeSlicer()
@@ -83,6 +220,8 @@
   };
 
 
+
+  // TODO -> Remove
   class IVolumeImageSlicer : public IVolumeSlicer
   {
   public:
@@ -105,7 +244,8 @@
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
 
-    virtual ISceneLayer* CreateSceneLayer(const CoordinateSystem3D& cuttingPlane)
+    virtual ISceneLayer* CreateSceneLayer(const ILayerStyleConfigurator* configurator,
+                                          const CoordinateSystem3D& cuttingPlane)
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
@@ -169,34 +309,24 @@
       return GetRevisionInternal(projection_, sliceIndex_);
     }
 
-    virtual ISceneLayer* CreateSceneLayer(const CoordinateSystem3D& cuttingPlane)
+    virtual ISceneLayer* CreateSceneLayer(const ILayerStyleConfigurator* configurator,
+                                          const CoordinateSystem3D& cuttingPlane)
     {
       CheckValid();
 
+      if (configurator == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer,
+                                        "A style configurator is mandatory for textures");
+      }
+
       std::auto_ptr<TextureBaseSceneLayer> texture;
         
       {
         const DicomInstanceParameters& parameters = GetDicomParameters(projection_, sliceIndex_);
         ImageBuffer3D::SliceReader reader(image_, projection_, sliceIndex_);
-
-        static unsigned int i = 1;
-        
-        if (i % 2)
-        {
-          texture.reset(parameters.CreateTexture(reader.GetAccessor()));
-        }
-        else
-        {
-          std::string lut;
-          Orthanc::EmbeddedResources::GetFileResource(lut, Orthanc::EmbeddedResources::COLORMAP_HOT);
-          
-          std::auto_ptr<LookupTableTextureSceneLayer> tmp(parameters.CreateLookupTableTexture(reader.GetAccessor()));
-          tmp->FitRange();
-          tmp->SetLookupTable(lut);
-          texture.reset(tmp.release());
-        }
-
-        i++;
+        texture.reset(dynamic_cast<TextureBaseSceneLayer*>
+                      (configurator->CreateFromDicomImage(reader.GetAccessor(), parameters)));
       }
 
       const CoordinateSystem3D& system = geometry_.GetProjectionGeometry(projection_);
@@ -1280,26 +1410,30 @@
   class VolumeSceneLayerSource : public boost::noncopyable
   {
   private:
-    int                                layerDepth_;
-    boost::shared_ptr<IVolumeSlicer>   slicer_;
-    bool                               linearInterpolation_;
-    std::auto_ptr<CoordinateSystem3D>  lastPlane_;
-    uint64_t                           lastRevision_;
+    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));
     }
 
   public:
-    VolumeSceneLayerSource(int layerDepth,
+    VolumeSceneLayerSource(Scene2D& scene,
+                           int layerDepth,
                            IVolumeSlicer* slicer) :   // Takes ownership
+      scene_(scene),
       layerDepth_(layerDepth),
-      slicer_(slicer),
-      linearInterpolation_(false)
+      slicer_(slicer)
     {
       if (slicer == NULL)
       {
@@ -1312,18 +1446,41 @@
       return *slicer_;
     }
 
-    void SetLinearInterpolation(bool enabled)
+    void RemoveConfigurator()
     {
-      linearInterpolation_ = enabled;
+      configurator_.reset();
+      lastPlane_.reset();
     }
 
-    bool IsLinearInterpolation() const
+    void SetConfigurator(ILayerStyleConfigurator* configurator)  // Takes ownership
     {
-      return linearInterpolation_;
+      if (configurator == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+      }
+
+      configurator_.reset(configurator);
+
+      // Invalidate the layer
+      lastPlane_.reset(NULL);
     }
 
-    void Update(Scene2D& scene,
-                const CoordinateSystem3D& plane)
+    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::ExtractedSlice> slice(slicer_->ExtractSlice(plane));
@@ -1336,14 +1493,22 @@
       if (!slice->IsValid())
       {
         // The slicer cannot handle this cutting plane: Clear the layer
-        scene.DeleteLayer(layerDepth_);
+        scene_.DeleteLayer(layerDepth_);
         lastPlane_.reset(NULL);
       }
       else if (lastPlane_.get() != NULL &&
                IsSameCuttingPlane(*lastPlane_, plane) &&
                lastRevision_ == slice->GetRevision())
       {
-        // The content of the slice has not changed: Do nothing
+        // 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
       {
@@ -1351,19 +1516,19 @@
         lastPlane_.reset(new CoordinateSystem3D(plane));
         lastRevision_ = slice->GetRevision();
 
-        std::auto_ptr<ISceneLayer> layer(slice->CreateSceneLayer(plane));
+        std::auto_ptr<ISceneLayer> layer(slice->CreateSceneLayer(configurator_.get(), plane));
         if (layer.get() == NULL)
         {
           throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);        
         }
 
-        if (layer->GetType() == ISceneLayer::Type_ColorTexture ||
-            layer->GetType() == ISceneLayer::Type_FloatTexture)
+        if (configurator_.get() != NULL)
         {
-          dynamic_cast<TextureBaseSceneLayer&>(*layer).SetLinearInterpolation(linearInterpolation_);
+          lastConfiguratorRevision_ = configurator_->GetRevision();
+          configurator_->ApplyStyle(*layer);
         }
-        
-        scene.SetLayer(layerDepth_, layer.release());
+
+        scene_.SetLayer(layerDepth_, layer.release());
       }
     }
   };
@@ -1495,12 +1660,12 @@
 
       if (source1_.get() != NULL)
       {
-        source1_->Update(scene_, plane);
+        source1_->Update(plane);
       }
       
       if (source2_.get() != NULL)
       {
-        source2_->Update(scene_, plane);
+        source2_->Update(plane);
       }
 
       scene_.FitContent(1024, 768);
@@ -1596,15 +1761,27 @@
   }
 
   void SetVolume1(int depth,
-                  OrthancStone::IVolumeSlicer* volume)
+                  OrthancStone::IVolumeSlicer* volume,
+                  OrthancStone::ILayerStyleConfigurator* style)
   {
-    source1_.reset(new OrthancStone::VolumeSceneLayerSource(depth, volume));
+    source1_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume));
+
+    if (style != NULL)
+    {
+      source1_->SetConfigurator(style);
+    }
   }
 
   void SetVolume2(int depth,
-                  OrthancStone::IVolumeSlicer* volume)
+                  OrthancStone::IVolumeSlicer* volume,
+                  OrthancStone::ILayerStyleConfigurator* style)
   {
-    source2_.reset(new OrthancStone::VolumeSceneLayerSource(depth, volume));
+    source2_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume));
+
+    if (style != NULL)
+    {
+      source2_->SetConfigurator(style);
+    }
   }
 };
 
@@ -1713,8 +1890,10 @@
   //loader1->LoadSeries("67f1b334-02c16752-45026e40-a5b60b6b-030ecab5");  // Lung 1/10mm
 
 
-  toto->SetVolume2(1, new OrthancStone::OrthancMultiframeVolumeLoader::MPRSlicer(loader3));
-  toto->SetVolume1(0, new OrthancStone::OrthancSeriesVolumeProgressiveLoader::MPRSlicer(loader1));
+  toto->SetVolume2(1, new OrthancStone::OrthancMultiframeVolumeLoader::MPRSlicer(loader3),
+                   new OrthancStone::LookupTableStyleConfigurator);
+  toto->SetVolume1(0, new OrthancStone::OrthancSeriesVolumeProgressiveLoader::MPRSlicer(loader1),
+                   new OrthancStone::GrayscaleStyleConfigurator);
 
   {
     oracle.Start();