view Framework/Widgets/LayerWidget.cpp @ 69:1553b67b24e5 wasm

OrthancSynchronousWebService
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 22 May 2017 20:35:11 +0200
parents 298f375dcb68
children 30c768873d47
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 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 "LayerWidget.h"

#include "../../Resources/Orthanc/Core/Logging.h"

namespace OrthancStone
{
  class LayerWidget::Scene : public boost::noncopyable
  {
  private:
    SliceGeometry                 slice_;
    size_t                        countMissing_;
    std::vector<ILayerRenderer*>  renderers_;

    void DeleteLayer(size_t index)
    {
      if (index >= renderers_.size())
      {
        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
      }

      assert(countMissing_ <= renderers_.size());

      if (renderers_[index] != NULL)
      {
        assert(countMissing_ < renderers_.size());
        delete renderers_[index];
        renderers_[index] = NULL;
        countMissing_++;
      }
    }
      
  public:
    Scene(const SliceGeometry& slice,
          size_t countLayers) :
      slice_(slice),
      countMissing_(countLayers),
      renderers_(countLayers, NULL)
    {
    }

    ~Scene()
    {
      for (size_t i = 0; i < renderers_.size(); i++)
      {
        DeleteLayer(i);
      }
    }

    void SetLayer(size_t index,
                  ILayerRenderer* renderer)  // Takes ownership
    {
      if (renderer == NULL)
      {
        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
      }

      DeleteLayer(index);

      renderers_[index] = renderer;
      countMissing_--;
    }

    const SliceGeometry& GetSlice() const
    {
      return slice_;
    }

    bool IsComplete() const
    {
      return countMissing_ == 0;
    }

    bool IsSamePlane(const SliceGeometry& slice,
                     double sliceThickness)
    {
      return slice_.IsSamePlane(slice, sliceThickness);
    }

    bool RenderScene(CairoContext& context,
                     const ViewportGeometry& view)
    {
      bool fullQuality = true;

      for (size_t i = 0; i < renderers_.size(); i++)
      {
        if (renderers_[i] != NULL &&
            !renderers_[i]->RenderLayer(context, view))
        {
          return false;
        }

        if (renderers_[i] != NULL &&
            !renderers_[i]->IsFullQuality())
        {
          fullQuality = false;
        }
      }

      if (!fullQuality)
      {
        double x, y;
        view.MapDisplayToScene(x, y, static_cast<double>(view.GetDisplayWidth()) / 2.0, 10);

        cairo_t *cr = context.GetObject();
        cairo_translate(cr, x, y);
        cairo_arc(cr, 0, 0, 5.0 / view.GetZoom(), 0, 2 * M_PI);
        cairo_set_line_width(cr, 2.0 / view.GetZoom());
        cairo_set_source_rgb(cr, 1, 1, 1); 
        cairo_stroke_preserve(cr);
        cairo_set_source_rgb(cr, 1, 0, 0); 
        cairo_fill(cr);
      }

      return true;
    }

    void SetLayerStyle(size_t index,
                       const RenderStyle& style)
    {
      if (renderers_[index] != NULL)
      {
        renderers_[index]->SetLayerStyle(style);
      }
    }
  };

  
  bool LayerWidget::LookupLayer(size_t& index /* out */,
                                ILayerSource& layer) const
  {
    LayersIndex::const_iterator found = layersIndex_.find(&layer);

    if (found == layersIndex_.end())
    {
      return false;
    }
    else
    {
      index = found->second;
      assert(index < layers_.size() &&
             layers_[index] == &layer);
      return true;
    }
  }
    
        
  void LayerWidget::GetSceneExtent(double& x1,
                                   double& y1,
                                   double& x2,
                                   double& y2)
  {
    bool first = true;

    for (size_t i = 0; i < layers_.size(); i++)
    {
      double ax, ay, bx, by;

      assert(layers_[i] != NULL);
      if (layers_[i]->GetExtent(ax, ay, bx, by, slice_))
      {
        if (ax > bx)
        {
          std::swap(ax, bx);
        }

        if (ay > by)
        {
          std::swap(ay, by);
        }

        //LOG(INFO) << "Extent of layer " << i << ": (" << ax << "," << ay << ")->(" << bx << "," << by << ")";
        printf("Extent %d: (%f,%f) -> (%f,%f)\n", (int) i, ax, ay, bx, by);

        if (first)
        {
          x1 = ax;
          y1 = ay;
          x2 = bx;
          y2 = by;
          first = false;
        }
        else
        {
          x1 = std::min(x1, ax);
          y1 = std::min(y1, ay);
          x2 = std::max(x2, bx);
          y2 = std::max(y2, by);
        }
      }
    }

    if (first)
    {
      // Set a default extent of (-1,-1) -> (0,0)
      x1 = -1;
      y1 = -1;
      x2 = 1;
      y2 = 1;
    }

    // Ensure the extent is non-empty
    if (x1 >= x2)
    {
      double tmp = x1;
      x1 = tmp - 0.5;
      x2 = tmp + 0.5;
    }

    if (y1 >= y2)
    {
      double tmp = y1;
      y1 = tmp - 0.5;
      y2 = tmp + 0.5;
    }
  }

  
  bool LayerWidget::RenderScene(CairoContext& context,
                                const ViewportGeometry& view)
  {
    if (currentScene_.get() != NULL)
    {
      return currentScene_->RenderScene(context, view);
    }
    else
    {
      return true;
    }
  }

  
  void LayerWidget::ResetPendingScene()
  {
    pendingScene_.reset(new Scene(slice_, layers_.size()));
  }
  

