changeset 110:53025eecbc95 wasm

renamed SliceGeometry as CoordinateSystem3D
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 14 Jun 2017 15:50:38 +0200
parents 53bd9277b025
children 7665ccbf33db
files Framework/Layers/CircleMeasureTracker.cpp Framework/Layers/CircleMeasureTracker.h Framework/Layers/ColorFrameRenderer.cpp Framework/Layers/ColorFrameRenderer.h Framework/Layers/FrameRenderer.cpp Framework/Layers/FrameRenderer.h Framework/Layers/GrayscaleFrameRenderer.cpp Framework/Layers/GrayscaleFrameRenderer.h Framework/Layers/ILayerRenderer.h Framework/Layers/ILayerSource.h Framework/Layers/LineLayerRenderer.cpp Framework/Layers/LineLayerRenderer.h Framework/Layers/LineMeasureTracker.cpp Framework/Layers/LineMeasureTracker.h Framework/Layers/OrthancFrameLayerSource.cpp Framework/Layers/OrthancFrameLayerSource.h Framework/Layers/SliceOutlineRenderer.h Framework/Toolbox/CoordinateSystem3D.cpp Framework/Toolbox/CoordinateSystem3D.h Framework/Toolbox/DicomStructureSet.cpp Framework/Toolbox/DicomStructureSet.h Framework/Toolbox/OrthancSeriesLoader.cpp Framework/Toolbox/OrthancSlicesLoader.cpp Framework/Toolbox/OrthancSlicesLoader.h Framework/Toolbox/ParallelSlices.cpp Framework/Toolbox/ParallelSlices.h Framework/Toolbox/ParallelSlicesCursor.cpp Framework/Toolbox/ParallelSlicesCursor.h Framework/Toolbox/Slice.cpp Framework/Toolbox/Slice.h Framework/Toolbox/SliceGeometry.cpp Framework/Toolbox/SliceGeometry.h Framework/Toolbox/SlicesSorter.cpp Framework/Toolbox/SlicesSorter.h Framework/Volumes/ImageBuffer3D.cpp Framework/Volumes/ImageBuffer3D.h Framework/Widgets/IWorldSceneInteractor.h Framework/Widgets/LayerWidget.cpp Framework/Widgets/LayerWidget.h Framework/Widgets/TestWorldSceneWidget.cpp Framework/Widgets/TestWorldSceneWidget.h Framework/Widgets/WorldSceneWidget.cpp Framework/dev.h Resources/CMake/OrthancStone.cmake
diffstat 44 files changed, 365 insertions(+), 359 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Layers/CircleMeasureTracker.cpp	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Layers/CircleMeasureTracker.cpp	Wed Jun 14 15:50:38 2017 +0200
@@ -31,7 +31,7 @@
 namespace OrthancStone
 {
   CircleMeasureTracker::CircleMeasureTracker(IStatusBar* statusBar,
-                                             const SliceGeometry& slice,
+                                             const CoordinateSystem3D& slice,
                                              double x, 
                                              double y,
                                              uint8_t red,
--- a/Framework/Layers/CircleMeasureTracker.h	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Layers/CircleMeasureTracker.h	Wed Jun 14 15:50:38 2017 +0200
@@ -24,25 +24,25 @@
 #include "../Widgets/IWorldSceneMouseTracker.h"
 
 #include "../Viewport/IStatusBar.h"
-#include "../Toolbox/SliceGeometry.h"
+#include "../Toolbox/CoordinateSystem3D.h"
 
 namespace OrthancStone
 {
   class CircleMeasureTracker : public IWorldSceneMouseTracker
   {
   private:
-    IStatusBar*    statusBar_;
-    SliceGeometry  slice_;
-    double         x1_;
-    double         y1_;
-    double         x2_;
-    double         y2_;
-    uint8_t        color_[3];
-    unsigned int   fontSize_;
+    IStatusBar*         statusBar_;
+    CoordinateSystem3D  slice_;
+    double              x1_;
+    double              y1_;
+    double              x2_;
+    double              y2_;
+    uint8_t             color_[3];
+    unsigned int        fontSize_;
 
   public:
     CircleMeasureTracker(IStatusBar* statusBar,
-                         const SliceGeometry& slice,
+                         const CoordinateSystem3D& slice,
                          double x, 
                          double y,
                          uint8_t red,
--- a/Framework/Layers/ColorFrameRenderer.cpp	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Layers/ColorFrameRenderer.cpp	Wed Jun 14 15:50:38 2017 +0200
@@ -38,7 +38,7 @@
 
 
   ColorFrameRenderer::ColorFrameRenderer(Orthanc::ImageAccessor* frame,
-                                         const SliceGeometry& frameSlice,
+                                         const CoordinateSystem3D& frameSlice,
                                          double pixelSpacingX,
                                          double pixelSpacingY,
                                          bool isFullQuality) :
--- a/Framework/Layers/ColorFrameRenderer.h	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Layers/ColorFrameRenderer.h	Wed Jun 14 15:50:38 2017 +0200
@@ -35,7 +35,7 @@
 
   public:
     ColorFrameRenderer(Orthanc::ImageAccessor* frame,    // Takes ownership
-                       const SliceGeometry& frameSlice,
+                       const CoordinateSystem3D& frameSlice,
                        double pixelSpacingX,
                        double pixelSpacingY,
                        bool isFullQuality);
--- a/Framework/Layers/FrameRenderer.cpp	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Layers/FrameRenderer.cpp	Wed Jun 14 15:50:38 2017 +0200
@@ -28,7 +28,7 @@
 
 namespace OrthancStone
 {
-  FrameRenderer::FrameRenderer(const SliceGeometry& frameSlice,
+  FrameRenderer::FrameRenderer(const CoordinateSystem3D& frameSlice,
                                double pixelSpacingX,
                                double pixelSpacingY,
                                bool isFullQuality) :
--- a/Framework/Layers/FrameRenderer.h	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Layers/FrameRenderer.h	Wed Jun 14 15:50:38 2017 +0200
@@ -30,7 +30,7 @@
   class FrameRenderer : public ILayerRenderer
   {
   private:
-    SliceGeometry                 frameSlice_;
+    CoordinateSystem3D            frameSlice_;
     double                        pixelSpacingX_;
     double                        pixelSpacingY_;
     RenderStyle                   style_;
@@ -41,7 +41,7 @@
     virtual CairoSurface* GenerateDisplay(const RenderStyle& style) = 0;
 
   public:
-    FrameRenderer(const SliceGeometry& frameSlice,
+    FrameRenderer(const CoordinateSystem3D& frameSlice,
                   double pixelSpacingX,
                   double pixelSpacingY,
                   bool isFullQuality);
@@ -49,7 +49,7 @@
     virtual bool RenderLayer(CairoContext& context,
                              const ViewportGeometry& view);
 
-    virtual const SliceGeometry& GetLayerSlice()
+    virtual const CoordinateSystem3D& GetLayerSlice()
     {
       return frameSlice_;
     }
--- a/Framework/Layers/GrayscaleFrameRenderer.cpp	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Layers/GrayscaleFrameRenderer.cpp	Wed Jun 14 15:50:38 2017 +0200
@@ -110,7 +110,7 @@
 
   GrayscaleFrameRenderer::GrayscaleFrameRenderer(Orthanc::ImageAccessor* frame,
                                                  const DicomFrameConverter& converter,
-                                                 const SliceGeometry& frameSlice,
+                                                 const CoordinateSystem3D& frameSlice,
                                                  double pixelSpacingX,
                                                  double pixelSpacingY,
                                                  bool isFullQuality) :
--- a/Framework/Layers/GrayscaleFrameRenderer.h	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Layers/GrayscaleFrameRenderer.h	Wed Jun 14 15:50:38 2017 +0200
@@ -39,7 +39,7 @@
   public:
     GrayscaleFrameRenderer(Orthanc::ImageAccessor* frame,    // Takes ownership
                            const DicomFrameConverter& converter,
-                           const SliceGeometry& frameSlice,
+                           const CoordinateSystem3D& frameSlice,
                            double pixelSpacingX,
                            double pixelSpacingY,
                            bool isFullQuality);
--- a/Framework/Layers/ILayerRenderer.h	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Layers/ILayerRenderer.h	Wed Jun 14 15:50:38 2017 +0200
@@ -22,7 +22,7 @@
 #pragma once
 
 #include "../Viewport/CairoContext.h"
-#include "../Toolbox/SliceGeometry.h"
+#include "../Toolbox/CoordinateSystem3D.h"
 #include "../Toolbox/ViewportGeometry.h"
 #include "RenderStyle.h"
 
@@ -40,7 +40,7 @@
 
     virtual void SetLayerStyle(const RenderStyle& style) = 0;
 
-    virtual const SliceGeometry& GetLayerSlice() = 0;
+    virtual const CoordinateSystem3D& GetLayerSlice() = 0;
     
     virtual bool IsFullQuality() = 0;
   };
--- a/Framework/Layers/ILayerSource.h	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Layers/ILayerSource.h	Wed Jun 14 15:50:38 2017 +0200
@@ -66,8 +66,8 @@
     virtual void Register(IObserver& observer) = 0;
 
     virtual bool GetExtent(std::vector<Vector>& points,
-                           const SliceGeometry& viewportSlice) = 0;
+                           const CoordinateSystem3D& viewportSlice) = 0;
 
-    virtual void ScheduleLayerCreation(const SliceGeometry& viewportSlice) = 0;
+    virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice) = 0;
   };
 }
--- a/Framework/Layers/LineLayerRenderer.cpp	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Layers/LineLayerRenderer.cpp	Wed Jun 14 15:50:38 2017 +0200
@@ -39,7 +39,7 @@
 
   bool LineLayerRenderer::RenderLayer(CairoContext& context,
                                       const ViewportGeometry& view,
-                                      const SliceGeometry& slice)
+                                      const CoordinateSystem3D& slice)
   {
     if (visible_)
     {
--- a/Framework/Layers/LineLayerRenderer.h	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Layers/LineLayerRenderer.h	Wed Jun 14 15:50:38 2017 +0200
@@ -43,7 +43,7 @@
 
     virtual bool RenderLayer(CairoContext& context,
                              const ViewportGeometry& view,
-                             const SliceGeometry& slice);
+                             const CoordinateSystem3D& slice);
 
     virtual void SetLayerStyle(const RenderStyle& style);
 
--- a/Framework/Layers/LineMeasureTracker.cpp	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Layers/LineMeasureTracker.cpp	Wed Jun 14 15:50:38 2017 +0200
@@ -28,7 +28,7 @@
 namespace OrthancStone
 {
   LineMeasureTracker::LineMeasureTracker(IStatusBar* statusBar,
-                                         const SliceGeometry& slice,
+                                         const CoordinateSystem3D& slice,
                                          double x, 
                                          double y,
                                          uint8_t red,
--- a/Framework/Layers/LineMeasureTracker.h	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Layers/LineMeasureTracker.h	Wed Jun 14 15:50:38 2017 +0200
@@ -24,25 +24,25 @@
 #include "../Widgets/IWorldSceneMouseTracker.h"
 
 #include "../Viewport/IStatusBar.h"
-#include "../Toolbox/SliceGeometry.h"
+#include "../Toolbox/CoordinateSystem3D.h"
 
 namespace OrthancStone
 {
   class LineMeasureTracker : public IWorldSceneMouseTracker
   {
   private:
-    IStatusBar*    statusBar_;
-    SliceGeometry  slice_;
-    double         x1_;
-    double         y1_;
-    double         x2_;
-    double         y2_;
-    uint8_t        color_[3];
-    unsigned int   fontSize_;
+    IStatusBar*         statusBar_;
+    CoordinateSystem3D  slice_;
+    double              x1_;
+    double              y1_;
+    double              x2_;
+    double              y2_;
+    uint8_t             color_[3];
+    unsigned int        fontSize_;
 
   public:
     LineMeasureTracker(IStatusBar* statusBar,
-                       const SliceGeometry& slice,
+                       const CoordinateSystem3D& slice,
                        double x, 
                        double y,
                        uint8_t red,
--- a/Framework/Layers/OrthancFrameLayerSource.cpp	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Layers/OrthancFrameLayerSource.cpp	Wed Jun 14 15:50:38 2017 +0200
@@ -88,7 +88,7 @@
 
 
   bool OrthancFrameLayerSource::GetExtent(std::vector<Vector>& points,
-                                          const SliceGeometry& viewportSlice)
+                                          const CoordinateSystem3D& viewportSlice)
   {
     size_t index;
     if (loader_.IsGeometryReady() &&
@@ -104,7 +104,7 @@
   }
 
   
-  void OrthancFrameLayerSource::ScheduleLayerCreation(const SliceGeometry& viewportSlice)
+  void OrthancFrameLayerSource::ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice)
   {
     size_t index;
 
--- a/Framework/Layers/OrthancFrameLayerSource.h	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Layers/OrthancFrameLayerSource.h	Wed Jun 14 15:50:38 2017 +0200
@@ -74,8 +74,8 @@
     }
 
     virtual bool GetExtent(std::vector<Vector>& points,
-                           const SliceGeometry& viewportSlice);
+                           const CoordinateSystem3D& viewportSlice);
 
-    virtual void ScheduleLayerCreation(const SliceGeometry& viewportSlice);
+    virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice);
   };
 }
--- a/Framework/Layers/SliceOutlineRenderer.h	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Layers/SliceOutlineRenderer.h	Wed Jun 14 15:50:38 2017 +0200
@@ -46,7 +46,7 @@
       style_ = style;
     }
 
-    virtual const SliceGeometry& GetLayerSlice()
+    virtual const CoordinateSystem3D& GetLayerSlice()
     {
       return slice_.GetGeometry();
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Toolbox/CoordinateSystem3D.cpp	Wed Jun 14 15:50:38 2017 +0200
@@ -0,0 +1,155 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "CoordinateSystem3D.h"
+
+#include "GeometryToolbox.h"
+
+#include "../../Resources/Orthanc/Core/Logging.h"
+#include "../../Resources/Orthanc/Core/Toolbox.h"
+#include "../../Resources/Orthanc/Core/OrthancException.h"
+
+namespace OrthancStone
+{
+  void CoordinateSystem3D::CheckAndComputeNormal()
+  {
+    // DICOM expects normal vectors to define the axes: "The row and
+    // column direction cosine vectors shall be normal, i.e., the dot
+    // product of each direction cosine vector with itself shall be
+    // unity."
+    // http://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.7.6.2.html
+    if (!GeometryToolbox::IsNear(boost::numeric::ublas::norm_2(axisX_), 1.0) ||
+        !GeometryToolbox::IsNear(boost::numeric::ublas::norm_2(axisY_), 1.0))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+
+    // The vectors within "Image Orientation Patient" must be
+    // orthogonal, according to the DICOM specification: "The row and
+    // column direction cosine vectors shall be orthogonal, i.e.,
+    // their dot product shall be zero."
+    // http://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.7.6.2.html
+    if (!GeometryToolbox::IsCloseToZero(boost::numeric::ublas::inner_prod(axisX_, axisY_)))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+
+    GeometryToolbox::CrossProduct(normal_, axisX_, axisY_);
+
+    // Just a sanity check, it should be useless by construction
+    assert(GeometryToolbox::IsNear(boost::numeric::ublas::norm_2(normal_), 1.0));
+  }
+
+
+  void CoordinateSystem3D::SetupCanonical()
+  {
+    GeometryToolbox::AssignVector(origin_, 0, 0, 0);
+    GeometryToolbox::AssignVector(axisX_, 1, 0, 0);
+    GeometryToolbox::AssignVector(axisY_, 0, 1, 0);
+    CheckAndComputeNormal();
+  }
+
+
+  CoordinateSystem3D::CoordinateSystem3D(const Vector& origin,
+                                         const Vector& axisX,
+                                         const Vector& axisY) :
+    origin_(origin),
+    axisX_(axisX),
+    axisY_(axisY)
+  {
+    CheckAndComputeNormal();
+  }
+
+
+  void CoordinateSystem3D::Setup(const std::string& imagePositionPatient,
+                                 const std::string& imageOrientationPatient)
+  {
+    std::string tmpPosition = Orthanc::Toolbox::StripSpaces(imagePositionPatient);
+    std::string tmpOrientation = Orthanc::Toolbox::StripSpaces(imageOrientationPatient);
+
+    Vector orientation;
+    if (!GeometryToolbox::ParseVector(origin_, tmpPosition) ||
+        !GeometryToolbox::ParseVector(orientation, tmpOrientation) ||
+        origin_.size() != 3 ||
+        orientation.size() != 6)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+
+    axisX_.resize(3);
+    axisX_[0] = orientation[0];
+    axisX_[1] = orientation[1];
+    axisX_[2] = orientation[2];
+
+    axisY_.resize(3);
+    axisY_[0] = orientation[3];
+    axisY_[1] = orientation[4];
+    axisY_[2] = orientation[5];
+
+    CheckAndComputeNormal();
+  }   
+
+
+  CoordinateSystem3D::CoordinateSystem3D(const OrthancPlugins::IDicomDataset& dicom)
+  {
+    std::string a, b;
+
+    if (dicom.GetStringValue(a, OrthancPlugins::DICOM_TAG_IMAGE_POSITION_PATIENT) &&
+        dicom.GetStringValue(b, OrthancPlugins::DICOM_TAG_IMAGE_ORIENTATION_PATIENT))
+    {
+      Setup(a, b);
+    }
+    else
+    {
+      SetupCanonical();
+    }
+  }   
+
+
+  Vector CoordinateSystem3D::MapSliceToWorldCoordinates(double x,
+                                                        double y) const
+  {
+    return origin_ + x * axisX_ + y * axisY_;
+  }
+
+
+  double CoordinateSystem3D::ProjectAlongNormal(const Vector& point) const
+  {
+    return boost::numeric::ublas::inner_prod(point, normal_);
+  }
+
+
+  void CoordinateSystem3D::ProjectPoint(double& offsetX,
+                                        double& offsetY,
+                                        const Vector& point) const
+  {
+    // Project the point onto the slice
+    Vector projection;
+    GeometryToolbox::ProjectPointOntoPlane(projection, point, normal_, origin_);
+
+    // As the axes are orthonormal vectors thanks to
+    // CheckAndComputeNormal(), the following dot products give the
+    // offset of the origin of the slice wrt. the origin of the
+    // reference plane https://en.wikipedia.org/wiki/Vector_projection
+    offsetX = boost::numeric::ublas::inner_prod(axisX_, projection - origin_);
+    offsetY = boost::numeric::ublas::inner_prod(axisY_, projection - origin_);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Toolbox/CoordinateSystem3D.h	Wed Jun 14 15:50:38 2017 +0200
@@ -0,0 +1,92 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017 Osimis, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "GeometryToolbox.h"
+#include "../../Resources/Orthanc/Plugins/Samples/Common/IDicomDataset.h"
+
+namespace OrthancStone
+{
+  // Geometry of a 3D plane
+  class CoordinateSystem3D
+  {
+  private:
+    Vector    origin_;
+    Vector    normal_;
+    Vector    axisX_;
+    Vector    axisY_;
+
+    void CheckAndComputeNormal();
+
+    void Setup(const std::string& imagePositionPatient,
+               const std::string& imageOrientationPatient);
+
+    void SetupCanonical();
+
+  public:
+    CoordinateSystem3D()
+    {
+      SetupCanonical();
+    }
+
+    CoordinateSystem3D(const Vector& origin,
+                       const Vector& axisX,
+                       const Vector& axisY);
+
+    CoordinateSystem3D(const OrthancPlugins::IDicomDataset& dicom);
+
+    CoordinateSystem3D(const std::string& imagePositionPatient,
+                       const std::string& imageOrientationPatient)
+    {
+      Setup(imagePositionPatient, imageOrientationPatient);
+    }
+
+    const Vector& GetNormal() const
+    {
+      return normal_;
+    }
+
+    const Vector& GetOrigin() const
+    {
+      return origin_;
+    }
+
+    const Vector& GetAxisX() const
+    {
+      return axisX_;
+    }
+
+    const Vector& GetAxisY() const
+    {
+      return axisY_;
+    }
+
+    Vector MapSliceToWorldCoordinates(double x,
+                                      double y) const;
+    
+    double ProjectAlongNormal(const Vector& point) const;
+
+    void ProjectPoint(double& offsetX,
+                      double& offsetY,
+                      const Vector& point) const;
+  };
+}
--- a/Framework/Toolbox/DicomStructureSet.cpp	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Toolbox/DicomStructureSet.cpp	Wed Jun 14 15:50:38 2017 +0200
@@ -61,7 +61,7 @@
   }
 
 
-  SliceGeometry DicomStructureSet::ExtractSliceGeometry(double& sliceThickness,
+  CoordinateSystem3D DicomStructureSet::ExtractSliceGeometry(double& sliceThickness,
                                                         OrthancPlugins::IOrthancConnection& orthanc,
                                                         const OrthancPlugins::IDicomDataset& tags,
                                                         size_t contourIndex,
@@ -122,7 +122,7 @@
     }
 
     FullOrthancDataset parentTags(orthanc, "/instances/" + parentLookup[0]["ID"].asString() + "/tags");
-    SliceGeometry slice(parentTags);
+    CoordinateSystem3D slice(parentTags);
 
     Vector v;
     if (GeometryToolbox::ParseVector(v, parentTags, DICOM_TAG_SLICE_THICKNESS) &&
@@ -161,7 +161,7 @@
 
 
   bool DicomStructureSet::IsPolygonOnSlice(const Polygon& polygon,
-                                           const SliceGeometry& geometry) const
+                                           const CoordinateSystem3D& geometry) const
   {
     double d = boost::numeric::ublas::inner_prod(geometry.GetOrigin(), normal_);
 
@@ -267,7 +267,7 @@
         }
 
         Polygon polygon;
-        SliceGeometry geometry = ExtractSliceGeometry(polygon.sliceThickness_, orthanc, tags, i, j);
+        CoordinateSystem3D geometry = ExtractSliceGeometry(polygon.sliceThickness_, orthanc, tags, i, j);
         polygon.projectionAlongNormal_ = geometry.ProjectAlongNormal(geometry.GetOrigin());
 
         for (size_t k = 0; k < countPoints; k++)
@@ -351,7 +351,7 @@
 
 
   void DicomStructureSet::Render(CairoContext& context,
-                                 const SliceGeometry& slice) const
+                                 const CoordinateSystem3D& slice) const
   {
     cairo_t* cr = context.GetObject();
 
--- a/Framework/Toolbox/DicomStructureSet.h	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Toolbox/DicomStructureSet.h	Wed Jun 14 15:50:38 2017 +0200
@@ -21,7 +21,7 @@
 
 #pragma once
 
-#include "SliceGeometry.h"
+#include "CoordinateSystem3D.h"
 #include "../Viewport/CairoContext.h"
 #include "../../Resources/Orthanc/Plugins/Samples/Common/IOrthancConnection.h"
 
@@ -59,16 +59,16 @@
     std::string  parentSeriesId_;
     Vector       normal_;
 
-    SliceGeometry ExtractSliceGeometry(double& sliceThickness,
-                                       OrthancPlugins::IOrthancConnection& orthanc,
-                                       const OrthancPlugins::IDicomDataset& tags,
-                                       size_t contourIndex,
-                                       size_t sliceIndex);
+    CoordinateSystem3D ExtractSliceGeometry(double& sliceThickness,
+                                            OrthancPlugins::IOrthancConnection& orthanc,
+                                            const OrthancPlugins::IDicomDataset& tags,
+                                            size_t contourIndex,
+                                            size_t sliceIndex);
 
     const Structure& GetStructure(size_t index) const;
 
     bool IsPolygonOnSlice(const Polygon& polygon,
-                          const SliceGeometry& geometry) const;
+                          const CoordinateSystem3D& geometry) const;
 
 
   public:
@@ -97,6 +97,6 @@
     }
 
     void Render(CairoContext& context,
-                const SliceGeometry& slice) const;
+                const CoordinateSystem3D& slice) const;
   };
 }
--- a/Framework/Toolbox/OrthancSeriesLoader.cpp	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Toolbox/OrthancSeriesLoader.cpp	Wed Jun 14 15:50:38 2017 +0200
@@ -34,9 +34,9 @@
   class OrthancSeriesLoader::Slice : public boost::noncopyable
   {
   private:
-    std::string     instanceId_;
-    SliceGeometry   geometry_;
-    double          projectionAlongNormal_;
+    std::string         instanceId_;
+    CoordinateSystem3D  geometry_;
+    double              projectionAlongNormal_;
 
   public:
     Slice(const std::string& instanceId,
@@ -52,7 +52,7 @@
       return instanceId_;
     }
 
-    const SliceGeometry& GetGeometry() const
+    const CoordinateSystem3D& GetGeometry() const
     {
       return geometry_;
     }
--- a/Framework/Toolbox/OrthancSlicesLoader.cpp	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Toolbox/OrthancSlicesLoader.cpp	Wed Jun 14 15:50:38 2017 +0200
@@ -597,7 +597,7 @@
   
 
   bool OrthancSlicesLoader::LookupSlice(size_t& index,
-                                        const SliceGeometry& plane) const
+                                        const CoordinateSystem3D& plane) const
   {
     if (state_ != State_GeometryReady)
     {
--- a/Framework/Toolbox/OrthancSlicesLoader.h	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Toolbox/OrthancSlicesLoader.h	Wed Jun 14 15:50:38 2017 +0200
@@ -123,7 +123,7 @@
     const Slice& GetSlice(size_t index) const;
 
     bool LookupSlice(size_t& index,
-                     const SliceGeometry& plane) const;
+                     const CoordinateSystem3D& plane) const;
 
     void ScheduleLoadSliceImage(size_t index,
                                 SliceImageQuality quality);
--- a/Framework/Toolbox/ParallelSlices.cpp	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Toolbox/ParallelSlices.cpp	Wed Jun 14 15:50:38 2017 +0200
@@ -41,7 +41,7 @@
     for (size_t i = 0; i < slices_.size(); i++)
     {
       assert(other.slices_[i] != NULL);
-      slices_[i] = new SliceGeometry(*other.slices_[i]);
+      slices_[i] = new CoordinateSystem3D(*other.slices_[i]);
     }
   }
 
@@ -59,16 +59,16 @@
   }
 
 
