changeset 652:6646957eff7f

Merge from default
author Benjamin Golinvaux <bgo@osimis.io>
date Tue, 14 May 2019 09:49:24 +0200
parents 62f6ff016085 (current diff) 1088d4c4d78c (diff)
children 4eccf698e52f
files
diffstat 13 files changed, 228 insertions(+), 112 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/Samples/SingleFrameApplication.h	Tue May 14 09:48:14 2019 +0200
+++ b/Applications/Samples/SingleFrameApplication.h	Tue May 14 09:49:24 2019 +0200
@@ -136,9 +136,9 @@
             slice = 0;
           }
 
-          if (slice >= static_cast<int>(source_->GetSliceCount()))
+          if (slice >= static_cast<int>(source_->GetSlicesCount()))
           {
-            slice = static_cast<int>(source_->GetSliceCount()) - 1;
+            slice = static_cast<int>(source_->GetSlicesCount()) - 1;
           }
 
           if (slice != static_cast<int>(slice_)) 
@@ -158,7 +158,7 @@
       void SetSlice(size_t index)
       {
         if (source_ != NULL &&
-            index < source_->GetSliceCount())
+            index < source_->GetSlicesCount())
         {
           slice_ = static_cast<unsigned int>(index);
           
@@ -191,7 +191,7 @@
         // slice
         if (source_ == &message.GetOrigin())
         {
-          SetSlice(source_->GetSliceCount() / 2);
+          SetSlice(source_->GetSlicesCount() / 2);
         }
 
         GetMainWidget().FitContent();
--- a/Framework/Layers/DicomSeriesVolumeSlicer.cpp	Tue May 14 09:48:14 2019 +0200
+++ b/Framework/Layers/DicomSeriesVolumeSlicer.cpp	Tue May 14 09:49:24 2019 +0200
@@ -34,7 +34,7 @@
 
   void DicomSeriesVolumeSlicer::OnSliceGeometryReady(const OrthancSlicesLoader::SliceGeometryReadyMessage& message)
   {
-    if (message.GetOrigin().GetSliceCount() > 0)
+    if (message.GetOrigin().GetSlicesCount() > 0)
     {
       BroadcastMessage(IVolumeSlicer::GeometryReadyMessage(*this));
     }
--- a/Framework/Layers/DicomSeriesVolumeSlicer.h	Tue May 14 09:48:14 2019 +0200
+++ b/Framework/Layers/DicomSeriesVolumeSlicer.h	Tue May 14 09:49:24 2019 +0200
@@ -102,9 +102,9 @@
       return quality_;
     }
 
-    size_t GetSliceCount() const
+    size_t GetSlicesCount() const
     {
-      return loader_.GetSliceCount();
+      return loader_.GetSlicesCount();
     }
 
     const Slice& GetSlice(size_t slice) const 
--- a/Framework/Toolbox/CoordinateSystem3D.cpp	Tue May 14 09:48:14 2019 +0200
+++ b/Framework/Toolbox/CoordinateSystem3D.cpp	Tue May 14 09:49:24 2019 +0200
@@ -187,4 +187,24 @@
   {
     return GeometryToolbox::IntersectPlaneAndLine(p, normal_, d_, origin, direction);
   }
+
+
+  bool CoordinateSystem3D::GetDistance(double& distance,
+                                       const CoordinateSystem3D& a,
+                                       const CoordinateSystem3D& b)
+  {
+    bool opposite;   // Ignored
+
+    if (OrthancStone::GeometryToolbox::IsParallelOrOpposite(
+          opposite, a.GetNormal(), b.GetNormal()))
+    {
+      distance = std::abs(a.ProjectAlongNormal(a.GetOrigin()) -
+                          a.ProjectAlongNormal(b.GetOrigin()));
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
 }
--- a/Framework/Toolbox/CoordinateSystem3D.h	Tue May 14 09:48:14 2019 +0200
+++ b/Framework/Toolbox/CoordinateSystem3D.h	Tue May 14 09:49:24 2019 +0200
@@ -102,5 +102,10 @@
     bool IntersectLine(Vector& p,
                        const Vector& origin,
                        const Vector& direction) const;
+
+    // Returns "false" is the two planes are not parallel
+    static bool GetDistance(double& distance,
+                            const CoordinateSystem3D& a,
+                            const CoordinateSystem3D& b);
   };
 }
