view Framework/Radiography/RadiographyScene.h @ 408:6834c236b36d

reorganization
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 12 Nov 2018 14:52:10 +0100
parents
children 99c9b3238008
line wrap: on
line source

/**
 * Stone of Orthanc
 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
 * Department, University Hospital of Liege, Belgium
 * Copyright (C) 2017-2018 Osimis S.A., 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 "../Toolbox/Extent2D.h"
#include "../Toolbox/LinearAlgebra.h"
#include "../Toolbox/OrthancApiClient.h"
#include "../Viewport/CairoContext.h"


namespace OrthancStone
{
  class RadiographyScene :
    public IObserver,
    public IObservable
  {
  public:
    typedef OriginMessage<MessageType_Widget_GeometryChanged, RadiographyScene> GeometryChangedMessage;
    typedef OriginMessage<MessageType_Widget_ContentChanged, RadiographyScene> ContentChangedMessage;

    enum Corner
    {
      Corner_TopLeft,
      Corner_TopRight,
      Corner_BottomLeft,
      Corner_BottomRight
    };

    class Layer : public boost::noncopyable
    {
      friend class RadiographyScene;
      
    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:
      void UpdateTransform();
      
      void AddToExtent(Extent2D& extent,
                       double x,
                       double y) const;

      void GetCornerInternal(double& x,
                             double& y,
                             Corner corner,
                             unsigned int cropX,
                             unsigned int cropY,
                             unsigned int cropWidth,
                             unsigned int cropHeight) const;

      void SetIndex(size_t index)
      {
        index_ = index;
      }
      
      bool Contains(double x,
                    double y) const;
      
      void DrawBorders(CairoContext& context,
                       double zoom);

    public:
      Layer();

      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);

      void GetCrop(unsigned int& x,
                   unsigned int& y,
                   unsigned int& width,
                   unsigned int& height) const;

      void SetAngle(double angle);

      double GetAngle() const
      {
        return angle_;
      }

      void SetSize(unsigned int width,
                   unsigned int height);

      unsigned int GetWidth() const
      {
        return width_;
      }        

      unsigned int GetHeight() const
      {
        return height_;
      }       

      Extent2D GetExtent() const;

      bool GetPixel(unsigned int& imageX,
                    unsigned int& imageY,
                    double sceneX,
                    double sceneY) const;

      void SetPan(double x,
                  double y);

      void SetPixelSpacing(double x,
                           double y);

      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;

      void GetCorner(double& x /* out */,
                     double& y /* out */,
                     Corner corner) const;
      
      bool LookupCorner(Corner& corner /* out */,
                        double x,
                        double y,
                        double zoom,
                        double viewportDistance) const;

      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:
      RadiographyScene&  scene_;
      size_t             index_;
      Layer*             layer_;

    public:
      LayerAccessor(RadiographyScene& scene,
                    size_t index);

      LayerAccessor(RadiographyScene& scene,
                    double x,
                    double y);

      void Invalidate()
      {
        layer_ = NULL;
      }

      bool IsValid() const
      {
        return layer_ != NULL;
      }

      RadiographyScene& GetScene() const;

      size_t GetIndex() const;

      Layer& GetLayer() const;
    };


  private:
    class AlphaLayer;    
    class DicomLayer;

    typedef std::map<size_t, Layer*>  Layers;
        
    OrthancApiClient&  orthanc_;
    size_t             countLayers_;
    bool               hasWindowing_;
    float              windowingCenter_;
    float              windowingWidth_;
    Layers             layers_;

    Layer& RegisterLayer(Layer* layer);

    void OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message);

    void OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message);
    
    void OnDicomExported(const OrthancApiClient::JsonResponseReadyMessage& message);

  public:
    RadiographyScene(MessageBroker& broker,
                     OrthancApiClient& orthanc);
    
    virtual ~RadiographyScene();

    bool GetWindowing(float& center,
                      float& width) const;

    void GetWindowingWithDefault(float& center,
                                 float& width) const;

    void SetWindowing(float center,
                      float width);

    Layer& LoadText(const Orthanc::Font& font,
                    const std::string& utf8);
    
    Layer& LoadTestBlock(unsigned int width,
                         unsigned int height);
    
    Layer& LoadDicomFrame(const std::string& instance,
                          unsigned int frame,
                          bool httpCompression);

    Extent2D GetSceneExtent() const;

    void Render(Orthanc::ImageAccessor& buffer,
                const Matrix& viewTransform,
                ImageInterpolation interpolation) const;

    bool LookupLayer(size_t& index /* out */,
                     double x,
                     double y) const;
    
    void DrawBorder(CairoContext& context,
                    unsigned int layer,
                    double zoom);

    void GetRange(float& minValue,
                  float& maxValue) const;

    // Export using PAM is faster than using PNG, but requires Orthanc
    // core >= 1.4.3
    void ExportDicom(const Orthanc::DicomMap& dicom,
                     double pixelSpacingX,
                     double pixelSpacingY,
                     bool invert,
                     ImageInterpolation interpolation,
                     bool usePam);
  };
}