Mercurial > hg > orthanc-stone
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)); } } } }