--- a/Framework/Toolbox/LinearAlgebra.h	Tue May 14 09:48:14 2019 +0200
+++ b/Framework/Toolbox/LinearAlgebra.h	Tue May 14 09:49:24 2019 +0200
@@ -137,7 +137,7 @@
                        double y,
                        double threshold)
     {
-      return fabs(x - y) < threshold;
+      return fabs(x - y) <= threshold;
     }
 
     inline bool IsNear(double x,
--- a/Framework/Toolbox/OrthancSlicesLoader.cpp	Tue May 14 09:48:14 2019 +0200
+++ b/Framework/Toolbox/OrthancSlicesLoader.cpp	Tue May 14 09:49:24 2019 +0200
@@ -191,25 +191,13 @@
   
   void OrthancSlicesLoader::SortAndFinalizeSlices()
   {
-    bool ok = false;
-    
-    if (slices_.GetSliceCount() > 0)
-    {
-      Vector normal;
-      if (slices_.SelectNormal(normal))
-      {
-        slices_.FilterNormal(normal);
-        slices_.SetNormal(normal);
-        slices_.Sort();
-        ok = true;
-      }
-    }
+    bool ok = slices_.Sort();
     
     state_ = State_GeometryReady;
     
     if (ok)
     {
-      LOG(INFO) << "Loaded a series with " << slices_.GetSliceCount() << " slice(s)";
+      LOG(INFO) << "Loaded a series with " << slices_.GetSlicesCount() << " slice(s)";
       BroadcastMessage(SliceGeometryReadyMessage(*this));
     }
     else
@@ -256,7 +244,8 @@
         std::auto_ptr<Slice> slice(new Slice);
         if (slice->ParseOrthancFrame(dicom, instances[i], frame))
         {
-          slices_.AddSlice(slice.release());
+          CoordinateSystem3D geometry = slice->GetGeometry();
+          slices_.AddSlice(geometry, slice.release());
         }
         else
         {
@@ -291,7 +280,8 @@
       std::auto_ptr<Slice> slice(new Slice);
       if (slice->ParseOrthancFrame(dicom, instanceId, frame))
       {
-        slices_.AddSlice(slice.release());
+        CoordinateSystem3D geometry = slice->GetGeometry();
+        slices_.AddSlice(geometry, slice.release());
       }
       else
       {
@@ -322,7 +312,10 @@
     if (slice->ParseOrthancFrame(dicom, instanceId, frame))
     {
       LOG(INFO) << "Loaded instance geometry " << instanceId;
-      slices_.AddSlice(slice.release());
+
+      CoordinateSystem3D geometry = slice->GetGeometry();
+      slices_.AddSlice(geometry, slice.release());
+      
       BroadcastMessage(SliceGeometryReadyMessage(*this));
     }
     else
@@ -717,14 +710,14 @@
   }
   
   
-  size_t OrthancSlicesLoader::GetSliceCount() const
+  size_t OrthancSlicesLoader::GetSlicesCount() const
   {
     if (state_ != State_GeometryReady)
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
     
-    return slices_.GetSliceCount();
+    return slices_.GetSlicesCount();
   }
   
   
@@ -734,8 +727,8 @@
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
-    
-    return slices_.GetSlice(index);
+
+    return dynamic_cast<const Slice&>(slices_.GetSlicePayload(index));
   }
   
   
@@ -746,8 +739,10 @@
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
-    
-    return slices_.LookupSlice(index, plane);
+
+    double distance;
+    return (slices_.LookupClosestSlice(index, distance, plane) &&
+            distance <= GetSlice(index).GetThickness() / 2.0);
   }
   
   
--- a/Framework/Toolbox/OrthancSlicesLoader.h	Tue May 14 09:48:14 2019 +0200
+++ b/Framework/Toolbox/OrthancSlicesLoader.h	Tue May 14 09:49:24 2019 +0200
@@ -26,6 +26,7 @@
 #include "IWebService.h"
 #include "OrthancApiClient.h"
 #include "SlicesSorter.h"
+#include "Slice.h"
 
 #include <Core/Images/Image.h>
 
@@ -195,7 +196,7 @@
 
     bool IsGeometryReady() const;
 
-    size_t GetSliceCount() const;
+    size_t GetSlicesCount() const;
 
     const Slice& GetSlice(size_t index) const;
 
--- a/Framework/Toolbox/Slice.h	Tue May 14 09:48:14 2019 +0200
+++ b/Framework/Toolbox/Slice.h	Tue May 14 09:49:24 2019 +0200
@@ -25,10 +25,13 @@
 #include "DicomFrameConverter.h"
 
 #include <Core/DicomFormat/DicomImageInformation.h>
+#include <Core/IDynamicObject.h>
 
 namespace OrthancStone
 {
-  class Slice : public boost::noncopyable
+  // TODO - Remove this class
+  class Slice :
+    public Orthanc::IDynamicObject  /* to be used as a payload of SlicesSorter */
   {
   private:
     enum Type
--- a/Framework/Toolbox/SlicesSorter.cpp	Tue May 14 09:48:14 2019 +0200
+++ b/Framework/Toolbox/SlicesSorter.cpp	Tue May 14 09:49:24 2019 +0200
@@ -30,25 +30,23 @@
   class SlicesSorter::SliceWithDepth : public boost::noncopyable
   {
   private:
-    std::auto_ptr<Slice>   slice_;
-    double                 depth_;
+    CoordinateSystem3D  geometry_;
+    double              depth_;
+
+    std::auto_ptr<Orthanc::IDynamicObject>   payload_;
 
   public:
-    SliceWithDepth(Slice* slice) :
-      slice_(slice),
-      depth_(0)
+    SliceWithDepth(const CoordinateSystem3D& geometry,
+                   Orthanc::IDynamicObject* payload) :
+      geometry_(geometry),
+      depth_(0),
+      payload_(payload)
     {
-      if (slice == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-      }
     }
 
     void SetNormal(const Vector& normal)
     {
-      assert(slice_.get() != NULL);
-      depth_ = boost::numeric::ublas::inner_prod
-        (slice_->GetGeometry().GetOrigin(), normal);
+      depth_ = boost::numeric::ublas::inner_prod(geometry_.GetOrigin(), normal);
     }
 
     double GetDepth() const
@@ -56,10 +54,26 @@
       return depth_;
     }
 
-    const Slice& GetSlice() const
+    const CoordinateSystem3D& GetGeometry() const
+    {
+      return geometry_;
+    }
+
+    bool HasPayload() const
     {
-      assert(slice_.get() != NULL);
-      return *slice_;
+      return (payload_.get() != NULL);
+    }
+
+    const Orthanc::IDynamicObject& GetPayload() const
+    {
+      if (HasPayload())
+      {
+        return *payload_;
+      }
+      else
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+      }
     }
   };
 
@@ -84,21 +98,42 @@
   }
 
 
