changeset 1802:757987cb5a68

recycling of layers in MacroSceneLayer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 20 May 2021 13:58:26 +0200
parents 64dad1d7aca4
children d1849468729b
files OrthancStone/Sources/Scene2D/MacroSceneLayer.cpp OrthancStone/Sources/Scene2D/MacroSceneLayer.h UnitTestsSources/VolumeRenderingTests.cpp
diffstat 3 files changed, 126 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancStone/Sources/Scene2D/MacroSceneLayer.cpp	Thu May 20 13:23:59 2021 +0200
+++ b/OrthancStone/Sources/Scene2D/MacroSceneLayer.cpp	Thu May 20 13:58:26 2021 +0200
@@ -24,10 +24,38 @@
 
 #include <OrthancException.h>
 
+#include <cassert>
+
 namespace OrthancStone
 {
+  void MacroSceneLayer::CheckInvariant() const
+  {
+#if !defined(NDEBUG)
+    // Only run the sanity check in debug mode
+    size_t countRecycled = 0;
+
+    for (size_t i = 0; i < layers_.size(); i++)
+    {
+      if (layers_[i] == NULL)
+      {
+        assert(recycledLayers_.find(i) != recycledLayers_.end());
+        countRecycled++;
+      }
+      else
+      {
+        assert(recycledLayers_.find(i) == recycledLayers_.end());
+      }
+    }
+
+    assert(countRecycled == recycledLayers_.size());
+#endif
+  }
+  
+
   void MacroSceneLayer::Clear()
   {
+    CheckInvariant();
+    
     for (size_t i = 0; i < layers_.size(); i++)
     {
       if (layers_[i] != NULL)
@@ -37,22 +65,37 @@
     }
 
     layers_.clear();
+    recycledLayers_.clear();
+    
     BumpRevision();
   }
 
   
   size_t MacroSceneLayer::AddLayer(ISceneLayer* layer)
   {
+    CheckInvariant();
+    
     if (layer == NULL)
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
     }
     else
     {
-      // TODO - Use recycling list from DeleteLayer()
+      size_t index;
+
+      if (recycledLayers_.empty())
+      {
+        index = layers_.size();
+        layers_.push_back(layer);
+      }
+      else
+      {
+        index = *recycledLayers_.begin();
+        assert(layers_[index] == NULL);
+        layers_[index] = layer;
+        recycledLayers_.erase(index);
+      }
       
-      size_t index = layers_.size();
-      layers_.push_back(layer);
       BumpRevision();
       return index;
     }
@@ -62,6 +105,8 @@
   void MacroSceneLayer::UpdateLayer(size_t index,
                                     ISceneLayer* layer)
   {
+    CheckInvariant();
+    
     if (layer == NULL)
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
@@ -72,8 +117,14 @@
     }
     else
     {
-      if (layers_[index] != NULL)
+      if (layers_[index] == NULL)
       {
+        assert(recycledLayers_.find(index) != recycledLayers_.end());
+        recycledLayers_.erase(index);
+      }
+      else
+      {
+        assert(recycledLayers_.find(index) == recycledLayers_.end());
         delete layers_[index];
       }
 
@@ -85,6 +136,8 @@
 
   bool MacroSceneLayer::HasLayer(size_t index) const
   {
+    CheckInvariant();
+    
     if (index >= layers_.size())
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
@@ -98,6 +151,8 @@
 
   void MacroSceneLayer::DeleteLayer(size_t index)
   {
+    CheckInvariant();
+    
     if (index >= layers_.size())
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
@@ -108,16 +163,19 @@
     }
     else
     {
-      // TODO - Add to a recycling list
-      
       delete layers_[index];
       layers_[index] = NULL;
+
+      assert(recycledLayers_.find(index) == recycledLayers_.end());
+      recycledLayers_.insert(index);
     }
   }
 
 
   const ISceneLayer& MacroSceneLayer::GetLayer(size_t index) const
   {
+    CheckInvariant();
+    
     if (index >= layers_.size())
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
@@ -135,6 +193,8 @@
 
   ISceneLayer* MacroSceneLayer::Clone() const
   {
+    CheckInvariant();
+    
     std::unique_ptr<MacroSceneLayer> copy(new MacroSceneLayer);
 
     for (size_t i = 0; i < layers_.size(); i++)
@@ -149,7 +209,7 @@
       }
     }
 
-    // TODO - Copy recycling list
+    copy->recycledLayers_ = recycledLayers_;
 
     return copy.release();
   }
@@ -157,6 +217,8 @@
 
   void MacroSceneLayer::GetBoundingBox(Extent2D& target) const
   {
+    CheckInvariant();
+    
     target.Clear();
 
     for (size_t i = 0; i < layers_.size(); i++)
--- a/OrthancStone/Sources/Scene2D/MacroSceneLayer.h	Thu May 20 13:23:59 2021 +0200
+++ b/OrthancStone/Sources/Scene2D/MacroSceneLayer.h	Thu May 20 13:58:26 2021 +0200
@@ -27,6 +27,7 @@
 #include <Compatibility.h>  // For ORTHANC_OVERRIDE
 
 #include <deque>
+#include <set>
 
 namespace OrthancStone
 {
@@ -41,6 +42,9 @@
     // to randomly access the layers
     std::deque<ISceneLayer*>  layers_;
     uint64_t                  revision_;
+    std::set<size_t>          recycledLayers_;
+
+    void CheckInvariant() const;
 
   protected:
     void BumpRevision()
--- a/UnitTestsSources/VolumeRenderingTests.cpp	Thu May 20 13:23:59 2021 +0200
+++ b/UnitTestsSources/VolumeRenderingTests.cpp	Thu May 20 13:58:26 2021 +0200
@@ -22,6 +22,8 @@
 #include "../OrthancStone/Sources/Scene2D/CairoCompositor.h"
 #include "../OrthancStone/Sources/Scene2D/ColorTextureSceneLayer.h"
 #include "../OrthancStone/Sources/Scene2D/CopyStyleConfigurator.h"
+#include "../OrthancStone/Sources/Scene2D/MacroSceneLayer.h"
+#include "../OrthancStone/Sources/Scene2D/PolylineSceneLayer.h"
 #include "../OrthancStone/Sources/Toolbox/SubvoxelReader.h"
 #include "../OrthancStone/Sources/Volumes/DicomVolumeImageMPRSlicer.h"
 #include "../OrthancStone/Sources/Volumes/DicomVolumeImageReslicer.h"
@@ -1012,3 +1014,54 @@
     }
   }
 }
