Mercurial > hg > orthanc-stone
comparison 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 |
comparison
equal
deleted
inserted
replaced
65:885932a893de | 66:298f375dcb68 |
---|---|
1 /** | |
2 * Stone of Orthanc | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
5 * Copyright (C) 2017 Osimis, Belgium | |
6 * | |
7 * This program is free software: you can redistribute it and/or | |
8 * modify it under the terms of the GNU Affero General Public License | |
9 * as published by the Free Software Foundation, either version 3 of | |
10 * the License, or (at your option) any later version. | |
11 * | |
12 * This program is distributed in the hope that it will be useful, but | |
13 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 * Affero General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU Affero General Public License | |
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 **/ | |
20 | |
21 | |
22 #include "LayerWidget.h" | |
23 | |
24 #include "../../Resources/Orthanc/Core/Logging.h" | |
25 | |
26 namespace OrthancStone | |
27 { | |
28 class LayerWidget::Scene : public boost::noncopyable | |
29 { | |
30 private: | |
31 SliceGeometry slice_; | |
32 size_t countMissing_; | |
33 std::vector<ILayerRenderer*> renderers_; | |
34 | |
35 void DeleteLayer(size_t index) | |
36 { | |
37 if (index >= renderers_.size()) | |
38 { | |
39 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
40 } | |
41 | |
42 assert(countMissing_ <= renderers_.size()); | |
43 | |
44 if (renderers_[index] != NULL) | |
45 { | |
46 assert(countMissing_ < renderers_.size()); | |
47 delete renderers_[index]; | |
48 renderers_[index] = NULL; | |
49 countMissing_++; | |
50 } | |
51 } | |
52 | |
53 public: | |
54 Scene(const SliceGeometry& slice, | |
55 size_t countLayers) : | |
56 slice_(slice), | |
57 countMissing_(countLayers), | |
58 renderers_(countLayers, NULL) | |
59 { | |
60 } | |
61 | |
62 ~Scene() | |
63 { | |
64 for (size_t i = 0; i < renderers_.size(); i++) | |
65 { | |
66 DeleteLayer(i); | |
67 } | |
68 } | |
69 | |
70 void SetLayer(size_t index, | |
71 ILayerRenderer* renderer) // Takes ownership | |
72 { | |
73 if (renderer == NULL) | |
74 { | |
75 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | |
76 } | |
77 | |
78 DeleteLayer(index); | |
79 | |
80 renderers_[index] = renderer; | |
81 countMissing_--; | |
82 } | |
83 | |
84 const SliceGeometry& GetSlice() const | |
85 { | |
86 return slice_; | |
87 } | |
88 | |
89 bool IsComplete() const | |
90 { | |
91 return countMissing_ == 0; | |
92 } | |
93 | |
94 bool IsSamePlane(const SliceGeometry& slice, | |
95 double sliceThickness) | |
96 { | |
97 return slice_.IsSamePlane(slice, sliceThickness); | |
98 } | |
99 | |
100 bool RenderScene(CairoContext& context, | |
101 const ViewportGeometry& view) | |
102 { | |
103 bool fullQuality = true; | |
104 | |
105 for (size_t i = 0; i < renderers_.size(); i++) | |
106 { | |
107 if (renderers_[i] != NULL && | |
108 !renderers_[i]->RenderLayer(context, view)) | |
109 { | |
110 return false; | |
111 } | |
112 | |
113 if (renderers_[i] != NULL && | |
114 !renderers_[i]->IsFullQuality()) | |
115 { | |
116 fullQuality = false; | |
117 } | |
118 } | |
119 | |
120 if (!fullQuality) | |
121 { | |
122 double x, y; | |
123 view.MapDisplayToScene(x, y, static_cast<double>(view.GetDisplayWidth()) / 2.0, 10); | |
124 | |
125 cairo_t *cr = context.GetObject(); | |
126 cairo_translate(cr, x, y); | |
127 cairo_arc(cr, 0, 0, 5.0 / view.GetZoom(), 0, 2 * M_PI); | |
128 cairo_set_line_width(cr, 2.0 / view.GetZoom()); | |
129 cairo_set_source_rgb(cr, 1, 1, 1); | |
130 cairo_stroke_preserve(cr); | |
131 cairo_set_source_rgb(cr, 1, 0, 0); | |
132 cairo_fill(cr); | |
133 } | |
134 | |
135 return true; | |
136 } | |
137 | |
138 void SetLayerStyle(size_t index, | |
139 const RenderStyle& style) | |
140 { | |
141 if (renderers_[index] != NULL) | |
142 { | |
143 renderers_[index]->SetLayerStyle(style); | |
144 } | |
145 } | |
146 }; | |
147 | |
148 | |
149 bool LayerWidget::LookupLayer(size_t& index /* out */, | |
150 ILayerSource& layer) const | |
151 { | |
152 LayersIndex::const_iterator found = layersIndex_.find(&layer); | |
153 | |
154 if (found == layersIndex_.end()) | |
155 { | |
156 return false; | |
157 } | |
158 else | |
159 { | |
160 index = found->second; | |
161 assert(index < layers_.size() && | |
162 layers_[index] == &layer); | |
163 return true; | |
164 } | |
165 } | |
166 | |
167 | |
168 void LayerWidget::GetSceneExtent(double& x1, | |
169 double& y1, | |
170 double& x2, | |
171 double& y2) | |
172 { | |
173 bool first = true; | |
174 | |
175 for (size_t i = 0; i < layers_.size(); i++) | |
176 { | |
177 double ax, ay, bx, by; | |
178 | |
179 assert(layers_[i] != NULL); | |
180 if (layers_[i]->GetExtent(ax, ay, bx, by, slice_)) | |
181 { | |
182 if (ax > bx) | |
183 { | |
184 std::swap(ax, bx); | |
185 } | |
186 | |
187 if (ay > by) | |
188 { | |
189 std::swap(ay, by); | |
190 } | |
191 | |
192 //LOG(INFO) << "Extent of layer " << i << ": (" << ax << "," << ay << ")->(" << bx << "," << by << ")"; | |
193 printf("Extent %d: (%f,%f) -> (%f,%f)\n", (int) i, ax, ay, bx, by); | |
194 | |
195 if (first) | |
196 { | |
197 x1 = ax; | |
198 y1 = ay; | |
199 x2 = bx; | |
200 y2 = by; | |
201 first = false; | |
202 } | |
203 else | |
204 { | |
205 x1 = std::min(x1, ax); | |
206 y1 = std::min(y1, ay); | |
207 x2 = std::max(x2, bx); | |
208 y2 = std::max(y2, by); | |
209 } | |
210 } | |
211 } | |
212 | |
213 if (first) | |
214 { | |
215 // Set a default extent of (-1,-1) -> (0,0) | |
216 x1 = -1; | |
217 y1 = -1; | |
218 x2 = 1; | |
219 y2 = 1; | |
220 } | |
221 | |
222 // Ensure the extent is non-empty | |
223 if (x1 >= x2) | |
224 { | |
225 double tmp = x1; | |
226 x1 = tmp - 0.5; | |
227 x2 = tmp + 0.5; | |
228 } | |
229 | |
230 if (y1 >= y2) | |
231 { | |
232 double tmp = y1; | |
233 y1 = tmp - 0.5; | |
234 y2 = tmp + 0.5; | |
235 } | |
236 } | |
237 | |
238 | |
239 bool LayerWidget::RenderScene(CairoContext& context, | |
240 const ViewportGeometry& view) | |
241 { | |
242 if (currentScene_.get() != NULL) | |
243 { | |
244 return currentScene_->RenderScene(context, view); | |
245 } | |
246 else | |
247 { | |
248 return true; | |
249 } | |
250 } | |
251 | |
252 | |
253 void LayerWidget::ResetPendingScene() | |
254 { | |
255 pendingScene_.reset(new Scene(slice_, layers_.size())); | |
256 } | |
257 | |
258 | |
259 void LayerWidget::UpdateLayer(size_t index, | |
260 ILayerRenderer* renderer, | |
261 const SliceGeometry& slice) | |
262 { | |
263 printf("Updating layer %d\n", (int) index); | |
264 | |
265 std::auto_ptr<ILayerRenderer> tmp(renderer); | |
266 | |
267 if (renderer == NULL) | |
268 { | |
269 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | |
270 } | |
271 | |
272 if (index >= layers_.size()) | |
273 { | |
274 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
275 } | |
276 | |
277 assert(layers_.size() == styles_.size()); | |
278 renderer->SetLayerStyle(styles_[index]); | |
279 | |
280 if (currentScene_.get() != NULL && | |
281 currentScene_->IsSamePlane(slice, sliceThickness_)) | |
282 { | |
283 currentScene_->SetLayer(index, tmp.release()); | |
284 NotifyChange(); | |
285 } | |
286 else if (pendingScene_.get() != NULL && | |
287 pendingScene_->IsSamePlane(slice, sliceThickness_)) | |
288 { | |
289 pendingScene_->SetLayer(index, tmp.release()); | |
290 | |
291 if (currentScene_.get() == NULL || | |
292 pendingScene_->IsComplete()) | |
293 { | |
294 currentScene_ = pendingScene_; | |
295 NotifyChange(); | |
296 } | |
297 } | |
298 } | |
299 | |
300 | |
301 LayerWidget::LayerWidget() : | |
302 started_(false), | |
303 sliceThickness_(1) | |
304 { | |
305 SetBackgroundCleared(true); | |
306 } | |
307 | |
308 | |
309 LayerWidget::~LayerWidget() | |
310 { | |
311 for (size_t i = 0; i < layers_.size(); i++) | |
312 { | |
313 delete layers_[i]; | |
314 } | |
315 } | |
316 | |
317 | |
318 size_t LayerWidget::AddLayer(ILayerSource* layer) // Takes ownership | |
319 { | |
320 if (layer == NULL) | |
321 { | |
322 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | |
323 } | |
324 | |
325 size_t index = layers_.size(); | |
326 layers_.push_back(layer); | |
327 styles_.push_back(RenderStyle()); | |
328 layersIndex_[layer] = index; | |
329 | |
330 ResetPendingScene(); | |
331 layer->SetObserver(*this); | |
332 | |
333 return index; | |
334 } | |
335 | |
336 | |
337 void LayerWidget::SetLayerStyle(size_t layer, | |
338 const RenderStyle& style) | |
339 { | |
340 if (layer >= layers_.size()) | |
341 { | |
342 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
343 } | |
344 | |
345 assert(layers_.size() == styles_.size()); | |
346 styles_[layer] = style; | |
347 | |
348 if (currentScene_.get() != NULL) | |
349 { | |
350 currentScene_->SetLayerStyle(layer, style); | |
351 } | |
352 | |
353 if (pendingScene_.get() != NULL) | |
354 { | |
355 pendingScene_->SetLayerStyle(layer, style); | |
356 } | |
357 | |
358 NotifyChange(); | |
359 } | |
360 | |
361 | |
362 void LayerWidget::SetSlice(const SliceGeometry& slice, | |
363 double sliceThickness) | |
364 { | |
365 if (!slice_.IsSamePlane(slice, 100.0 * std::numeric_limits<double>::epsilon())) | |
366 { | |
367 if (currentScene_.get() == NULL || | |
368 (pendingScene_.get() != NULL && | |
369 pendingScene_->IsComplete())) | |
370 { | |
371 currentScene_ = pendingScene_; | |
372 } | |
373 | |
374 slice_ = slice; | |
375 sliceThickness_ = sliceThickness; | |
376 ResetPendingScene(); | |
377 | |
378 if (started_) | |
379 { | |
380 for (size_t i = 0; i < layers_.size(); i++) | |
381 { | |
382 assert(layers_[i] != NULL); | |
383 layers_[i]->ScheduleLayerCreation(slice_); | |
384 } | |
385 } | |
386 } | |
387 } | |
388 | |
389 | |
390 void LayerWidget::NotifyGeometryReady(ILayerSource& source) | |
391 { | |
392 size_t i; | |
393 if (LookupLayer(i, source)) | |
394 printf("Geometry ready for layer %d\n", (int) i); | |
395 | |
396 SetDefaultView(); | |
397 layers_[i]->ScheduleLayerCreation(slice_); | |
398 } | |
399 | |
400 | |
401 void LayerWidget::NotifySourceChange(ILayerSource& source) | |
402 { | |
403 source.ScheduleLayerCreation(slice_); | |
404 } | |
405 | |
406 | |
407 void LayerWidget::NotifySliceChange(ILayerSource& source, | |
408 const SliceGeometry& slice) | |
409 { | |
410 if (slice_.IsSamePlane(slice, sliceThickness_)) | |
411 { | |
412 source.ScheduleLayerCreation(slice_); | |
413 } | |
414 } | |
415 | |
416 | |
417 void LayerWidget::NotifyLayerReady(ILayerRenderer* renderer, | |
418 ILayerSource& source, | |
419 const SliceGeometry& viewportSlice) | |
420 { | |
421 std::auto_ptr<ILayerRenderer> tmp(renderer); | |
422 | |
423 size_t i; | |
424 if (LookupLayer(i, source)) | |
425 printf("Renderer ready for layer %d\n", (int) i); | |
426 | |
427 size_t index; | |
428 if (LookupLayer(index, source)) | |
429 { | |
430 UpdateLayer(index, tmp.release(), viewportSlice); | |
431 } | |
432 } | |
433 | |
434 | |
435 void LayerWidget::NotifyLayerError(ILayerSource& source, | |
436 const SliceGeometry& viewportSlice) | |
437 { | |
438 size_t i; | |
439 if (LookupLayer(i, source)) | |
440 LOG(ERROR) << "Error on layer " << i; | |
441 } | |
442 | |
443 | |
444 void LayerWidget::Start() | |
445 { | |
446 if (started_) | |
447 { | |
448 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
449 } | |
450 | |
451 for (size_t i = 0; i < layers_.size(); i++) | |
452 { | |
453 assert(layers_[i] != NULL); | |
454 layers_[i]->Start(); | |
455 } | |
456 } | |
457 } |