-  void SlicesSorter::AddSlice(Slice* slice)
+  void SlicesSorter::AddSlice(const CoordinateSystem3D& slice,
+                              Orthanc::IDynamicObject* payload)
   {
-    slices_.push_back(new SliceWithDepth(slice));
+    slices_.push_back(new SliceWithDepth(slice, payload));
   }
 
   
-  const Slice& SlicesSorter::GetSlice(size_t i) const
+  const SlicesSorter::SliceWithDepth& SlicesSorter::GetSlice(size_t i) const
   {
     if (i >= slices_.size())
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
     }
+    else
+    {
+      assert(slices_[i] != NULL);
+      return *slices_[i];
+    }
+  }
 
-    assert(slices_[i] != NULL);
-    return slices_[i]->GetSlice();
+
+  const CoordinateSystem3D& SlicesSorter::GetSliceGeometry(size_t i) const
+  {
+    return GetSlice(i).GetGeometry();
+  }
+  
+  
+  bool SlicesSorter::HasSlicePayload(size_t i) const
+  {
+    return GetSlice(i).HasPayload();
+  }
+  
+    
+  const Orthanc::IDynamicObject& SlicesSorter::GetSlicePayload(size_t i) const
+  {
+    return GetSlice(i).GetPayload();
   }
 
   
