view Framework/Widgets/LayerWidget.cpp @ 94:7b14c12a3be5 wasm

fix
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 29 May 2017 18:03:30 +0200
parents 5945e81734a3
children f47349f4815c
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"
#include "../Layers/MissingLayerRenderer.h"

static const double THIN_SLICE_THICKNESS = 100.0 * std::numeric_limits<double>::epsilon();

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 HasRenderer(size_t index)
    {
      return renderers_[index] != NULL;
    }

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

    unsigned int GetCountMissing() const
    {
      return countMissing_;
    }

    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, slice_))
        {
          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 */,
                                const 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;
    }
  }
    

  bool LayerWidget::GetAndFixExtent(double& x1,
                                    double& y1,
                                    double& x2,
                                    double& y2,
                                    ILayerSource& source) const
  {
    if (source.GetExtent(x1, y1, x2, y2, slice_))
    {
      if (x1 > x2)
      {
        std::swap(x1, x2);
      }

      if (y1 > y2)
      {
        std::swap(y1, y2);
      }

      return true;
    }
    else
    {
      return false;
    }
  }

        
  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 (GetAndFixExtent(ax, ay, bx, by, *layers_[i]))
      {
        LOG(INFO) << "Extent of layer " << 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 Slice& slice)
  {
    LOG(INFO) << "Updating layer " << 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 &&
        slice.ContainsPlane(currentScene_->GetSlice()))
    {
      currentScene_->SetLayer(index, tmp.release());
      NotifyChange();
    }
    else if (pendingScene_.get() != NULL &&
             slice.ContainsPlane(pendingScene_->GetSlice()))
    {
      pendingScene_->SetLayer(index, tmp.release());

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

  
  LayerWidget::LayerWidget() :
    started_(false)
  {
    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->Register(*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)
  {
    LOG(INFO) << "Setting slice origin: (" << slice.GetOrigin()[0]
              << "," << slice.GetOrigin()[1]
              << "," << slice.GetOrigin()[2] << ")";
    
    Slice displayedSlice(slice_, THIN_SLICE_THICKNESS);

    if (!displayedSlice.ContainsPlane(slice))
    {
      if (currentScene_.get() == NULL ||
          (pendingScene_.get() != NULL &&
           pendingScene_->IsComplete()))
      {
        currentScene_ = pendingScene_;
      }

      slice_ = slice;
      ResetPendingScene();

      InvalidateAllLayers();   // TODO Removing this line avoid loading twice the image in WASM
    }
  }


  void LayerWidget::NotifyGeometryReady(const ILayerSource& source)
  {
    size_t i;
    if (LookupLayer(i, source))
    {
      LOG(INFO) << "Geometry ready for layer " << i;
      layers_[i]->ScheduleLayerCreation(slice_);
    }
  }
  

  void LayerWidget::NotifyGeometryError(const ILayerSource& source)
  {
    LOG(ERROR) << "Cannot get geometry";
  }
  

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


  void LayerWidget::InvalidateLayer(size_t layer)
  {
    if (layer >= layers_.size())
    {
      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
    }

    assert(layers_[layer] != NULL);
    layers_[layer]->ScheduleLayerCreation(slice_);
  }


  void LayerWidget::NotifyContentChange(const ILayerSource& source)
  {
    size_t index;
    if (LookupLayer(index, source))
    {
      layers_[index]->ScheduleLayerCreation(slice_);
    }
  }
  

  void LayerWidget::NotifySliceChange(const ILayerSource& source,
                                      const Slice& slice)
  {
    if (slice.ContainsPlane(slice_))
    {
      size_t index;
      if (LookupLayer(index, source))
      {
        layers_[index]->ScheduleLayerCreation(slice_);
      }
    }
  }
  
  
  void LayerWidget::NotifyLayerReady(std::auto_ptr<ILayerRenderer>& renderer,
                                     const ILayerSource& source,
                                     const Slice& slice)
  {
    size_t index;
    if (LookupLayer(index, source) &&
        slice.ContainsPlane(slice_))  // Whether the slice comes from an older request
    {
      LOG(INFO) << "Renderer ready for layer " << index;
      UpdateLayer(index, renderer.release(), slice);
    }
  }

  
  void LayerWidget::NotifyLayerError(const ILayerSource& source,
                                     const SliceGeometry& slice)
  {
    size_t index;

    Slice expected(slice_, THIN_SLICE_THICKNESS);

    if (LookupLayer(index, source) &&
        expected.ContainsPlane(slice))  // Whether the slice comes from an older request
    {
      LOG(INFO) << "Unable to load a slice from layer " << index;

      double x1, y1, x2, y2;
      if (GetAndFixExtent(x1, y1, x2, y2, *layers_[index]))
      {
        printf("**%d** %f %f %f %f\n", index, x1, y1, x2, y2);
        UpdateLayer(index, new MissingLayerRenderer(x1, y1, x2, y2), Slice(slice, THIN_SLICE_THICKNESS));
      }
    }
  }    
}