-  void ParallelSlices::AddSlice(const SliceGeometry& slice)
+  void ParallelSlices::AddSlice(const CoordinateSystem3D& slice)
   {
     if (slices_.empty())
     {
       normal_ = slice.GetNormal();
-      slices_.push_back(new SliceGeometry(slice));
+      slices_.push_back(new CoordinateSystem3D(slice));
     }
     else if (GeometryToolbox::IsParallel(slice.GetNormal(), normal_))
     {
-      slices_.push_back(new SliceGeometry(slice));
+      slices_.push_back(new CoordinateSystem3D(slice));
     }
     else
     {
@@ -82,12 +82,12 @@
                                 const Vector& axisX,
                                 const Vector& axisY)
   {
-    SliceGeometry slice(origin, axisX, axisY);
+    CoordinateSystem3D slice(origin, axisX, axisY);
     AddSlice(slice);
   }
 
 
-  const SliceGeometry& ParallelSlices::GetSlice(size_t index) const
+  const CoordinateSystem3D& ParallelSlices::GetSlice(size_t index) const
   {
     if (index >= slices_.size())
     {
@@ -135,7 +135,7 @@
 
     for (size_t i = slices_.size(); i > 0; i--)
     {
-      const SliceGeometry& slice = *slices_[i - 1];
+      const CoordinateSystem3D& slice = *slices_[i - 1];
 
       reversed->AddSlice(slice.GetOrigin(),
                          -slice.GetAxisX(),
--- a/Framework/Toolbox/ParallelSlices.h	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Toolbox/ParallelSlices.h	Wed Jun 14 15:50:38 2017 +0200
@@ -21,15 +21,15 @@
 
 #pragma once
 
-#include "SliceGeometry.h"
+#include "CoordinateSystem3D.h"
 
 namespace OrthancStone
 {
   class ParallelSlices : public boost::noncopyable
   {
   private:
-    Vector                       normal_;
-    std::vector<SliceGeometry*>  slices_;
+    Vector                            normal_;
+    std::vector<CoordinateSystem3D*>  slices_;
     
     ParallelSlices& operator= (const ParallelSlices& other);  // Forbidden
 
@@ -45,7 +45,7 @@
       return normal_;
     }
 
-    void AddSlice(const SliceGeometry& slice);
+    void AddSlice(const CoordinateSystem3D& slice);
 
     void AddSlice(const Vector& origin,
                   const Vector& axisX,
@@ -56,7 +56,7 @@
       return slices_.size();
     }
 
-    const SliceGeometry& GetSlice(size_t index) const;
+    const CoordinateSystem3D& GetSlice(size_t index) const;
 
     bool ComputeClosestSlice(size_t& closestSlice,
                              double& closestDistance,
--- a/Framework/Toolbox/ParallelSlicesCursor.cpp	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Toolbox/ParallelSlicesCursor.cpp	Wed Jun 14 15:50:38 2017 +0200
@@ -51,11 +51,11 @@
   }
 
 
-  SliceGeometry ParallelSlicesCursor::GetSlice(size_t slice)
+  CoordinateSystem3D ParallelSlicesCursor::GetSlice(size_t slice)
   {
     if (slices_.get() == NULL)
     {
-      return SliceGeometry();
+      return CoordinateSystem3D();
     }
     else
     {
@@ -72,7 +72,7 @@
   }
 
 
-  SliceGeometry ParallelSlicesCursor::GetCurrentSlice()
+  CoordinateSystem3D ParallelSlicesCursor::GetCurrentSlice()
   {
     if (slices_.get() != NULL &&
         currentSlice_ < slices_->GetSliceCount())
@@ -81,7 +81,7 @@
     }
     else
     {
-      return SliceGeometry();  // No slice is available, return the canonical geometry
+      return CoordinateSystem3D();  // No slice is available, return the canonical geometry
     }
   }
 
--- a/Framework/Toolbox/ParallelSlicesCursor.h	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Toolbox/ParallelSlicesCursor.h	Wed Jun 14 15:50:38 2017 +0200
@@ -44,9 +44,9 @@
 
     size_t GetSliceCount();
 
-    SliceGeometry GetSlice(size_t slice);
+    CoordinateSystem3D GetSlice(size_t slice);
 
-    SliceGeometry GetCurrentSlice();
+    CoordinateSystem3D GetCurrentSlice();
 
     // Returns "true" iff. the slice has actually changed
     bool SetDefaultSlice();
--- a/Framework/Toolbox/Slice.cpp	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Toolbox/Slice.cpp	Wed Jun 14 15:50:38 2017 +0200
@@ -53,7 +53,7 @@
     if (dataset.GetStringValue(position, OrthancPlugins::DICOM_TAG_IMAGE_POSITION_PATIENT) &&
         dataset.GetStringValue(orientation, OrthancPlugins::DICOM_TAG_IMAGE_ORIENTATION_PATIENT))
     {
-      geometry_ = SliceGeometry(position, orientation);
+      geometry_ = CoordinateSystem3D(position, orientation);
     }
       
     if (reader.GetUnsignedIntegerValue(width_, OrthancPlugins::DICOM_TAG_COLUMNS) &&
@@ -95,7 +95,7 @@
   }
 
   
-  const SliceGeometry& Slice::GetGeometry() const
+  const CoordinateSystem3D& Slice::GetGeometry() const
   {
     if (type_ == Type_Invalid)
     {
@@ -172,7 +172,7 @@
   }
 
 
-  bool Slice::ContainsPlane(const SliceGeometry& plane) const
+  bool Slice::ContainsPlane(const CoordinateSystem3D& plane) const
   {
     if (type_ == Type_Invalid)
     {
--- a/Framework/Toolbox/Slice.h	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Toolbox/Slice.h	Wed Jun 14 15:50:38 2017 +0200
@@ -21,7 +21,7 @@
 
 #pragma once
 
-#include "SliceGeometry.h"
+#include "CoordinateSystem3D.h"
 #include "DicomFrameConverter.h"
 
 namespace OrthancStone
@@ -40,7 +40,7 @@
     Type                 type_;
     std::string          orthancInstanceId_;
     unsigned int         frame_;
-    SliceGeometry        geometry_;
+    CoordinateSystem3D   geometry_;
     double               pixelSpacingX_;
     double               pixelSpacingY_;
     double               thickness_;
@@ -55,7 +55,7 @@
 
     // TODO Is this constructor the best way to go to tackle missing
     // layers within LayerWidget?
-    Slice(const SliceGeometry& plane,
+    Slice(const CoordinateSystem3D& plane,
           double thickness) :
       type_(Type_Standalone),
       frame_(0),
@@ -68,7 +68,7 @@
     {      
     }
 
-    Slice(const SliceGeometry& plane,
+    Slice(const CoordinateSystem3D& plane,
           double pixelSpacingX,
           double pixelSpacingY,
           double thickness,
@@ -104,7 +104,7 @@
 
     unsigned int GetFrame() const;
 
-    const SliceGeometry& GetGeometry() const;
+    const CoordinateSystem3D& GetGeometry() const;
 
     double GetThickness() const;
 
@@ -118,7 +118,7 @@
 
     const DicomFrameConverter& GetConverter() const;
 
-    bool ContainsPlane(const SliceGeometry& plane) const;
+    bool ContainsPlane(const CoordinateSystem3D& plane) const;
 
     void GetExtent(std::vector<Vector>& points) const;
   };
--- a/Framework/Toolbox/SliceGeometry.cpp	Wed Jun 14 15:34:08 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,155 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017 Osimis, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "SliceGeometry.h"
-
-#include "GeometryToolbox.h"
-
-#include "../../Resources/Orthanc/Core/Logging.h"
-#include "../../Resources/Orthanc/Core/Toolbox.h"
-#include "../../Resources/Orthanc/Core/OrthancException.h"
-
-namespace OrthancStone
-{
-  void SliceGeometry::CheckAndComputeNormal()
-  {
-    // DICOM expects normal vectors to define the axes: "The row and
-    // column direction cosine vectors shall be normal, i.e., the dot
-    // product of each direction cosine vector with itself shall be
-    // unity."
-    // http://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.7.6.2.html
-    if (!GeometryToolbox::IsNear(boost::numeric::ublas::norm_2(axisX_), 1.0) ||
-        !GeometryToolbox::IsNear(boost::numeric::ublas::norm_2(axisY_), 1.0))
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-    }
-
-    // The vectors within "Image Orientation Patient" must be
-    // orthogonal, according to the DICOM specification: "The row and
-    // column direction cosine vectors shall be orthogonal, i.e.,
-    // their dot product shall be zero."
-    // http://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.7.6.2.html
-    if (!GeometryToolbox::IsCloseToZero(boost::numeric::ublas::inner_prod(axisX_, axisY_)))
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-    }
-
-    GeometryToolbox::CrossProduct(normal_, axisX_, axisY_);
-
-    // Just a sanity check, it should be useless by construction
-    assert(GeometryToolbox::IsNear(boost::numeric::ublas::norm_2(normal_), 1.0));
-  }
-
-
-  void SliceGeometry::SetupCanonical()
-  {
-    GeometryToolbox::AssignVector(origin_, 0, 0, 0);
-    GeometryToolbox::AssignVector(axisX_, 1, 0, 0);
-    GeometryToolbox::AssignVector(axisY_, 0, 1, 0);
-    CheckAndComputeNormal();
-  }
-
-
-  SliceGeometry::SliceGeometry(const Vector& origin,
-                               const Vector& axisX,
-                               const Vector& axisY) :
-    origin_(origin),
-    axisX_(axisX),
-    axisY_(axisY)
-  {
-    CheckAndComputeNormal();
-  }
-
-
-  void SliceGeometry::Setup(const std::string& imagePositionPatient,
-                            const std::string& imageOrientationPatient)
-  {
-    std::string tmpPosition = Orthanc::Toolbox::StripSpaces(imagePositionPatient);
-    std::string tmpOrientation = Orthanc::Toolbox::StripSpaces(imageOrientationPatient);
-
-    Vector orientation;
-    if (!GeometryToolbox::ParseVector(origin_, tmpPosition) ||
-        !GeometryToolbox::ParseVector(orientation, tmpOrientation) ||
-        origin_.size() != 3 ||
-        orientation.size() != 6)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-    }
-
-    axisX_.resize(3);
-    axisX_[0] = orientation[0];
-    axisX_[1] = orientation[1];
-    axisX_[2] = orientation[2];
-
-    axisY_.resize(3);
-    axisY_[0] = orientation[3];
-    axisY_[1] = orientation[4];
-    axisY_[2] = orientation[5];
-
-    CheckAndComputeNormal();
-  }   
-
-
-  SliceGeometry::SliceGeometry(const OrthancPlugins::IDicomDataset& dicom)
-  {
-    std::string a, b;
-
-    if (dicom.GetStringValue(a, OrthancPlugins::DICOM_TAG_IMAGE_POSITION_PATIENT) &&
-        dicom.GetStringValue(b, OrthancPlugins::DICOM_TAG_IMAGE_ORIENTATION_PATIENT))
-    {
-      Setup(a, b);
-    }
-    else
-    {
-      SetupCanonical();
-    }
-  }   
-
-
-  Vector SliceGeometry::MapSliceToWorldCoordinates(double x,
-                                                   double y) const
-  {
-    return origin_ + x * axisX_ + y * axisY_;
-  }
-
-
-  double SliceGeometry::ProjectAlongNormal(const Vector& point) const
-  {
-    return boost::numeric::ublas::inner_prod(point, normal_);
-  }
-
-
-  void SliceGeometry::ProjectPoint(double& offsetX,
-                                   double& offsetY,
-                                   const Vector& point) const
-  {
-    // Project the point onto the slice
-    Vector projection;
-    GeometryToolbox::ProjectPointOntoPlane(projection, point, normal_, origin_);
-
-    // As the axes are orthonormal vectors thanks to
-    // CheckAndComputeNormal(), the following dot products give the
-    // offset of the origin of the slice wrt. the origin of the
-    // reference plane https://en.wikipedia.org/wiki/Vector_projection
-    offsetX = boost::numeric::ublas::inner_prod(axisX_, projection - origin_);
-    offsetY = boost::numeric::ublas::inner_prod(axisY_, projection - origin_);
-  }
-}
--- a/Framework/Toolbox/SliceGeometry.h	Wed Jun 14 15:34:08 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,92 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017 Osimis, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "GeometryToolbox.h"
-#include "../../Resources/Orthanc/Plugins/Samples/Common/IDicomDataset.h"
-
-namespace OrthancStone
-{
-  // Geometry of a 3D plane
-  class SliceGeometry
-  {
-  private:
-    Vector    origin_;
-    Vector    normal_;
-    Vector    axisX_;
-    Vector    axisY_;
-
-    void CheckAndComputeNormal();
-
-    void Setup(const std::string& imagePositionPatient,
-               const std::string& imageOrientationPatient);
-
-    void SetupCanonical();
-
-  public:
-    SliceGeometry()
-    {
-      SetupCanonical();
-    }
-
-    SliceGeometry(const Vector& origin,
-                  const Vector& axisX,
-                  const Vector& axisY);
-
-    SliceGeometry(const OrthancPlugins::IDicomDataset& dicom);
-
-    SliceGeometry(const std::string& imagePositionPatient,
-                  const std::string& imageOrientationPatient)
-    {
-      Setup(imagePositionPatient, imageOrientationPatient);
-    }
-
-    const Vector& GetNormal() const
-    {
-      return normal_;
-    }
-
-    const Vector& GetOrigin() const
-    {
-      return origin_;
-    }
-
-    const Vector& GetAxisX() const
-    {
-      return axisX_;
-    }
-
-    const Vector& GetAxisY() const
-    {
-      return axisY_;
-    }
-
-    Vector MapSliceToWorldCoordinates(double x,
-                                      double y) const;
-    
-    double ProjectAlongNormal(const Vector& point) const;
-
-    void ProjectPoint(double& offsetX,
-                      double& offsetY,
-                      const Vector& point) const;
-  };
-}
--- a/Framework/Toolbox/SlicesSorter.cpp	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Toolbox/SlicesSorter.cpp	Wed Jun 14 15:50:38 2017 +0200
@@ -195,7 +195,7 @@
 
 
   bool SlicesSorter::LookupSlice(size_t& index,
-                                 const SliceGeometry& slice) const
+                                 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
--- a/Framework/Toolbox/SlicesSorter.h	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Toolbox/SlicesSorter.h	Wed Jun 14 15:50:38 2017 +0200
@@ -66,6 +66,6 @@
     bool SelectNormal(Vector& normal) const;
 
     bool LookupSlice(size_t& index,
-                     const SliceGeometry& slice) const;
+                     const CoordinateSystem3D& slice) const;
   };
 }
--- a/Framework/Volumes/ImageBuffer3D.cpp	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Volumes/ImageBuffer3D.cpp	Wed Jun 14 15:50:38 2017 +0200
@@ -132,7 +132,7 @@
   }
 
 
-  void ImageBuffer3D::SetAxialGeometry(const SliceGeometry& geometry)
+  void ImageBuffer3D::SetAxialGeometry(const CoordinateSystem3D& geometry)
   {
     axialGeometry_ = geometry;
   }
--- a/Framework/Volumes/ImageBuffer3D.h	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Volumes/ImageBuffer3D.h	Wed Jun 14 15:50:38 2017 +0200
@@ -22,7 +22,7 @@
 #pragma once
 
 #include "../Enumerations.h"
-#include "../Toolbox/SliceGeometry.h"
+#include "../Toolbox/CoordinateSystem3D.h"
 #include "../Toolbox/ParallelSlices.h"
 
 #include "../../Resources/Orthanc/Core/Images/Image.h"
@@ -32,7 +32,7 @@
   class ImageBuffer3D : public boost::noncopyable
   {
   private:
-    SliceGeometry          axialGeometry_;
+    CoordinateSystem3D     axialGeometry_;
     Vector                 voxelDimensions_;
     Orthanc::Image         image_;
     Orthanc::PixelFormat   format_;
@@ -58,7 +58,7 @@
 
     // Set the geometry of the first axial slice (i.e. the one whose
     // depth == 0)
-    void SetAxialGeometry(const SliceGeometry& geometry);
+    void SetAxialGeometry(const CoordinateSystem3D& geometry);
 
     void SetVoxelDimensions(double x,
                             double y,
--- a/Framework/Widgets/IWorldSceneInteractor.h	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Widgets/IWorldSceneInteractor.h	Wed Jun 14 15:50:38 2017 +0200
@@ -23,7 +23,6 @@
 
 #include "IWorldSceneMouseTracker.h"
 
-#include "../Toolbox/SliceGeometry.h"
 #include "../Toolbox/ViewportGeometry.h"
 #include "../Enumerations.h"
 #include "../Viewport/IStatusBar.h"
--- a/Framework/Widgets/LayerWidget.cpp	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Widgets/LayerWidget.cpp	Wed Jun 14 15:50:38 2017 +0200
@@ -31,7 +31,7 @@
   class LayerWidget::Scene : public boost::noncopyable
   {
   private:
-    SliceGeometry                 slice_;
+    CoordinateSystem3D            slice_;
     size_t                        countMissing_;
     std::vector<ILayerRenderer*>  renderers_;
 
@@ -54,7 +54,7 @@
     }
       
   public:
-    Scene(const SliceGeometry& slice,
+    Scene(const CoordinateSystem3D& slice,
           size_t countLayers) :
       slice_(slice),
       countMissing_(countLayers),
@@ -84,7 +84,7 @@
       countMissing_--;
     }
 
-    const SliceGeometry& GetSlice() const
+    const CoordinateSystem3D& GetSlice() const
     {
       return slice_;
     }
@@ -106,7 +106,7 @@
 
     bool RenderScene(CairoContext& context,
                      const ViewportGeometry& view,
-                     const SliceGeometry& viewportSlice)
+                     const CoordinateSystem3D& viewportSlice)
     {
       bool fullQuality = true;
       cairo_t *cr = context.GetObject();
@@ -115,7 +115,7 @@
       {
         if (renderers_[i] != NULL)
         {
-          const SliceGeometry& frameSlice = renderers_[i]->GetLayerSlice();
+          const CoordinateSystem3D& frameSlice = renderers_[i]->GetLayerSlice();
           
           double x0, y0, x1, y1, x2, y2;
           viewportSlice.ProjectPoint(x0, y0, frameSlice.GetOrigin());
@@ -374,7 +374,7 @@
   }
   
 
-  void LayerWidget::SetSlice(const SliceGeometry& slice)
+  void LayerWidget::SetSlice(const CoordinateSystem3D& slice)
   {
     LOG(INFO) << "Setting slice origin: (" << slice.GetOrigin()[0]
               << "," << slice.GetOrigin()[1]
--- a/Framework/Widgets/LayerWidget.h	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Widgets/LayerWidget.h	Wed Jun 14 15:50:38 2017 +0200
@@ -42,7 +42,7 @@
     LayersIndex                 layersIndex_;
     std::vector<ILayerSource*>  layers_;
     std::vector<RenderStyle>    styles_;
-    SliceGeometry               slice_;
+    CoordinateSystem3D          slice_;
     std::auto_ptr<Scene>        currentScene_;
     std::auto_ptr<Scene>        pendingScene_;
     std::vector<bool>           changedLayers_;
@@ -100,9 +100,9 @@
     void SetLayerStyle(size_t layer,
                        const RenderStyle& style);
 
-    void SetSlice(const SliceGeometry& slice);
+    void SetSlice(const CoordinateSystem3D& slice);
 
-    const SliceGeometry& GetSlice() const
+    const CoordinateSystem3D& GetSlice() const
     {
       return slice_;
     }
--- a/Framework/Widgets/TestWorldSceneWidget.cpp	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Widgets/TestWorldSceneWidget.cpp	Wed Jun 14 15:50:38 2017 +0200
@@ -21,6 +21,7 @@
 
 #include "TestWorldSceneWidget.h"
 
+#include <math.h>
 #include <stdio.h>
 
 namespace OrthancStone
--- a/Framework/Widgets/TestWorldSceneWidget.h	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Widgets/TestWorldSceneWidget.h	Wed Jun 14 15:50:38 2017 +0200
@@ -23,6 +23,8 @@
 
 #include "WorldSceneWidget.h"
 
+#include <memory>
+
 namespace OrthancStone
 {
   namespace Samples
--- a/Framework/Widgets/WorldSceneWidget.cpp	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/Widgets/WorldSceneWidget.cpp	Wed Jun 14 15:50:38 2017 +0200
@@ -21,6 +21,10 @@
 
 #include "WorldSceneWidget.h"
 
+#include <math.h>
+#include <memory>
+#include <cassert>
+
 namespace OrthancStone
 {
   static void MapMouseToScene(double& sceneX,
--- a/Framework/dev.h	Wed Jun 14 15:34:08 2017 +0200
+++ b/Framework/dev.h	Wed Jun 14 15:50:38 2017 +0200
@@ -250,7 +250,7 @@
     double               pixelSpacingX_;
     double               pixelSpacingY_;
     double               sliceThickness_;
-    SliceGeometry        reference_;
+    CoordinateSystem3D   reference_;
     DicomFrameConverter  converter_;
     
     double ComputeAxialThickness(const OrthancVolumeImage& volume) const
@@ -315,7 +315,7 @@
       origin += (static_cast<double>(volume.GetSliceCount() - 1) *
                  axialThickness * axial.GetGeometry().GetNormal());
       
-      reference_ = SliceGeometry(origin,
+      reference_ = CoordinateSystem3D(origin,
                                  axial.GetGeometry().GetAxisX(), 
                                  -axial.GetGeometry().GetNormal());
     }
@@ -337,7 +337,7 @@
       origin += (static_cast<double>(volume.GetSliceCount() - 1) *
                  axialThickness * axial.GetGeometry().GetNormal());
       
-      reference_ = SliceGeometry(origin,
+      reference_ = CoordinateSystem3D(origin,
                                  axial.GetGeometry().GetAxisY(), 
                                  axial.GetGeometry().GetNormal());
     }
@@ -383,7 +383,7 @@
     }
     
     bool LookupSlice(size_t& index,
-                     const SliceGeometry& slice) const
+                     const CoordinateSystem3D& slice) const
     {
       bool opposite;
       if (!GeometryToolbox::IsParallelOrOpposite(opposite,
@@ -419,7 +419,7 @@
       }
       else
       {
-        SliceGeometry origin(reference_.GetOrigin() +
+        CoordinateSystem3D origin(reference_.GetOrigin() +
                              static_cast<double>(slice) * sliceThickness_ * reference_.GetNormal(),
                              reference_.GetAxisX(),
                              reference_.GetAxisY());
@@ -505,7 +505,7 @@
 
 
     bool DetectProjection(VolumeProjection& projection,
-                          const SliceGeometry& viewportSlice)
+                          const CoordinateSystem3D& viewportSlice)
     {
       bool isOpposite;  // Ignored
 
@@ -545,7 +545,7 @@
     }
 
     virtual bool GetExtent(std::vector<Vector>& points,
-                           const SliceGeometry& viewportSlice)
+                           const CoordinateSystem3D& viewportSlice)
     {
       VolumeProjection projection;
       
@@ -565,7 +565,7 @@
     }
     
 
-    virtual void ScheduleLayerCreation(const SliceGeometry& viewportSlice)
+    virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice)
     {
       VolumeProjection projection;
       
--- a/Resources/CMake/OrthancStone.cmake	Wed Jun 14 15:34:08 2017 +0200
+++ b/Resources/CMake/OrthancStone.cmake	Wed Jun 14 15:50:38 2017 +0200
@@ -214,7 +214,7 @@
   ${ORTHANC_STONE_DIR}/Framework/Toolbox/ParallelSlices.cpp
   ${ORTHANC_STONE_DIR}/Framework/Toolbox/ParallelSlicesCursor.cpp
   ${ORTHANC_STONE_DIR}/Framework/Toolbox/Slice.cpp
-  ${ORTHANC_STONE_DIR}/Framework/Toolbox/SliceGeometry.cpp
+  ${ORTHANC_STONE_DIR}/Framework/Toolbox/CoordinateSystem3D.cpp
   ${ORTHANC_STONE_DIR}/Framework/Toolbox/SlicesSorter.cpp
   ${ORTHANC_STONE_DIR}/Framework/Toolbox/ViewportGeometry.cpp
   ${ORTHANC_STONE_DIR}/Framework/Viewport/CairoContext.cpp