+
+
+TEST(VolumeRendering, MacroLayer)
+{
+  OrthancStone::MacroSceneLayer layer;
+  ASSERT_THROW(layer.AddLayer(NULL), Orthanc::OrthancException);
+
+  ASSERT_EQ(0u, layer.AddLayer(new OrthancStone::PolylineSceneLayer));
+  ASSERT_EQ(1u, layer.AddLayer(new OrthancStone::PolylineSceneLayer));
+  ASSERT_EQ(2u, layer.AddLayer(new OrthancStone::PolylineSceneLayer));
+  ASSERT_EQ(3u, layer.GetSize());
+  ASSERT_TRUE(layer.HasLayer(0));
+  ASSERT_TRUE(layer.HasLayer(1));
+  ASSERT_TRUE(layer.HasLayer(2));
+
+  layer.DeleteLayer(1);
+  ASSERT_EQ(3u, layer.GetSize());
+  ASSERT_TRUE(layer.HasLayer(0));
+  ASSERT_FALSE(layer.HasLayer(1));
+  ASSERT_TRUE(layer.HasLayer(2));
+
+  ASSERT_THROW(layer.UpdateLayer(1, NULL), Orthanc::OrthancException);
+  layer.UpdateLayer(1, new OrthancStone::PolylineSceneLayer);
+  ASSERT_TRUE(layer.HasLayer(1));
+
+  ASSERT_EQ(3u, layer.AddLayer(new OrthancStone::PolylineSceneLayer));
+  ASSERT_EQ(4u, layer.GetSize());
+
+  layer.DeleteLayer(1);
+  layer.DeleteLayer(2);
+  ASSERT_EQ(1u, layer.AddLayer(new OrthancStone::PolylineSceneLayer));
+
+  std::unique_ptr<OrthancStone::MacroSceneLayer> clone(dynamic_cast<OrthancStone::MacroSceneLayer*>(layer.Clone()));
+  
+  layer.UpdateLayer(2, new OrthancStone::PolylineSceneLayer);
+  ASSERT_EQ(4u, layer.AddLayer(new OrthancStone::PolylineSceneLayer));
+  ASSERT_EQ(5u, layer.GetSize());
+  ASSERT_TRUE(layer.HasLayer(0));
+  ASSERT_TRUE(layer.HasLayer(1));
+  ASSERT_TRUE(layer.HasLayer(2));
+  ASSERT_TRUE(layer.HasLayer(3));
+  ASSERT_TRUE(layer.HasLayer(4));
+
+  ASSERT_EQ(2u, clone->AddLayer(new OrthancStone::PolylineSceneLayer));
+  ASSERT_EQ(4u, clone->GetSize());
+  ASSERT_TRUE(clone->HasLayer(0));
+  ASSERT_TRUE(clone->HasLayer(1));
+  ASSERT_TRUE(clone->HasLayer(2));
+  ASSERT_TRUE(clone->HasLayer(3));
+  ASSERT_THROW(clone->HasLayer(4), Orthanc::OrthancException);
+}