Mercurial > hg > orthanc-stone
diff Framework/Widgets/LayerWidget.cpp @ 66:298f375dcb68 wasm
LayerWidget
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 17 May 2017 22:03:09 +0200 |
parents | |
children | 30c768873d47 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/LayerWidget.cpp Wed May 17 22:03:09 2017 +0200 @@ -0,0 +1,457 @@ +/** + * 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(); + } + } +}