@@ -113,7 +148,7 @@
   }
   
     
-  void SlicesSorter::Sort()
+  void SlicesSorter::SortInternal()
   {
     if (!hasNormal_)
     {
@@ -131,7 +166,7 @@
 
     for (size_t i = 0; i < slices_.size(); i++)
     {
-      if (GeometryToolbox::IsParallel(normal, slices_[i]->GetSlice().GetGeometry().GetNormal()))
+      if (GeometryToolbox::IsParallel(normal, slices_[i]->GetGeometry().GetNormal()))
       {
         // This slice is compatible with the selected normal
         slices_[pos] = slices_[i];
@@ -155,7 +190,7 @@
 
     bool found = false;
 
-    for (size_t i = 0; !found && i < GetSliceCount(); i++)
+    for (size_t i = 0; !found && i < GetSlicesCount(); i++)
     {
       const Vector& normal = GetSlice(i).GetGeometry().GetNormal();
 
@@ -190,8 +225,8 @@
     for (size_t i = 0; !found && i < normalCandidates.size(); i++)
     {
       unsigned int count = normalCount[i];
-      if (count == GetSliceCount() ||
-          count + 1 == GetSliceCount())
+      if (count == GetSlicesCount() ||
+          count + 1 == GetSlicesCount())
       {
         normal = normalCandidates[i];
         found = true;
@@ -202,21 +237,52 @@
   }
 
 
-  bool SlicesSorter::LookupSlice(size_t& index,
-                                 const CoordinateSystem3D& slice) const
+  bool SlicesSorter::Sort()
   {
-    // TODO Turn this linear-time lookup into a log-time lookup,
-    // keeping track of whether the slices are sorted along the normal
-
-    for (size_t i = 0; i < slices_.size(); i++)
+    if (GetSlicesCount() > 0)
     {
-      if (slices_[i]->GetSlice().ContainsPlane(slice))
+      Vector normal;
+      if (SelectNormal(normal))
       {
-        index = i;
+        FilterNormal(normal);
+        SetNormal(normal);
+        SortInternal();
         return true;
       }
     }
 
     return false;
   }
+
+
+  bool SlicesSorter::LookupClosestSlice(size_t& index,
+                                        double& distance,
+                                        const CoordinateSystem3D& slice) const
+  {
+    // TODO Turn this linear-time lookup into a log-time lookup,
+    // keeping track of whether the slices are sorted along the normal
+
+    bool found = false;
+    
+    distance = std::numeric_limits<double>::infinity();
+    
+    for (size_t i = 0; i < slices_.size(); i++)
+    {
+      assert(slices_[i] != NULL);
+
+      double tmp;
+      if (CoordinateSystem3D::GetDistance(tmp, slices_[i]->GetGeometry(), slice))
+      {
+        if (!found ||
+            tmp < distance)
+        {
+          index = i;
+          distance = tmp;
+          found = true;
+        }
+      }
+    }
+
+    return found;
+  }
 }
--- a/Framework/Toolbox/SlicesSorter.h	Tue May 14 09:48:14 2019 +0200
+++ b/Framework/Toolbox/SlicesSorter.h	Tue May 14 09:49:24 2019 +0200
@@ -21,10 +21,13 @@
 
 #pragma once
 
-#include "Slice.h"
+#include "CoordinateSystem3D.h"
+
+#include <Core/IDynamicObject.h>
 
 namespace OrthancStone
 {
+  // TODO - Rename this as "PlanesSorter"
   class SlicesSorter : public boost::noncopyable
   {
   private:
@@ -36,6 +39,16 @@
     Slices  slices_;
     bool    hasNormal_;
     
+    const SliceWithDepth& GetSlice(size_t i) const;
+    
+    void SetNormal(const Vector& normal);
+    
+    void SortInternal();
+
+    void FilterNormal(const Vector& normal);
+    
+    bool SelectNormal(Vector& normal) const;
+
   public:
     SlicesSorter() : hasNormal_(false)
     {
@@ -48,24 +61,29 @@
       slices_.reserve(count);
     }
 
-    void AddSlice(Slice* slice);  // Takes ownership
+    void AddSlice(const CoordinateSystem3D& plane)
+    {
+      AddSlice(plane, NULL);
+    }
 
-    size_t GetSliceCount() const
+    void AddSlice(const CoordinateSystem3D& plane,
+                  Orthanc::IDynamicObject* payload);  // Takes ownership
+
+    size_t GetSlicesCount() const
     {
       return slices_.size();
     }
 
-    const Slice& GetSlice(size_t i) const;
+    const CoordinateSystem3D& GetSliceGeometry(size_t i) const;
 
-    void SetNormal(const Vector& normal);
+    bool HasSlicePayload(size_t i) const;
     
-    void Sort();
+    const Orthanc::IDynamicObject& GetSlicePayload(size_t i) const;
 
-    void FilterNormal(const Vector& normal);
+    bool Sort();
     
-    bool SelectNormal(Vector& normal) const;
-
-    bool LookupSlice(size_t& index,
-                     const CoordinateSystem3D& slice) const;
+    bool LookupClosestSlice(size_t& index,
+                            double& distance,
+                            const CoordinateSystem3D& slice) const;
   };
 }
--- a/Framework/dev.h	Tue May 14 09:48:14 2019 +0200
+++ b/Framework/dev.h	Tue May 14 09:49:24 2019 +0200
@@ -110,14 +110,14 @@
     {
       assert(&message.GetOrigin() == &loader_);
 
-      if (loader_.GetSliceCount() == 0)
+      if (loader_.GetSlicesCount() == 0)
       {
         LOG(ERROR) << "Empty volume image";
         BroadcastMessage(ISlicedVolume::GeometryErrorMessage(*this));
         return;
       }
 
-      for (size_t i = 1; i < loader_.GetSliceCount(); i++)
+      for (size_t i = 1; i < loader_.GetSlicesCount(); i++)
       {
         if (!IsCompatible(loader_.GetSlice(0), loader_.GetSlice(i)))
         {
@@ -128,7 +128,7 @@
 
       double spacingZ;
 
-      if (loader_.GetSliceCount() > 1)
+      if (loader_.GetSlicesCount() > 1)
       {
         spacingZ = GetDistance(loader_.GetSlice(0), loader_.GetSlice(1));
       }
@@ -139,7 +139,7 @@
         spacingZ = 1;
       }
 
-      for (size_t i = 1; i < loader_.GetSliceCount(); i++)
+      for (size_t i = 1; i < loader_.GetSlicesCount(); i++)
       {
         if (!LinearAlgebra::IsNear(spacingZ, GetDistance(loader_.GetSlice(i - 1), loader_.GetSlice(i)),
                                    0.001 /* this is expressed in mm */))
@@ -154,16 +154,16 @@
       unsigned int height = loader_.GetSlice(0).GetHeight();
       Orthanc::PixelFormat format = loader_.GetSlice(0).GetConverter().GetExpectedPixelFormat();
       LOG(INFO) << "Creating a volume image of size " << width << "x" << height
-                << "x" << loader_.GetSliceCount() << " in " << Orthanc::EnumerationToString(format);
+                << "x" << loader_.GetSlicesCount() << " in " << Orthanc::EnumerationToString(format);
 
-      image_.reset(new ImageBuffer3D(format, width, height, static_cast<unsigned int>(loader_.GetSliceCount()), computeRange_));
+      image_.reset(new ImageBuffer3D(format, width, height, static_cast<unsigned int>(loader_.GetSlicesCount()), computeRange_));
       image_->SetAxialGeometry(loader_.GetSlice(0).GetGeometry());
       image_->SetVoxelDimensions(loader_.GetSlice(0).GetPixelSpacingX(),
                                  loader_.GetSlice(0).GetPixelSpacingY(), spacingZ);
       image_->Clear();
 
-      downloadStack_.reset(new DownloadStack(static_cast<unsigned int>(loader_.GetSliceCount())));
-      pendingSlices_ = loader_.GetSliceCount();
+      downloadStack_.reset(new DownloadStack(static_cast<unsigned int>(loader_.GetSlicesCount())));
+      pendingSlices_ = loader_.GetSlicesCount();
 
       for (unsigned int i = 0; i < 4; i++)  // Limit to 4 simultaneous downloads
       {
@@ -263,9 +263,9 @@
       loader_.ScheduleLoadFrame(instanceId, frame);
     }
 
-    virtual size_t GetSliceCount() const
+    virtual size_t GetSlicesCount() const
     {
-      return loader_.GetSliceCount();
+      return loader_.GetSlicesCount();
     }
 
     virtual const Slice& GetSlice(size_t index) const
@@ -317,7 +317,7 @@
     {
       double thickness;
 
-      size_t n = volume.GetSliceCount();
+      size_t n = volume.GetSlicesCount();
       if (n > 1)
       {
         const Slice& a = volume.GetSlice(0);
@@ -349,7 +349,7 @@
 
       width_ = axial.GetWidth();
       height_ = axial.GetHeight();
-      depth_ = volume.GetSliceCount();
+      depth_ = volume.GetSlicesCount();
 
       pixelSpacingX_ = axial.GetPixelSpacingX();
       pixelSpacingY_ = axial.GetPixelSpacingY();
@@ -364,7 +364,7 @@
       double axialThickness = ComputeAxialThickness(volume);
 
       width_ = axial.GetWidth();
-      height_ = static_cast<unsigned int>(volume.GetSliceCount());
+      height_ = static_cast<unsigned int>(volume.GetSlicesCount());
       depth_ = axial.GetHeight();
 
       pixelSpacingX_ = axial.GetPixelSpacingX();
@@ -372,7 +372,7 @@
       sliceThickness_ = axial.GetPixelSpacingY();
 
       Vector origin = axial.GetGeometry().GetOrigin();
-      origin += (static_cast<double>(volume.GetSliceCount() - 1) *
+      origin += (static_cast<double>(volume.GetSlicesCount() - 1) *
                 axialThickness * axial.GetGeometry().GetNormal());
 
       reference_ = CoordinateSystem3D(origin,
@@ -386,7 +386,7 @@
       double axialThickness = ComputeAxialThickness(volume);
 
       width_ = axial.GetHeight();
-      height_ = static_cast<unsigned int>(volume.GetSliceCount());
+      height_ = static_cast<unsigned int>(volume.GetSlicesCount());
       depth_ = axial.GetWidth();
 
       pixelSpacingX_ = axial.GetPixelSpacingY();
@@ -394,7 +394,7 @@
       sliceThickness_ = axial.GetPixelSpacingX();
 
       Vector origin = axial.GetGeometry().GetOrigin();
-      origin += (static_cast<double>(volume.GetSliceCount() - 1) *
+      origin += (static_cast<double>(volume.GetSlicesCount() - 1) *
                 axialThickness * axial.GetGeometry().GetNormal());
 
       reference_ = CoordinateSystem3D(origin,
@@ -406,7 +406,7 @@
     VolumeImageGeometry(const OrthancVolumeImage& volume,
                         VolumeProjection projection)
     {
-      if (volume.GetSliceCount() == 0)
+      if (volume.GetSlicesCount() == 0)
       {
         throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
       }
@@ -432,7 +432,7 @@
       }
     }
 
-    size_t GetSliceCount() const
+    size_t GetSlicesCount() const
     {
       return depth_;
     }
@@ -728,7 +728,7 @@
           dynamic_cast<const OrthancVolumeImage&>(message.GetOrigin());
 
         slices_.reset(new VolumeImageGeometry(image, projection_));
-        SetSlice(slices_->GetSliceCount() / 2);
+        SetSlice(slices_->GetSlicesCount() / 2);
 
         widget_.FitContent();
       }
@@ -817,7 +817,7 @@
       return slices_.get() != NULL;
     }
 
-    size_t GetSliceCount() const
+    size_t GetSlicesCount() const
     {
       if (slices_.get() == NULL)
       {
@@ -825,7 +825,7 @@
       }
       else
       {
-        return slices_->GetSliceCount();
+        return slices_->GetSlicesCount();
       }
     }
 
@@ -840,9 +840,9 @@
           slice = 0;
         }
 
-        if (slice >= static_cast<int>(slices_->GetSliceCount()))
+        if (slice >= static_cast<int>(slices_->GetSlicesCount()))
         {
-          slice = static_cast<unsigned int>(slices_->GetSliceCount()) - 1;
+          slice = static_cast<unsigned int>(slices_->GetSlicesCount()) - 1;
         }
 
         if (slice != static_cast<int>(slice_))
--- a/Samples/Sdl/Loader.cpp	Tue May 14 09:48:14 2019 +0200
+++ b/Samples/Sdl/Loader.cpp	Tue May 14 09:49:24 2019 +0200
@@ -26,6 +26,7 @@
 #include "../../Framework/StoneInitialization.h"
 #include "../../Framework/Toolbox/GeometryToolbox.h"
 #include "../../Framework/Volumes/ImageBuffer3D.h"
+#include "../../Framework/Toolbox/SlicesSorter.h"
 
 // From Orthanc framework
 #include <Core/Compression/GzipCompressor.h>
@@ -899,8 +900,6 @@
 
       if (object.get() != NULL)
       {
-        printf("===========================> REQUEST\n");
-        
         const Item& item = dynamic_cast<Item&>(*object);
 
         try
@@ -1140,7 +1139,8 @@
 
 
 
-  class DicomInstanceParameters : public boost::noncopyable
+  class DicomInstanceParameters :
+    public Orthanc::IDynamicObject  /* to be used as a payload of SlicesSorter */
   {
   private:
     Orthanc::DicomImageInformation    imageInformation_;
@@ -1374,13 +1374,10 @@
         tmp = GetFrameGeometry(frame);
       }
 
-      bool opposite;   // Ignored
-      return (OrthancStone::GeometryToolbox::IsParallelOrOpposite(
-                opposite, tmp.GetNormal(), plane.GetNormal()) &&
-              OrthancStone::LinearAlgebra::IsNear(
-                tmp.ProjectAlongNormal(tmp.GetOrigin()),
-                tmp.ProjectAlongNormal(plane.GetOrigin()),
-                thickness_ / 2.0));
+      double distance;
+
+      return (OrthancStone::CoordinateSystem3D::GetDistance(distance, tmp, plane) &&
+              distance <= thickness_ / 2.0);
     }
 
     bool IsColor() const
@@ -1496,8 +1493,19 @@
           Orthanc::DicomMap dicom;
           dicom.FromDicomAsJson(value[instances[i]]);
 
-          DicomInstanceParameters instance(dicom);
+          std::auto_ptr<DicomInstanceParameters> instance(new DicomInstanceParameters(dicom));
+
+          OrthancStone::CoordinateSystem3D geometry = instance->GetGeometry();
+          that_.slices_.AddSlice(geometry, instance.release());
         }
+
+        if (!that_.slices_.Sort())
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange,
+                                          "Cannot sort the 3D slices of a DICOM series");          
+        }
+
+        printf("series sorted\n");
       }
     };
 
@@ -1533,7 +1541,7 @@
 
     bool                                        active_;
     std::auto_ptr<OrthancStone::ImageBuffer3D>  image_;
-
+    OrthancStone::SlicesSorter                  slices_;
 
   public:
     AxialVolumeOrthancLoader(OrthancStone::IObservable& oracle) :