diff Applications/Samples/SingleFrameEditorApplication.h @ 408:6834c236b36d

reorganization
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 12 Nov 2018 14:52:10 +0100
parents 842a3c7cfdc0
children 6decc0ba9da5
line wrap: on
line diff
--- a/Applications/Samples/SingleFrameEditorApplication.h	Mon Nov 12 11:44:20 2018 +0100
+++ b/Applications/Samples/SingleFrameEditorApplication.h	Mon Nov 12 14:52:10 2018 +0100
@@ -23,9 +23,9 @@
 
 #include "SampleApplicationBase.h"
 
-#include "../../Framework/Toolbox/ImageGeometry.h"
-#include "../../Framework/Toolbox/OrthancApiClient.h"
-#include "../../Framework/Toolbox/DicomFrameConverter.h"
+#include "../../Framework/Radiography/RadiographyScene.h"
+
+#include "../../Framework/Toolbox/UndoRedoStack.h"
 
 #include <Core/Images/FontRegistry.h>
 #include <Core/Images/Image.h>
@@ -51,1478 +51,26 @@
 
 namespace OrthancStone
 {
-  class RadiologyScene :
-    public IObserver,
-    public IObservable
+  class RadiographyLayerCommand : public UndoRedoStack::ICommand
   {
-  public:
-    typedef OriginMessage<MessageType_Widget_GeometryChanged, RadiologyScene> GeometryChangedMessage;
-    typedef OriginMessage<MessageType_Widget_ContentChanged, RadiologyScene> ContentChangedMessage;
-
-
-    enum Corner
-    {
-      Corner_TopLeft,
-      Corner_TopRight,
-      Corner_BottomLeft,
-      Corner_BottomRight
-    };
-
-
-
-    class Layer : public boost::noncopyable
-    {
-      friend class RadiologyScene;
-      
-    private:
-      size_t        index_;
-      bool          hasSize_;
-      unsigned int  width_;
-      unsigned int  height_;
-      bool          hasCrop_;
-      unsigned int  cropX_;
-      unsigned int  cropY_;
-      unsigned int  cropWidth_;
-      unsigned int  cropHeight_;
-      Matrix        transform_;
-      Matrix        transformInverse_;
-      double        pixelSpacingX_;
-      double        pixelSpacingY_;
-      double        panX_;
-      double        panY_;
-      double        angle_;
-      bool          resizeable_;
-
-
-    protected:
-      const Matrix& GetTransform() const
-      {
-        return transform_;
-      }
-
-
-    private:
-      static void ApplyTransform(double& x /* inout */,
-                                 double& y /* inout */,
-                                 const Matrix& transform)
-      {
-        Vector p;
-        LinearAlgebra::AssignVector(p, x, y, 1);
-
-        Vector q = LinearAlgebra::Product(transform, p);
-
-        if (!LinearAlgebra::IsNear(q[2], 1.0))
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-        }
-        else
-        {
-          x = q[0];
-          y = q[1];
-        }
-      }
-      
-      
-      void UpdateTransform()
-      {
-        transform_ = CreateScalingMatrix(pixelSpacingX_, pixelSpacingY_);
-
-        double centerX, centerY;
-        GetCenter(centerX, centerY);
-
-        transform_ = LinearAlgebra::Product(
-          CreateOffsetMatrix(panX_ + centerX, panY_ + centerY),
-          CreateRotationMatrix(angle_),
-          CreateOffsetMatrix(-centerX, -centerY),
-          transform_);
-
-        LinearAlgebra::InvertMatrix(transformInverse_, transform_);
-      }
-
-
-      void AddToExtent(Extent2D& extent,
-                       double x,
-                       double y) const
-      {
-        ApplyTransform(x, y, transform_);
-        extent.AddPoint(x, y);
-      }
-
-
-      void GetCornerInternal(double& x,
-                             double& y,
-                             Corner corner,
-                             unsigned int cropX,
-                             unsigned int cropY,
-                             unsigned int cropWidth,
-                             unsigned int cropHeight) const
-      {
-        double dx = static_cast<double>(cropX);
-        double dy = static_cast<double>(cropY);
-        double dwidth = static_cast<double>(cropWidth);
-        double dheight = static_cast<double>(cropHeight);
-
-        switch (corner)
-        {
-          case Corner_TopLeft:
-            x = dx;
-            y = dy;
-            break;
-
-          case Corner_TopRight:
-            x = dx + dwidth;
-            y = dy;
-            break;
-
-          case Corner_BottomLeft:
-            x = dx;
-            y = dy + dheight;
-            break;
-
-          case Corner_BottomRight:
-            x = dx + dwidth;
-            y = dy + dheight;
-            break;
-
-          default:
-            throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-        }
-
-        ApplyTransform(x, y, transform_);
-      }
-
-
-      void SetIndex(size_t index)
-      {
-        index_ = index;
-      }
-
-      
-      bool Contains(double x,
-                    double y) const
-      {
-        ApplyTransform(x, y, transformInverse_);
-        
-        unsigned int cropX, cropY, cropWidth, cropHeight;
-        GetCrop(cropX, cropY, cropWidth, cropHeight);
-
-        return (x >= cropX && x <= cropX + cropWidth &&
-                y >= cropY && y <= cropY + cropHeight);
-      }
-
-
-      void DrawBorders(CairoContext& context,
-                       double zoom)
-      {
-        unsigned int cx, cy, width, height;
-        GetCrop(cx, cy, width, height);
-
-        double dx = static_cast<double>(cx);
-        double dy = static_cast<double>(cy);
-        double dwidth = static_cast<double>(width);
-        double dheight = static_cast<double>(height);
-
-        cairo_t* cr = context.GetObject();
-        cairo_set_line_width(cr, 2.0 / zoom);
-        
-        double x, y;
-        x = dx;
-        y = dy;
-        ApplyTransform(x, y, transform_);
-        cairo_move_to(cr, x, y);
-
-        x = dx + dwidth;
-        y = dy;
-        ApplyTransform(x, y, transform_);
-        cairo_line_to(cr, x, y);
-
-        x = dx + dwidth;
-        y = dy + dheight;
-        ApplyTransform(x, y, transform_);
-        cairo_line_to(cr, x, y);
-
-        x = dx;
-        y = dy + dheight;
-        ApplyTransform(x, y, transform_);
-        cairo_line_to(cr, x, y);
-
-        x = dx;
-        y = dy;
-        ApplyTransform(x, y, transform_);
-        cairo_line_to(cr, x, y);
-
-        cairo_stroke(cr);
-      }
-
-
-      static double Square(double x)
-      {
-        return x * x;
-      }
-
-
-    public:
-      Layer() :
-        index_(0),
-        hasSize_(false),
-        width_(0),
-        height_(0),
-        hasCrop_(false),
-        pixelSpacingX_(1),
-        pixelSpacingY_(1),
-        panX_(0),
-        panY_(0),
-        angle_(0),
-        resizeable_(false)
-      {
-        UpdateTransform();
-      }
-
-      virtual ~Layer()
-      {
-      }
-
-      size_t GetIndex() const
-      {
-        return index_;
-      }
-
-      void ResetCrop()
-      {
-        hasCrop_ = false;
-      }
-
-      void SetCrop(unsigned int x,
-                   unsigned int y,
-                   unsigned int width,
-                   unsigned int height)
-      {
-        if (!hasSize_)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-        }
-        
-        if (x + width > width_ ||
-            y + height > height_)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-        }
-        
-        hasCrop_ = true;
-        cropX_ = x;
-        cropY_ = y;
-        cropWidth_ = width;
-        cropHeight_ = height;
-
-        UpdateTransform();
-      }
-
-      void GetCrop(unsigned int& x,
-                   unsigned int& y,
-                   unsigned int& width,
-                   unsigned int& height) const
-      {
-        if (hasCrop_)
-        {
-          x = cropX_;
-          y = cropY_;
-          width = cropWidth_;
-          height = cropHeight_;
-        }
-        else 
-        {
-          x = 0;
-          y = 0;
-          width = width_;
-          height = height_;
-        }
-      }
-
-      void SetAngle(double angle)
-      {
-        angle_ = angle;
-        UpdateTransform();
-      }
-
-      double GetAngle() const
-      {
-        return angle_;
-      }
-
-      void SetSize(unsigned int width,
-                   unsigned int height)
-      {
-        if (hasSize_ &&
-            (width != width_ ||
-             height != height_))
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize);
-        }
-        
-        hasSize_ = true;
-        width_ = width;
-        height_ = height;
-
-        UpdateTransform();
-      }
-
-
-      unsigned int GetWidth() const
-      {
-        return width_;
-      }
-        
-
-      unsigned int GetHeight() const
-      {
-        return height_;
-      }       
-
-
-      Extent2D GetExtent() const
-      {
-        Extent2D extent;
-       
-        unsigned int x, y, width, height;
-        GetCrop(x, y, width, height);
-
-        double dx = static_cast<double>(x);
-        double dy = static_cast<double>(y);
-        double dwidth = static_cast<double>(width);
-        double dheight = static_cast<double>(height);
-
-        AddToExtent(extent, dx, dy);
-        AddToExtent(extent, dx + dwidth, dy);
-        AddToExtent(extent, dx, dy + dheight);
-        AddToExtent(extent, dx + dwidth, dy + dheight);
-        
-        return extent;
-      }
-
-
-      bool GetPixel(unsigned int& imageX,
-                    unsigned int& imageY,
-                    double sceneX,
-                    double sceneY) const
-      {
-        if (width_ == 0 ||
-            height_ == 0)
-        {
-          return false;
-        }
-        else
-        {
-          ApplyTransform(sceneX, sceneY, transformInverse_);
-        
-          int x = static_cast<int>(std::floor(sceneX));
-          int y = static_cast<int>(std::floor(sceneY));
-
-          if (x < 0)
-          {
-            imageX = 0;
-          }
-          else if (x >= static_cast<int>(width_))
-          {
-            imageX = width_;
-          }
-          else
-          {
-            imageX = static_cast<unsigned int>(x);
-          }
-
-          if (y < 0)
-          {
-            imageY = 0;
-          }
-          else if (y >= static_cast<int>(height_))
-          {
-            imageY = height_;
-          }
-          else
-          {
-            imageY = static_cast<unsigned int>(y);
-          }
-
-          return true;
-        }
-      }
-
-
-      void SetPan(double x,
-                  double y)
-      {
-        panX_ = x;
-        panY_ = y;
-        UpdateTransform();
-      }
-
-
-      void SetPixelSpacing(double x,
-                           double y)
-      {
-        pixelSpacingX_ = x;
-        pixelSpacingY_ = y;
-        UpdateTransform();
-      }
-
-      double GetPixelSpacingX() const
-      {
-        return pixelSpacingX_;
-      }   
-
-      double GetPixelSpacingY() const
-      {
-        return pixelSpacingY_;
-      }   
-
-      double GetPanX() const
-      {
-        return panX_;
-      }
-
-      double GetPanY() const
-      {
-        return panY_;
-      }
-
-      void GetCenter(double& centerX,
-                     double& centerY) const
-      {
-        centerX = static_cast<double>(width_) / 2.0;
-        centerY = static_cast<double>(height_) / 2.0;
-        ApplyTransform(centerX, centerY, transform_);
-      }
-
-
-      void GetCorner(double& x /* out */,
-                     double& y /* out */,
-                     Corner corner) const
-      {
-        unsigned int cropX, cropY, cropWidth, cropHeight;
-        GetCrop(cropX, cropY, cropWidth, cropHeight);
-        GetCornerInternal(x, y, corner, cropX, cropY, cropWidth, cropHeight);
-      }
-      
-      
-      bool LookupCorner(Corner& corner /* out */,
-                        double x,
-                        double y,
-                        double zoom,
-                        double viewportDistance) const
-      {
-        static const Corner CORNERS[] = {
-          Corner_TopLeft,
-          Corner_TopRight,
-          Corner_BottomLeft,
-          Corner_BottomRight
-        };
-        
-        unsigned int cropX, cropY, cropWidth, cropHeight;
-        GetCrop(cropX, cropY, cropWidth, cropHeight);
-
-        double threshold = Square(viewportDistance / zoom);
-        
-        for (size_t i = 0; i < 4; i++)
-        {
-          double cx, cy;
-          GetCornerInternal(cx, cy, CORNERS[i], cropX, cropY, cropWidth, cropHeight);
+  private:
+    RadiographyScene&  scene_;
+    size_t             layer_;
 
-          double d = Square(cx - x) + Square(cy - y);
-        
-          if (d <= threshold)
-          {
-            corner = CORNERS[i];
-            return true;
-          }
-        }
-        
-        return false;
-      }
-
-      bool IsResizeable() const
-      {
-        return resizeable_;
-      }
-
-      void SetResizeable(bool resizeable)
-      {
-        resizeable_ = resizeable;
-      }
-
-      virtual bool GetDefaultWindowing(float& center,
-                                       float& width) const = 0;
-
-      virtual void Render(Orthanc::ImageAccessor& buffer,
-                          const Matrix& viewTransform,
-                          ImageInterpolation interpolation) const = 0;
-
-      virtual bool GetRange(float& minValue,
-                            float& maxValue) const = 0;
-    }; 
-
-
-    class LayerAccessor : public boost::noncopyable
-    {
-    private:
-      RadiologyScene&  scene_;
-      size_t           index_;
-      Layer*           layer_;
-
-    public:
-      LayerAccessor(RadiologyScene& scene,
-                    size_t index) :
-        scene_(scene),
-        index_(index)
-      {
-        Layers::iterator layer = scene.layers_.find(index);
-        if (layer == scene.layers_.end())
-        {
-          layer_ = NULL;
-        }
-        else
-        {
-          assert(layer->second != NULL);
-          layer_ = layer->second;
-        }
-      }
-
-      LayerAccessor(RadiologyScene& scene,
-                    double x,
-                    double y) :
-        scene_(scene),
-        index_(0)  // Dummy initialization
-      {
-        if (scene.LookupLayer(index_, x, y))
-        {
-          Layers::iterator layer = scene.layers_.find(index_);
-          
-          if (layer == scene.layers_.end())
-          {
-            throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-          }
-          else
-          {
-            assert(layer->second != NULL);
-            layer_ = layer->second;
-          }
-        }
-        else
-        {
-          layer_ = NULL;
-        }
-      }
-
-      void Invalidate()
-      {
-        layer_ = NULL;
-      }
-
-      bool IsValid() const
-      {
-        return layer_ != NULL;
-      }
-
-      RadiologyScene& GetScene() const
-      {
-        if (IsValid())
-        {
-          return scene_;
-        }
-        else
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-        }
-      }
-
-      size_t GetIndex() const
-      {
-        if (IsValid())
-        {
-          return index_;
-        }
-        else
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-        }
-      }
-
-      Layer& GetLayer() const
-      {
-        if (IsValid())
-        {
-          return *layer_;
-        }
-        else
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-        }
-      }    
-    };    
-
-
-  private:
-    class AlphaLayer : public Layer
-    {
-    private:
-      const RadiologyScene&                  scene_;
-      std::auto_ptr<Orthanc::ImageAccessor>  alpha_;      // Grayscale8
-      bool                                   useWindowing_;
-      float                                  foreground_;
-
-    public:
-      AlphaLayer(const RadiologyScene& scene) :
-        scene_(scene),
-        useWindowing_(true),
-        foreground_(0)
-      {
-      }
-
-
-      void SetForegroundValue(float foreground)
-      {
-        useWindowing_ = false;
-        foreground_ = foreground;
-      }
-      
-      
-      void SetAlpha(Orthanc::ImageAccessor* image)
-      {
-        std::auto_ptr<Orthanc::ImageAccessor> raii(image);
-        
-        if (image == NULL)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-        }
-
-        if (image->GetFormat() != Orthanc::PixelFormat_Grayscale8)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
-        }
-
-        SetSize(image->GetWidth(), image->GetHeight());
-        alpha_ = raii;
-      }
-
-
-      void LoadText(const Orthanc::Font& font,
-                    const std::string& utf8)
-      {
-        SetAlpha(font.RenderAlpha(utf8));
-      }                   
-
-
-      virtual bool GetDefaultWindowing(float& center,
-                                       float& width) const
-      {
-        return false;
-      }
-      
-
-      virtual void Render(Orthanc::ImageAccessor& buffer,
-                          const Matrix& viewTransform,
-                          ImageInterpolation interpolation) const
-      {
-        if (alpha_.get() == NULL)
-        {
-          return;
-        }
-        
-        if (buffer.GetFormat() != Orthanc::PixelFormat_Float32)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
-        }
-
-        unsigned int cropX, cropY, cropWidth, cropHeight;
-        GetCrop(cropX, cropY, cropWidth, cropHeight);
-
-        Matrix m = LinearAlgebra::Product(viewTransform,
-                                          GetTransform(),
-                                          CreateOffsetMatrix(cropX, cropY));
-
-        Orthanc::ImageAccessor cropped;
-        alpha_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight);
-        
-        Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false);
-        ApplyProjectiveTransform(tmp, cropped, m, interpolation, true /* clear */);
-
-        // Blit
-        const unsigned int width = buffer.GetWidth();
-        const unsigned int height = buffer.GetHeight();
-
-        float value = foreground_;
-        
-        if (useWindowing_)
-        {
-          float center, width;
-          if (scene_.GetWindowing(center, width))
-          {
-            value = center + width / 2.0f;
-          }
-        }
-        
-        for (unsigned int y = 0; y < height; y++)
-        {
-          float *q = reinterpret_cast<float*>(buffer.GetRow(y));
-          const uint8_t *p = reinterpret_cast<uint8_t*>(tmp.GetRow(y));
+  protected:
+    virtual void UndoInternal(RadiographyScene::Layer& layer) const = 0;
 
-          for (unsigned int x = 0; x < width; x++, p++, q++)
-          {
-            float a = static_cast<float>(*p) / 255.0f;
-            
-            *q = (a * value + (1.0f - a) * (*q));
-          }
-        }        
-      }
-
-      
-      virtual bool GetRange(float& minValue,
-                            float& maxValue) const
-      {
-        if (useWindowing_)
-        {
-          return false;
-        }
-        else
-        {
-          minValue = 0;
-          maxValue = 0;
-
-          if (foreground_ < 0)
-          {
-            minValue = foreground_;
-          }
-
-          if (foreground_ > 0)
-          {
-            maxValue = foreground_;
-          }
-
-          return true;
-        }
-      }
-    };
-    
-    
-
-  private:
-    static Matrix CreateOffsetMatrix(double dx,
-                                     double dy)
-    {
-      Matrix m = LinearAlgebra::IdentityMatrix(3);
-      m(0, 2) = dx;
-      m(1, 2) = dy;
-      return m;
-    }
-      
-
-    static Matrix CreateScalingMatrix(double sx,
-                                      double sy)
-    {
-      Matrix m = LinearAlgebra::IdentityMatrix(3);
-      m(0, 0) = sx;
-      m(1, 1) = sy;
-      return m;
-    }
-      
-
-    static Matrix CreateRotationMatrix(double angle)
-    {
-      Matrix m;
-      const double v[] = { cos(angle), -sin(angle), 0,
-                           sin(angle), cos(angle), 0,
-                           0, 0, 1 };
-      LinearAlgebra::FillMatrix(m, 3, 3, v);
-      return m;
-    }
-      
-
-    class DicomLayer : public Layer
-    {
-    private:
-      std::auto_ptr<Orthanc::ImageAccessor>  source_;  // Content of PixelData
-      std::auto_ptr<DicomFrameConverter>     converter_;
-      std::auto_ptr<Orthanc::ImageAccessor>  converted_;  // Float32
-
-      static OrthancPlugins::DicomTag  ConvertTag(const Orthanc::DicomTag& tag)
-      {
-        return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement());
-      }
-      
-
-      void ApplyConverter()
-      {
-        if (source_.get() != NULL &&
-            converter_.get() != NULL)
-        {
-          converted_.reset(converter_->ConvertFrame(*source_));
-        }
-      }
-      
-    public:
-      void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset)
-      {
-        converter_.reset(new DicomFrameConverter);
-        converter_->ReadParameters(dataset);
-        ApplyConverter();
-
-        std::string tmp;
-        Vector pixelSpacing;
-        
-        if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PIXEL_SPACING)) &&
-            LinearAlgebra::ParseVector(pixelSpacing, tmp) &&
-            pixelSpacing.size() == 2)
-        {
-          SetPixelSpacing(pixelSpacing[0], pixelSpacing[1]);
-        }
-
-        //SetPan(-0.5 * GetPixelSpacingX(), -0.5 * GetPixelSpacingY());
-      
-        OrthancPlugins::DicomDatasetReader reader(dataset);
-
-        unsigned int width, height;
-        if (!reader.GetUnsignedIntegerValue(width, ConvertTag(Orthanc::DICOM_TAG_COLUMNS)) ||
-            !reader.GetUnsignedIntegerValue(height, ConvertTag(Orthanc::DICOM_TAG_ROWS)))
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-        }
-        else
-        {
-          SetSize(width, height);
-        }
-      }
-
-      
-      void SetSourceImage(Orthanc::ImageAccessor* image)   // Takes ownership
-      {
-        std::auto_ptr<Orthanc::ImageAccessor> raii(image);
-        
-        if (image == NULL)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-        }
-
-        SetSize(image->GetWidth(), image->GetHeight());
-        
-        source_ = raii;
-        ApplyConverter();
-      }
-
-      
-      virtual void Render(Orthanc::ImageAccessor& buffer,
-                          const Matrix& viewTransform,
-                          ImageInterpolation interpolation) const
-      {
-        if (converted_.get() != NULL)
-        {
-          if (converted_->GetFormat() != Orthanc::PixelFormat_Float32)
-          {
-            throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-          }
-
-          unsigned int cropX, cropY, cropWidth, cropHeight;
-          GetCrop(cropX, cropY, cropWidth, cropHeight);
-
-          Matrix m = LinearAlgebra::Product(viewTransform,
-                                            GetTransform(),
-                                            CreateOffsetMatrix(cropX, cropY));
-
-          Orthanc::ImageAccessor cropped;
-          converted_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight);
-        
-          ApplyProjectiveTransform(buffer, cropped, m, interpolation, false);
-        }
-      }
-
-
-      virtual bool GetDefaultWindowing(float& center,
-                                       float& width) const
-      {
-        if (converter_.get() != NULL &&
-            converter_->HasDefaultWindow())
-        {
-          center = static_cast<float>(converter_->GetDefaultWindowCenter());
-          width = static_cast<float>(converter_->GetDefaultWindowWidth());
-          return true;
-        }
-        else
-        {
-          return false;
-        }
-      }
-
-
-      virtual bool GetRange(float& minValue,
-                            float& maxValue) const
-      {
-        if (converted_.get() != NULL)
-        {
-          if (converted_->GetFormat() != Orthanc::PixelFormat_Float32)
-          {
-            throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-          }
-
-          Orthanc::ImageProcessing::GetMinMaxFloatValue(minValue, maxValue, *converted_);
-          return true;
-        }
-        else
-        {
-          return false;
-        }
-      }
-    };
-
-
-
-
-    typedef std::map<size_t, Layer*>  Layers;
-        
-    OrthancApiClient&  orthanc_;
-    size_t             countLayers_;
-    bool               hasWindowing_;
-    float              windowingCenter_;
-    float              windowingWidth_;
-    Layers             layers_;
-
-
-    Layer& RegisterLayer(Layer* layer)
-    {
-      if (layer == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-      }
-
-      std::auto_ptr<Layer> raii(layer);
-      
-      size_t index = countLayers_++;
-      raii->SetIndex(index);
-      layers_[index] = raii.release();
-
-      EmitMessage(GeometryChangedMessage(*this));
-      EmitMessage(ContentChangedMessage(*this));
-
-      return *layer;
-    }
-    
+    virtual void RedoInternal(RadiographyScene::Layer& layer) const = 0;
 
   public:
-    RadiologyScene(MessageBroker& broker,
-                   OrthancApiClient& orthanc) :
-      IObserver(broker),
-      IObservable(broker),
-      orthanc_(orthanc),
-      countLayers_(0),
-      hasWindowing_(false),
-      windowingCenter_(0),  // Dummy initialization
-      windowingWidth_(0)    // Dummy initialization
-    {
-    }
-
-
-    virtual ~RadiologyScene()
-    {
-      for (Layers::iterator it = layers_.begin(); it != layers_.end(); it++)
-      {
-        assert(it->second != NULL);
-        delete it->second;
-      }
-    }
-
-
-    bool GetWindowing(float& center,
-                      float& width) const
-    {
-      if (hasWindowing_)
-      {
-        center = windowingCenter_;
-        width = windowingWidth_;
-        return true;
-      }
-      else
-      {
-        return false;
-      }
-    }
-
-
-    void GetWindowingWithDefault(float& center,
-                                 float& width) const
-    {
-      if (!GetWindowing(center, width))
-      {
-        center = 128;
-        width = 256;
-      }
-    }
-
-
-    void SetWindowing(float center,
-                      float width)
-
-    {
-      hasWindowing_ = true;
-      windowingCenter_ = center;
-      windowingWidth_ = width;
-    }
-
-
-    Layer& LoadText(const Orthanc::Font& font,
-                    const std::string& utf8)
-    {
-      std::auto_ptr<AlphaLayer>  alpha(new AlphaLayer(*this));
-      alpha->LoadText(font, utf8);
-
-      return RegisterLayer(alpha.release());
-    }
-
-    
-    Layer& LoadTestBlock(unsigned int width,
-                         unsigned int height)
-    {
-      std::auto_ptr<Orthanc::Image>  block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false));
-
-      for (unsigned int padding = 0;
-           (width > 2 * padding) && (height > 2 * padding);
-           padding++)
-      {
-        uint8_t color;
-        if (255 > 10 * padding)
-        {
-          color = 255 - 10 * padding;
-        }
-        else
-        {
-          color = 0;
-        }
-
-        Orthanc::ImageAccessor region;
-        block->GetRegion(region, padding, padding, width - 2 * padding, height - 2 * padding);
-        Orthanc::ImageProcessing::Set(region, color);
-      }
-
-      std::auto_ptr<AlphaLayer>  alpha(new AlphaLayer(*this));
-      alpha->SetAlpha(block.release());
-
-      return RegisterLayer(alpha.release());
-    }
-
-    
-    Layer& LoadDicomFrame(const std::string& instance,
-                          unsigned int frame,
-                          bool httpCompression)
-    {
-      Layer& layer = RegisterLayer(new DicomLayer);
-
-      {
-        IWebService::Headers headers;
-        std::string uri = "/instances/" + instance + "/tags";
-        orthanc_.GetBinaryAsync(uri, headers,
-                                new Callable<RadiologyScene, OrthancApiClient::BinaryResponseReadyMessage>
-                                (*this, &RadiologyScene::OnTagsReceived), NULL,
-                                new Orthanc::SingleValueObject<size_t>(layer.GetIndex()));
-      }
-
-      {
-        IWebService::Headers headers;
-        headers["Accept"] = "image/x-portable-arbitrarymap";
-
-        if (httpCompression)
-        {
-          headers["Accept-Encoding"] = "gzip";
-        }
-        
-        std::string uri = "/instances/" + instance + "/frames/" + boost::lexical_cast<std::string>(frame) + "/image-uint16";
-        orthanc_.GetBinaryAsync(uri, headers,
-                                new Callable<RadiologyScene, OrthancApiClient::BinaryResponseReadyMessage>
-                                (*this, &RadiologyScene::OnFrameReceived), NULL,
-                                new Orthanc::SingleValueObject<size_t>(layer.GetIndex()));
-      }
-
-      return layer;
-    }
-
-    
-    void OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message)
-    {
-      size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>(message.GetPayload()).GetValue();
-
-      LOG(INFO) << "JSON received: " << message.GetUri().c_str()
-                << " (" << message.GetAnswerSize() << " bytes) for layer " << index;
-      
-      Layers::iterator layer = layers_.find(index);
-      if (layer != layers_.end())
-      {
-        assert(layer->second != NULL);
-        
-        OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize());
-        dynamic_cast<DicomLayer*>(layer->second)->SetDicomTags(dicom);
-
-        float c, w;
-        if (!hasWindowing_ &&
-            layer->second->GetDefaultWindowing(c, w))
-        {
-          hasWindowing_ = true;
-          windowingCenter_ = c;
-          windowingWidth_ = w;
-        }
-
-        EmitMessage(GeometryChangedMessage(*this));
-      }
-    }
-    
-
-    void OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message)
-    {
-      size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>(message.GetPayload()).GetValue();
-      
-      LOG(INFO) << "DICOM frame received: " << message.GetUri().c_str()
-                << " (" << message.GetAnswerSize() << " bytes) for layer " << index;
-      
-      Layers::iterator layer = layers_.find(index);
-      if (layer != layers_.end())
-      {
-        assert(layer->second != NULL);
-
-        std::string content;
-        if (message.GetAnswerSize() > 0)
-        {
-          content.assign(reinterpret_cast<const char*>(message.GetAnswer()), message.GetAnswerSize());
-        }
-        
-        std::auto_ptr<Orthanc::PamReader> reader(new Orthanc::PamReader);
-        reader->ReadFromMemory(content);
-        dynamic_cast<DicomLayer*>(layer->second)->SetSourceImage(reader.release());
-
-        EmitMessage(ContentChangedMessage(*this));
-      }
-    }
-
-
-    Extent2D GetSceneExtent() const
-    {
-      Extent2D extent;
-
-      for (Layers::const_iterator it = layers_.begin();
-           it != layers_.end(); ++it)
-      {
-        assert(it->second != NULL);
-        extent.Union(it->second->GetExtent());
-      }
-
-      return extent;
-    }
-    
-
-    void Render(Orthanc::ImageAccessor& buffer,
-                const Matrix& viewTransform,
-                ImageInterpolation interpolation) const
-    {
-      Orthanc::ImageProcessing::Set(buffer, 0);
-
-      // Render layers in the background-to-foreground order
-      for (size_t index = 0; index < countLayers_; index++)
-      {
-        Layers::const_iterator it = layers_.find(index);
-        if (it != layers_.end())
-        {
-          assert(it->second != NULL);
-          it->second->Render(buffer, viewTransform, interpolation);
-        }
-      }
-    }
-
-
-    bool LookupLayer(size_t& index /* out */,
-                     double x,
-                     double y) const
-    {
-      // Render layers in the foreground-to-background order
-      for (size_t i = countLayers_; i > 0; i--)
-      {
-        index = i - 1;
-        Layers::const_iterator it = layers_.find(index);
-        if (it != layers_.end())
-        {
-          assert(it->second != NULL);
-          if (it->second->Contains(x, y))
-          {
-            return true;
-          }
-        }
-      }
-
-      return false;
-    }
-
-    
-    void DrawBorder(CairoContext& context,
-                    unsigned int layer,
-                    double zoom)
-    {
-      Layers::const_iterator found = layers_.find(layer);
-        
-      if (found != layers_.end())
-      {
-        context.SetSourceColor(255, 0, 0);
-        found->second->DrawBorders(context, zoom);
-      }
-    }
-
-
-    void GetRange(float& minValue,
-                  float& maxValue) const
-    {
-      bool first = true;
-      
-      for (Layers::const_iterator it = layers_.begin();
-           it != layers_.end(); it++)
-      {
-        assert(it->second != NULL);
-
-        float a, b;
-        if (it->second->GetRange(a, b))
-        {
-          if (first)
-          {
-            minValue = a;
-            maxValue = b;
-            first = false;
-          }
-          else
-          {
-            minValue = std::min(a, minValue);
-            maxValue = std::max(b, maxValue);
-          }
-        }
-      }
-
-      if (first)
-      {
-        minValue = 0;
-        maxValue = 0;
-      }
-    }
-
-
-    // Export using PAM is faster than using PNG, but requires Orthanc
-    // core >= 1.4.3
-    void Export(const Orthanc::DicomMap& dicom,
-                double pixelSpacingX,
-                double pixelSpacingY,
-                bool invert,
-                ImageInterpolation interpolation,
-                bool usePam)
-    {
-      if (pixelSpacingX <= 0 ||
-          pixelSpacingY <= 0)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-      }
-      
-      LOG(INFO) << "Exporting DICOM";
-
-      Extent2D extent = GetSceneExtent();
-
-      int w = std::ceil(extent.GetWidth() / pixelSpacingX);
-      int h = std::ceil(extent.GetHeight() / pixelSpacingY);
-
-      if (w < 0 || h < 0)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-
-      Orthanc::Image layers(Orthanc::PixelFormat_Float32,
-                            static_cast<unsigned int>(w),
-                            static_cast<unsigned int>(h), false);
-
-      Matrix view = LinearAlgebra::Product(
-        CreateScalingMatrix(1.0 / pixelSpacingX, 1.0 / pixelSpacingY),
-        CreateOffsetMatrix(-extent.GetX1(), -extent.GetY1()));
-      
-      Render(layers, view, interpolation);
-
-      Orthanc::Image rendered(Orthanc::PixelFormat_Grayscale16,
-                              layers.GetWidth(), layers.GetHeight(), false);
-      Orthanc::ImageProcessing::Convert(rendered, layers);
-
-      std::string base64;
-
-      {
-        std::string content;
-
-        if (usePam)
-        {
-          Orthanc::PamWriter writer;
-          writer.WriteToMemory(content, rendered);
-        }
-        else
-        {
-          Orthanc::PngWriter writer;
-          writer.WriteToMemory(content, rendered);
-        }
-
-        Orthanc::Toolbox::EncodeBase64(base64, content);
-      }
-
-      std::set<Orthanc::DicomTag> tags;
-      dicom.GetTags(tags);
-
-      Json::Value json = Json::objectValue;
-      json["Tags"] = Json::objectValue;
-           
-      for (std::set<Orthanc::DicomTag>::const_iterator
-             tag = tags.begin(); tag != tags.end(); ++tag)
-      {
-        const Orthanc::DicomValue& value = dicom.GetValue(*tag);
-        if (!value.IsNull() &&
-            !value.IsBinary())
-        {
-          json["Tags"][tag->Format()] = value.GetContent();
-        }
-      }
-
-      json["Tags"][Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION.Format()] =
-        (invert ? "MONOCHROME1" : "MONOCHROME2");
-
-      // WARNING: The order of PixelSpacing is Y/X. We use "%0.8f" to
-      // avoid floating-point numbers to grow over 16 characters,
-      // which would be invalid according to DICOM standard
-      // ("dciodvfy" would complain).
-      char buf[32];
-      sprintf(buf, "%0.8f\\%0.8f", pixelSpacingY, pixelSpacingX);
-      
-      json["Tags"][Orthanc::DICOM_TAG_PIXEL_SPACING.Format()] = buf;
-
-      float center, width;
-      if (GetWindowing(center, width))
-      {
-        json["Tags"][Orthanc::DICOM_TAG_WINDOW_CENTER.Format()] =
-          boost::lexical_cast<std::string>(boost::math::iround(center));
-
-        json["Tags"][Orthanc::DICOM_TAG_WINDOW_WIDTH.Format()] =
-          boost::lexical_cast<std::string>(boost::math::iround(width));
-      }
-
-      // This is Data URI scheme: https://en.wikipedia.org/wiki/Data_URI_scheme
-      json["Content"] = ("data:" +
-                         std::string(usePam ? Orthanc::MIME_PAM : Orthanc::MIME_PNG) +
-                         ";base64," + base64);
-
-      orthanc_.PostJsonAsyncExpectJson(
-        "/tools/create-dicom", json,
-        new Callable<RadiologyScene, OrthancApiClient::JsonResponseReadyMessage>
-        (*this, &RadiologyScene::OnDicomExported),
-        NULL, NULL);
-    }
-
-
-    void OnDicomExported(const OrthancApiClient::JsonResponseReadyMessage& message)
-    {
-      LOG(INFO) << "DICOM export was successful:"
-                << message.GetJson().toStyledString();
-    }
-  };
-
-
-  class UndoRedoStack : public boost::noncopyable
-  {
-  public:
-    class ICommand : public boost::noncopyable
-    {
-    public:
-      virtual ~ICommand()
-      {
-      }
-      
-      virtual void Undo() const = 0;
-      
-      virtual void Redo() const = 0;
-    };
-
-  private:
-    typedef std::list<ICommand*>  Stack;
-
-    Stack            stack_;
-    Stack::iterator  current_;
-
-    void Clear(Stack::iterator from)
-    {
-      for (Stack::iterator it = from; it != stack_.end(); ++it)
-      {
-        assert(*it != NULL);
-        delete *it;
-      }
-
-      stack_.erase(from, stack_.end());
-    }
-
-  public:
-    UndoRedoStack() :
-      current_(stack_.end())
-    {
-    }
-    
-    ~UndoRedoStack()
-    {
-      Clear(stack_.begin());
-    }
-
-    void Add(ICommand* command)
-    {
-      if (command == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-      }
-      
-      Clear(current_);
-
-      stack_.push_back(command);
-      current_ = stack_.end();
-    }
-
-    void Undo()
-    {
-      if (current_ != stack_.begin())
-      {
-        --current_;
-        
-        assert(*current_ != NULL);
-        (*current_)->Undo();
-      }
-    }
-
-    void Redo()
-    {
-      if (current_ != stack_.end())
-      {
-        assert(*current_ != NULL);
-        (*current_)->Redo();
-
-        ++current_;
-      }
-    }
-  };
-
-
-  class RadiologyLayerCommand : public UndoRedoStack::ICommand
-  {
-  private:
-    RadiologyScene&  scene_;
-    size_t           layer_;
-
-  protected:
-    virtual void UndoInternal(RadiologyScene::Layer& layer) const = 0;
-
-    virtual void RedoInternal(RadiologyScene::Layer& layer) const = 0;
-
-  public:
-    RadiologyLayerCommand(RadiologyScene& scene,
-                          size_t layer) :
+    RadiographyLayerCommand(RadiographyScene& scene,
+                            size_t layer) :
       scene_(scene),
       layer_(layer)
     {
     }
 
-    RadiologyLayerCommand(const RadiologyScene::LayerAccessor& accessor) :
+    RadiographyLayerCommand(const RadiographyScene::LayerAccessor& accessor) :
       scene_(accessor.GetScene()),
       layer_(accessor.GetIndex())
     {
@@ -1530,7 +78,7 @@
 
     virtual void Undo() const
     {
-      RadiologyScene::LayerAccessor accessor(scene_, layer_);
+      RadiographyScene::LayerAccessor accessor(scene_, layer_);
 
       if (accessor.IsValid())
       {
@@ -1540,7 +88,7 @@
 
     virtual void Redo() const
     {
-      RadiologyScene::LayerAccessor accessor(scene_, layer_);
+      RadiographyScene::LayerAccessor accessor(scene_, layer_);
 
       if (accessor.IsValid())
       {
@@ -1550,11 +98,11 @@
   };
 
 
-  class RadiologyLayerRotateTracker : public IWorldSceneMouseTracker
+  class RadiographyLayerRotateTracker : public IWorldSceneMouseTracker
   {
   private:
     UndoRedoStack&                 undoRedoStack_;
-    RadiologyScene::LayerAccessor  accessor_;
+    RadiographyScene::LayerAccessor  accessor_;
     double                         centerX_;
     double                         centerY_;
     double                         originalAngle_;
@@ -1583,7 +131,7 @@
     }
 
 
-    class UndoRedoCommand : public RadiologyLayerCommand
+    class UndoRedoCommand : public RadiographyLayerCommand
     {
     private:
       double  sourceAngle_;
@@ -1595,21 +143,21 @@
       }
       
     protected:
-      virtual void UndoInternal(RadiologyScene::Layer& layer) const
+      virtual void UndoInternal(RadiographyScene::Layer& layer) const
       {
         LOG(INFO) << "Undo - Set angle to " << ToDegrees(sourceAngle_) << " degrees";
         layer.SetAngle(sourceAngle_);
       }
 
-      virtual void RedoInternal(RadiologyScene::Layer& layer) const
+      virtual void RedoInternal(RadiographyScene::Layer& layer) const
       {
         LOG(INFO) << "Redo - Set angle to " << ToDegrees(sourceAngle_) << " degrees";
         layer.SetAngle(targetAngle_);
       }
 
     public:
-      UndoRedoCommand(const RadiologyLayerRotateTracker& tracker) :
-        RadiologyLayerCommand(tracker.accessor_),
+      UndoRedoCommand(const RadiographyLayerRotateTracker& tracker) :
+        RadiographyLayerCommand(tracker.accessor_),
         sourceAngle_(tracker.originalAngle_),
         targetAngle_(tracker.accessor_.GetLayer().GetAngle())
       {
@@ -1618,13 +166,13 @@
 
       
   public:
-    RadiologyLayerRotateTracker(UndoRedoStack& undoRedoStack,
-                                RadiologyScene& scene,
-                                const ViewportGeometry& view,
-                                size_t layer,
-                                double x,
-                                double y,
-                                bool roundAngles) :
+    RadiographyLayerRotateTracker(UndoRedoStack& undoRedoStack,
+                                  RadiographyScene& scene,
+                                  const ViewportGeometry& view,
+                                  size_t layer,
+                                  double x,
+                                  double y,
+                                  bool roundAngles) :
       undoRedoStack_(undoRedoStack),
       accessor_(scene, layer),
       roundAngles_(roundAngles)
@@ -1688,18 +236,18 @@
   };
     
     
-  class RadiologyLayerMoveTracker : public IWorldSceneMouseTracker
+  class RadiographyLayerMoveTracker : public IWorldSceneMouseTracker
   {
   private:
-    UndoRedoStack&                 undoRedoStack_;
-    RadiologyScene::LayerAccessor  accessor_;
-    double                         clickX_;
-    double                         clickY_;
-    double                         panX_;
-    double                         panY_;
-    bool                           oneAxis_;
+    UndoRedoStack&                   undoRedoStack_;
+    RadiographyScene::LayerAccessor  accessor_;
+    double                           clickX_;
+    double                           clickY_;
+    double                           panX_;
+    double                           panY_;
+    bool                             oneAxis_;
 
-    class UndoRedoCommand : public RadiologyLayerCommand
+    class UndoRedoCommand : public RadiographyLayerCommand
     {
     private:
       double  sourceX_;
@@ -1708,19 +256,19 @@
       double  targetY_;
 
     protected:
-      virtual void UndoInternal(RadiologyScene::Layer& layer) const
+      virtual void UndoInternal(RadiographyScene::Layer& layer) const
       {
         layer.SetPan(sourceX_, sourceY_);
       }
 
-      virtual void RedoInternal(RadiologyScene::Layer& layer) const
+      virtual void RedoInternal(RadiographyScene::Layer& layer) const
       {
         layer.SetPan(targetX_, targetY_);
       }
 
     public:
-      UndoRedoCommand(const RadiologyLayerMoveTracker& tracker) :
-        RadiologyLayerCommand(tracker.accessor_),
+      UndoRedoCommand(const RadiographyLayerMoveTracker& tracker) :
+        RadiographyLayerCommand(tracker.accessor_),
         sourceX_(tracker.panX_),
         sourceY_(tracker.panY_),
         targetX_(tracker.accessor_.GetLayer().GetPanX()),
@@ -1731,12 +279,12 @@
 
 
   public:
-    RadiologyLayerMoveTracker(UndoRedoStack& undoRedoStack,
-                              RadiologyScene& scene,
-                              size_t layer,
-                              double x,
-                              double y,
-                              bool oneAxis) :
+    RadiographyLayerMoveTracker(UndoRedoStack& undoRedoStack,
+                                RadiographyScene& scene,
+                                size_t layer,
+                                double x,
+                                double y,
+                                bool oneAxis) :
       undoRedoStack_(undoRedoStack),
       accessor_(scene, layer),
       clickX_(x),
@@ -1799,18 +347,18 @@
   };
 
 
-  class RadiologyLayerCropTracker : public IWorldSceneMouseTracker
+  class RadiographyLayerCropTracker : public IWorldSceneMouseTracker
   {
   private:
-    UndoRedoStack&                 undoRedoStack_;
-    RadiologyScene::LayerAccessor  accessor_;
-    RadiologyScene::Corner         corner_;
-    unsigned int                   cropX_;
-    unsigned int                   cropY_;
-    unsigned int                   cropWidth_;
-    unsigned int                   cropHeight_;
+    UndoRedoStack&                   undoRedoStack_;
+    RadiographyScene::LayerAccessor  accessor_;
+    RadiographyScene::Corner         corner_;
+    unsigned int                     cropX_;
+    unsigned int                     cropY_;
+    unsigned int                     cropWidth_;
+    unsigned int                     cropHeight_;
 
-    class UndoRedoCommand : public RadiologyLayerCommand
+    class UndoRedoCommand : public RadiographyLayerCommand
     {
     private:
       unsigned int  sourceCropX_;
@@ -1823,19 +371,19 @@
       unsigned int  targetCropHeight_;
 
     protected:
-      virtual void UndoInternal(RadiologyScene::Layer& layer) const
+      virtual void UndoInternal(RadiographyScene::Layer& layer) const
       {
         layer.SetCrop(sourceCropX_, sourceCropY_, sourceCropWidth_, sourceCropHeight_);
       }
 
-      virtual void RedoInternal(RadiologyScene::Layer& layer) const
+      virtual void RedoInternal(RadiographyScene::Layer& layer) const
       {
         layer.SetCrop(targetCropX_, targetCropY_, targetCropWidth_, targetCropHeight_);
       }
 
     public:
-      UndoRedoCommand(const RadiologyLayerCropTracker& tracker) :
-        RadiologyLayerCommand(tracker.accessor_),
+      UndoRedoCommand(const RadiographyLayerCropTracker& tracker) :
+        RadiographyLayerCommand(tracker.accessor_),
         sourceCropX_(tracker.cropX_),
         sourceCropY_(tracker.cropY_),
         sourceCropWidth_(tracker.cropWidth_),
@@ -1848,13 +396,13 @@
 
 
   public:
-    RadiologyLayerCropTracker(UndoRedoStack& undoRedoStack,
-                              RadiologyScene& scene,
-                              const ViewportGeometry& view,
-                              size_t layer,
-                              double x,
-                              double y,
-                              RadiologyScene::Corner corner) :
+    RadiographyLayerCropTracker(UndoRedoStack& undoRedoStack,
+                                RadiographyScene& scene,
+                                const ViewportGeometry& view,
+                                size_t layer,
+                                double x,
+                                double y,
+                                RadiographyScene::Corner corner) :
       undoRedoStack_(undoRedoStack),
       accessor_(scene, layer),
       corner_(corner)
@@ -1893,13 +441,13 @@
       {
         unsigned int x, y;
         
-        RadiologyScene::Layer& layer = accessor_.GetLayer();
+        RadiographyScene::Layer& layer = accessor_.GetLayer();
         if (layer.GetPixel(x, y, sceneX, sceneY))
         {
           unsigned int targetX, targetWidth;
 
-          if (corner_ == RadiologyScene::Corner_TopLeft ||
-              corner_ == RadiologyScene::Corner_BottomLeft)
+          if (corner_ == RadiographyScene::Corner_TopLeft ||
+              corner_ == RadiographyScene::Corner_BottomLeft)
           {
             targetX = std::min(x, cropX_ + cropWidth_);
             targetWidth = cropX_ + cropWidth_ - targetX;
@@ -1912,8 +460,8 @@
 
           unsigned int targetY, targetHeight;
 
-          if (corner_ == RadiologyScene::Corner_TopLeft ||
-              corner_ == RadiologyScene::Corner_TopRight)
+          if (corner_ == RadiographyScene::Corner_TopLeft ||
+              corner_ == RadiographyScene::Corner_TopRight)
           {
             targetY = std::min(y, cropY_ + cropHeight_);
             targetHeight = cropY_ + cropHeight_ - targetY;
@@ -1931,20 +479,20 @@
   };
     
     
-  class RadiologyLayerResizeTracker : public IWorldSceneMouseTracker
+  class RadiographyLayerResizeTracker : public IWorldSceneMouseTracker
   {
   private:
-    UndoRedoStack&                 undoRedoStack_;
-    RadiologyScene::LayerAccessor  accessor_;
-    bool                           roundScaling_;
-    double                         originalSpacingX_;
-    double                         originalSpacingY_;
-    double                         originalPanX_;
-    double                         originalPanY_;
-    RadiologyScene::Corner         oppositeCorner_;
-    double                         oppositeX_;
-    double                         oppositeY_;
-    double                         baseScaling_;
+    UndoRedoStack&                   undoRedoStack_;
+    RadiographyScene::LayerAccessor  accessor_;
+    bool                             roundScaling_;
+    double                           originalSpacingX_;
+    double                           originalSpacingY_;
+    double                           originalPanX_;
+    double                           originalPanY_;
+    RadiographyScene::Corner         oppositeCorner_;
+    double                           oppositeX_;
+    double                           oppositeY_;
+    double                           baseScaling_;
 
     static double ComputeDistance(double x1,
                                   double y1,
@@ -1956,7 +504,7 @@
       return sqrt(dx * dx + dy * dy);
     }
       
-    class UndoRedoCommand : public RadiologyLayerCommand
+    class UndoRedoCommand : public RadiographyLayerCommand
     {
     private:
       double   sourceSpacingX_;
@@ -1969,21 +517,21 @@
       double   targetPanY_;
 
     protected:
-      virtual void UndoInternal(RadiologyScene::Layer& layer) const
+      virtual void UndoInternal(RadiographyScene::Layer& layer) const
       {
         layer.SetPixelSpacing(sourceSpacingX_, sourceSpacingY_);
         layer.SetPan(sourcePanX_, sourcePanY_);
       }
 
-      virtual void RedoInternal(RadiologyScene::Layer& layer) const
+      virtual void RedoInternal(RadiographyScene::Layer& layer) const
       {
         layer.SetPixelSpacing(targetSpacingX_, targetSpacingY_);
         layer.SetPan(targetPanX_, targetPanY_);
       }
 
     public:
-      UndoRedoCommand(const RadiologyLayerResizeTracker& tracker) :
-        RadiologyLayerCommand(tracker.accessor_),
+      UndoRedoCommand(const RadiographyLayerResizeTracker& tracker) :
+        RadiographyLayerCommand(tracker.accessor_),
         sourceSpacingX_(tracker.originalSpacingX_),
         sourceSpacingY_(tracker.originalSpacingY_),
         sourcePanX_(tracker.originalPanX_),
@@ -1998,13 +546,13 @@
 
 
   public:
-    RadiologyLayerResizeTracker(UndoRedoStack& undoRedoStack,
-                                RadiologyScene& scene,
-                                size_t layer,
-                                double x,
-                                double y,
-                                RadiologyScene::Corner corner,
-                                bool roundScaling) :
+    RadiographyLayerResizeTracker(UndoRedoStack& undoRedoStack,
+                                  RadiographyScene& scene,
+                                  size_t layer,
+                                  double x,
+                                  double y,
+                                  RadiographyScene::Corner corner,
+                                  bool roundScaling) :
       undoRedoStack_(undoRedoStack),
       accessor_(scene, layer),
       roundScaling_(roundScaling)
@@ -2019,20 +567,20 @@
 
         switch (corner)
         {
-          case RadiologyScene::Corner_TopLeft:
-            oppositeCorner_ = RadiologyScene::Corner_BottomRight;
+          case RadiographyScene::Corner_TopLeft:
+            oppositeCorner_ = RadiographyScene::Corner_BottomRight;
             break;
 
-          case RadiologyScene::Corner_TopRight:
-            oppositeCorner_ = RadiologyScene::Corner_BottomLeft;
+          case RadiographyScene::Corner_TopRight:
+            oppositeCorner_ = RadiographyScene::Corner_BottomLeft;
             break;
 
-          case RadiologyScene::Corner_BottomLeft:
-            oppositeCorner_ = RadiologyScene::Corner_TopRight;
+          case RadiographyScene::Corner_BottomLeft:
+            oppositeCorner_ = RadiographyScene::Corner_TopRight;
             break;
 
-          case RadiologyScene::Corner_BottomRight:
-            oppositeCorner_ = RadiologyScene::Corner_TopLeft;
+          case RadiographyScene::Corner_BottomRight:
+            oppositeCorner_ = RadiographyScene::Corner_TopLeft;
             break;
 
           default:
@@ -2091,7 +639,7 @@
           scaling = boost::math::round<double>((scaling / ROUND_SCALING) * ROUND_SCALING);
         }
           
-        RadiologyScene::Layer& layer = accessor_.GetLayer();
+        RadiographyScene::Layer& layer = accessor_.GetLayer();
         layer.SetPixelSpacing(scaling * originalSpacingX_,
                               scaling * originalSpacingY_);
 
@@ -2105,7 +653,7 @@
   };
 
 
-  class RadiologyWindowingTracker : public IWorldSceneMouseTracker
+  class RadiographyWindowingTracker : public IWorldSceneMouseTracker
   {   
   public:
     enum Action
@@ -2117,17 +665,17 @@
     };
     
   private:
-    UndoRedoStack&    undoRedoStack_;
-    RadiologyScene&   scene_;
-    int               clickX_;
-    int               clickY_;
-    Action            leftAction_;
-    Action            rightAction_;
-    Action            upAction_;
-    Action            downAction_;
-    float             strength_;
-    float             sourceCenter_;
-    float             sourceWidth_;
+    UndoRedoStack&      undoRedoStack_;
+    RadiographyScene&   scene_;
+    int                 clickX_;
+    int                 clickY_;
+    Action              leftAction_;
+    Action              rightAction_;
+    Action              upAction_;
+    Action              downAction_;
+    float               strength_;
+    float               sourceCenter_;
+    float               sourceWidth_;
 
     static void ComputeAxisEffect(int& deltaCenter,
                                   int& deltaWidth,
@@ -2189,14 +737,14 @@
     class UndoRedoCommand : public UndoRedoStack::ICommand
     {
     private:
-      RadiologyScene&  scene_;
-      float            sourceCenter_;
-      float            sourceWidth_;
-      float            targetCenter_;
-      float            targetWidth_;
+      RadiographyScene&  scene_;
+      float              sourceCenter_;
+      float              sourceWidth_;
+      float              targetCenter_;
+      float              targetWidth_;
 
     public:
-      UndoRedoCommand(const RadiologyWindowingTracker& tracker) :
+      UndoRedoCommand(const RadiographyWindowingTracker& tracker) :
         scene_(tracker.scene_),
         sourceCenter_(tracker.sourceCenter_),
         sourceWidth_(tracker.sourceWidth_)
@@ -2217,14 +765,14 @@
 
 
   public:
-    RadiologyWindowingTracker(UndoRedoStack& undoRedoStack,
-                              RadiologyScene& scene,
-                              int x,
-                              int y,
-                              Action leftAction,
-                              Action rightAction,
-                              Action upAction,
-                              Action downAction) :
+    RadiographyWindowingTracker(UndoRedoStack& undoRedoStack,
+                                RadiographyScene& scene,
+                                int x,
+                                int y,
+                                Action leftAction,
+                                Action rightAction,
+                                Action upAction,
+                                Action downAction) :
       undoRedoStack_(undoRedoStack),
       scene_(scene),
       clickX_(x),
@@ -2301,12 +849,12 @@
   };
 
 
-  class RadiologyWidget :
+  class RadiographyWidget :
     public WorldSceneWidget,
     public IObserver
   {
   private:
-    RadiologyScene&                scene_;
+    RadiographyScene&              scene_;
     std::auto_ptr<Orthanc::Image>  floatBuffer_;
     std::auto_ptr<CairoSurface>    cairoBuffer_;
     bool                           invert_;
@@ -2428,9 +976,9 @@
     }
 
   public:
-    RadiologyWidget(MessageBroker& broker,
-                    RadiologyScene& scene,
-                    const std::string& name) :
+    RadiographyWidget(MessageBroker& broker,
+                      RadiographyScene& scene,
+                      const std::string& name) :
       WorldSceneWidget(name),
       IObserver(broker),
       scene_(scene),
@@ -2440,15 +988,15 @@
       selectedLayer_(0)    // Dummy initialization
     {
       scene.RegisterObserverCallback(
-        new Callable<RadiologyWidget, RadiologyScene::GeometryChangedMessage>
-        (*this, &RadiologyWidget::OnGeometryChanged));
+        new Callable<RadiographyWidget, RadiographyScene::GeometryChangedMessage>
+        (*this, &RadiographyWidget::OnGeometryChanged));
 
       scene.RegisterObserverCallback(
-        new Callable<RadiologyWidget, RadiologyScene::ContentChangedMessage>
-        (*this, &RadiologyWidget::OnContentChanged));
+        new Callable<RadiographyWidget, RadiographyScene::ContentChangedMessage>
+        (*this, &RadiographyWidget::OnContentChanged));
     }
 
-    RadiologyScene& GetScene() const
+    RadiographyScene& GetScene() const
     {
       return scene_;
     }
@@ -2477,13 +1025,13 @@
       }
     }
 
-    void OnGeometryChanged(const RadiologyScene::GeometryChangedMessage& message)
+    void OnGeometryChanged(const RadiographyScene::GeometryChangedMessage& message)
     {
       LOG(INFO) << "Geometry has changed";
       FitContent();
     }
 
-    void OnContentChanged(const RadiologyScene::ContentChangedMessage& message)
+    void OnContentChanged(const RadiographyScene::ContentChangedMessage& message)
     {
       LOG(INFO) << "Content has changed";
       NotifyContentChanged();
@@ -2527,7 +1075,7 @@
   
   namespace Samples
   {
-    class RadiologyEditorInteractor :
+    class RadiographyEditorInteractor :
       public IWorldSceneInteractor,
       public IObserver
     {
@@ -2553,7 +1101,7 @@
     
          
     public:
-      RadiologyEditorInteractor(MessageBroker& broker) :
+      RadiographyEditorInteractor(MessageBroker& broker) :
         IObserver(broker),
         tool_(Tool_Move)
       {
@@ -2569,7 +1117,7 @@
                                                           double y,
                                                           IStatusBar* statusBar)
       {
-        RadiologyWidget& widget = dynamic_cast<RadiologyWidget&>(worldWidget);
+        RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget);
 
         if (button == MouseButton_Left)
         {
@@ -2577,12 +1125,13 @@
         
           if (tool_ == Tool_Windowing)
           {
-            return new RadiologyWindowingTracker(undoRedoStack_, widget.GetScene(),
-                                                 viewportX, viewportY,
-                                                 RadiologyWindowingTracker::Action_DecreaseWidth,
-                                                 RadiologyWindowingTracker::Action_IncreaseWidth,
-                                                 RadiologyWindowingTracker::Action_DecreaseCenter,
-                                                 RadiologyWindowingTracker::Action_IncreaseCenter);
+            return new RadiographyWindowingTracker(
+              undoRedoStack_, widget.GetScene(),
+              viewportX, viewportY,
+              RadiographyWindowingTracker::Action_DecreaseWidth,
+              RadiographyWindowingTracker::Action_IncreaseWidth,
+              RadiographyWindowingTracker::Action_DecreaseCenter,
+              RadiographyWindowingTracker::Action_IncreaseCenter);
           }
           else if (!widget.LookupSelectedLayer(selected))
           {
@@ -2598,18 +1147,20 @@
           else if (tool_ == Tool_Crop ||
                    tool_ == Tool_Resize)
           {
-            RadiologyScene::LayerAccessor accessor(widget.GetScene(), selected);
-            RadiologyScene::Corner corner;
+            RadiographyScene::LayerAccessor accessor(widget.GetScene(), selected);
+            RadiographyScene::Corner corner;
             if (accessor.GetLayer().LookupCorner(corner, x, y, view.GetZoom(), GetHandleSize()))
             {
               switch (tool_)
               {
                 case Tool_Crop:
-                  return new RadiologyLayerCropTracker(undoRedoStack_, widget.GetScene(), view, selected, x, y, corner);
+                  return new RadiographyLayerCropTracker
+                    (undoRedoStack_, widget.GetScene(), view, selected, x, y, corner);
 
                 case Tool_Resize:
-                  return new RadiologyLayerResizeTracker(undoRedoStack_, widget.GetScene(), selected, x, y, corner,
-                                                         (modifiers & KeyboardModifiers_Shift));
+                  return new RadiographyLayerResizeTracker
+                    (undoRedoStack_, widget.GetScene(), selected, x, y, corner,
+                     (modifiers & KeyboardModifiers_Shift));
 
                 default:
                   throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
@@ -2642,12 +1193,14 @@
                 switch (tool_)
                 {
                   case Tool_Move:
-                    return new RadiologyLayerMoveTracker(undoRedoStack_, widget.GetScene(), layer, x, y,
-                                                         (modifiers & KeyboardModifiers_Shift));
+                    return new RadiographyLayerMoveTracker
+                      (undoRedoStack_, widget.GetScene(), layer, x, y,
+                       (modifiers & KeyboardModifiers_Shift));
 
                   case Tool_Rotate:
-                    return new RadiologyLayerRotateTracker(undoRedoStack_, widget.GetScene(), view, layer, x, y,
-                                                           (modifiers & KeyboardModifiers_Shift));
+                    return new RadiographyLayerRotateTracker
+                      (undoRedoStack_, widget.GetScene(), view, layer, x, y,
+                       (modifiers & KeyboardModifiers_Shift));
                 
                   default:
                     break;
@@ -2681,7 +1234,7 @@
                              double y,
                              IStatusBar* statusBar)
       {
-        RadiologyWidget& widget = dynamic_cast<RadiologyWidget&>(worldWidget);
+        RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget);
 
 #if 0
         if (statusBar != NULL)
@@ -2698,9 +1251,9 @@
             (tool_ == Tool_Crop ||
              tool_ == Tool_Resize))
         {
-          RadiologyScene::LayerAccessor accessor(widget.GetScene(), selected);
+          RadiographyScene::LayerAccessor accessor(widget.GetScene(), selected);
         
-          RadiologyScene::Corner corner;
+          RadiographyScene::Corner corner;
           if (accessor.GetLayer().LookupCorner(corner, x, y, view.GetZoom(), GetHandleSize()))
           {
             accessor.GetLayer().GetCorner(x, y, corner);
@@ -2733,7 +1286,7 @@
                               KeyboardModifiers modifiers,
                               IStatusBar* statusBar)
       {
-        RadiologyWidget& widget = dynamic_cast<RadiologyWidget&>(worldWidget);
+        RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget);
 
         switch (keyChar)
         {
@@ -2767,8 +1320,8 @@
             tags.SetValue(Orthanc::DICOM_TAG_STUDY_ID, "STUDY", false);
             tags.SetValue(Orthanc::DICOM_TAG_VIEW_POSITION, "", false);
 
-            widget.GetScene().Export(tags, 0.1, 0.1, widget.IsInverted(),
-                                     widget.GetInterpolation(), EXPORT_USING_PAM);
+            widget.GetScene().ExportDicom(tags, 0.1, 0.1, widget.IsInverted(),
+                                          widget.GetInterpolation(), EXPORT_USING_PAM);
             break;
           }
 
@@ -2843,8 +1396,8 @@
     {
     private:
       std::auto_ptr<OrthancApiClient>  orthancApiClient_;
-      std::auto_ptr<RadiologyScene>    scene_;
-      RadiologyEditorInteractor        interactor_;
+      std::auto_ptr<RadiographyScene>  scene_;
+      RadiographyEditorInteractor      interactor_;
 
     public:
       SingleFrameEditorApplication(MessageBroker& broker) :
@@ -2907,25 +1460,25 @@
         Orthanc::FontRegistry fonts;
         fonts.AddFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16);
         
-        scene_.reset(new RadiologyScene(GetBroker(), *orthancApiClient_));
+        scene_.reset(new RadiographyScene(GetBroker(), *orthancApiClient_));
         scene_->LoadDicomFrame(instance, frame, false); //.SetPan(200, 0);
         //scene_->LoadDicomFrame("61f3143e-96f34791-ad6bbb8d-62559e75-45943e1b", 0, false);
 
         {
-          RadiologyScene::Layer& layer = scene_->LoadText(fonts.GetFont(0), "Hello\nworld");
-          //dynamic_cast<RadiologyScene::Layer&>(layer).SetForegroundValue(256);
-          dynamic_cast<RadiologyScene::Layer&>(layer).SetResizeable(true);
+          RadiographyScene::Layer& layer = scene_->LoadText(fonts.GetFont(0), "Hello\nworld");
+          //dynamic_cast<RadiographyScene::Layer&>(layer).SetForegroundValue(256);
+          dynamic_cast<RadiographyScene::Layer&>(layer).SetResizeable(true);
         }
         
         {
-          RadiologyScene::Layer& layer = scene_->LoadTestBlock(100, 50);
-          //dynamic_cast<RadiologyScene::Layer&>(layer).SetForegroundValue(256);
-          dynamic_cast<RadiologyScene::Layer&>(layer).SetResizeable(true);
-          dynamic_cast<RadiologyScene::Layer&>(layer).SetPan(0, 200);
+          RadiographyScene::Layer& layer = scene_->LoadTestBlock(100, 50);
+          //dynamic_cast<RadiographyScene::Layer&>(layer).SetForegroundValue(256);
+          dynamic_cast<RadiographyScene::Layer&>(layer).SetResizeable(true);
+          dynamic_cast<RadiographyScene::Layer&>(layer).SetPan(0, 200);
         }
         
         
-        mainWidget_ = new RadiologyWidget(GetBroker(), *scene_, "main-widget");
+        mainWidget_ = new RadiographyWidget(GetBroker(), *scene_, "main-widget");
         mainWidget_->SetTransmitMouseOver(true);
         mainWidget_->SetInteractor(interactor_);