  void LayerWidget::UpdateLayer(size_t index,
                                ILayerRenderer* renderer,
                                const SliceGeometry& slice)
  {
    printf("Updating layer %d\n", (int) index);
    
    std::auto_ptr<ILayerRenderer> tmp(renderer);

    if (renderer == NULL)
    {
      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
    }

    if (index >= layers_.size())
    {
      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
    }

    assert(layers_.size() == styles_.size());
    renderer->SetLayerStyle(styles_[index]);

    if (currentScene_.get() != NULL &&
        currentScene_->IsSamePlane(slice, sliceThickness_))
    {
      currentScene_->SetLayer(index, tmp.release());
      NotifyChange();
    }
    else if (pendingScene_.get() != NULL &&
             pendingScene_->IsSamePlane(slice, sliceThickness_))
    {
      pendingScene_->SetLayer(index, tmp.release());

      if (currentScene_.get() == NULL ||
          pendingScene_->IsComplete())
      {
        currentScene_ = pendingScene_;
        NotifyChange();
      }
    }
  }

  
  LayerWidget::LayerWidget() :
    started_(false),
    sliceThickness_(1)
  {
    SetBackgroundCleared(true);
  }
  
  
  LayerWidget::~LayerWidget()
  {
    for (size_t i = 0; i < layers_.size(); i++)
    {
      delete layers_[i];
    }
  }
  

  size_t LayerWidget::AddLayer(ILayerSource* layer)  // Takes ownership
  {
    if (layer == NULL)
    {
      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
    }

    size_t index = layers_.size();
    layers_.push_back(layer);
    styles_.push_back(RenderStyle());
    layersIndex_[layer] = index;

    ResetPendingScene();
    layer->SetObserver(*this);

    return index;
  }

  
  void LayerWidget::SetLayerStyle(size_t layer,
                                  const RenderStyle& style)
  {
    if (layer >= layers_.size())
    {
      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
    }

    assert(layers_.size() == styles_.size());
    styles_[layer] = style;

    if (currentScene_.get() != NULL)
    {
      currentScene_->SetLayerStyle(layer, style);
    }

    if (pendingScene_.get() != NULL)
    {
      pendingScene_->SetLayerStyle(layer, style);
    }

    NotifyChange();
  }
  

  void LayerWidget::SetSlice(const SliceGeometry& slice,
                             double sliceThickness)
  {
    if (!slice_.IsSamePlane(slice, 100.0 * std::numeric_limits<double>::epsilon()))
    {
      if (currentScene_.get() == NULL ||
          (pendingScene_.get() != NULL &&
           pendingScene_->IsComplete()))
      {
        currentScene_ = pendingScene_;
      }
        
      slice_ = slice;
      sliceThickness_ = sliceThickness;
      ResetPendingScene();

      if (started_)
      {
        for (size_t i = 0; i < layers_.size(); i++)
        {
          assert(layers_[i] != NULL);
          layers_[i]->ScheduleLayerCreation(slice_);
        }
      }
    }
  }

  
  void LayerWidget::NotifyGeometryReady(ILayerSource& source)
  {
    size_t i;
    if (LookupLayer(i, source))
      printf("Geometry ready for layer %d\n", (int) i);

    SetDefaultView();
    layers_[i]->ScheduleLayerCreation(slice_);
  }
  

  void LayerWidget::NotifySourceChange(ILayerSource& source)
  {
    source.ScheduleLayerCreation(slice_);
  }
  

  void LayerWidget::NotifySliceChange(ILayerSource& source,
                                      const SliceGeometry& slice)
  {
    if (slice_.IsSamePlane(slice, sliceThickness_))
    {
      source.ScheduleLayerCreation(slice_);
    }
  }
  

  void LayerWidget::NotifyLayerReady(ILayerRenderer* renderer,
                                     ILayerSource& source,
                                     const SliceGeometry& viewportSlice)
  {
    std::auto_ptr<ILayerRenderer> tmp(renderer);

    size_t i;
    if (LookupLayer(i, source))
      printf("Renderer ready for layer %d\n", (int) i);

    size_t index;
    if (LookupLayer(index, source))
    {
      UpdateLayer(index, tmp.release(), viewportSlice);
    }
  }

  
  void LayerWidget::NotifyLayerError(ILayerSource& source,
                                     const SliceGeometry& viewportSlice)
  {
    size_t i;
    if (LookupLayer(i, source))
      LOG(ERROR) << "Error on layer " << i;
  }    


  void LayerWidget::Start()
  {
    if (started_)
    {
      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);      
    }
    
    for (size_t i = 0; i < layers_.size(); i++)
    {
      assert(layers_[i] != NULL);
      layers_[i]->Start();
    }
  }
}