Mercurial > hg > orthanc-stone
comparison Framework/Widgets/LayeredSceneWidget.cpp @ 0:351ab0da0150
initial commit
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 14 Oct 2016 15:34:11 +0200 |
parents | |
children | ff1e935768e7 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:351ab0da0150 |
---|---|
1 /** | |
2 * Stone of Orthanc | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
5 * | |
6 * This program is free software: you can redistribute it and/or | |
7 * modify it under the terms of the GNU General Public License as | |
8 * published by the Free Software Foundation, either version 3 of the | |
9 * License, or (at your option) any later version. | |
10 * | |
11 * In addition, as a special exception, the copyright holders of this | |
12 * program give permission to link the code of its release with the | |
13 * OpenSSL project's "OpenSSL" library (or with modified versions of it | |
14 * that use the same license as the "OpenSSL" library), and distribute | |
15 * the linked executables. You must obey the GNU General Public License | |
16 * in all respects for all of the code used other than "OpenSSL". If you | |
17 * modify file(s) with this exception, you may extend this exception to | |
18 * your version of the file(s), but you are not obligated to do so. If | |
19 * you do not wish to do so, delete this exception statement from your | |
20 * version. If you delete this exception statement from all source files | |
21 * in the program, then also delete it here. | |
22 * | |
23 * This program is distributed in the hope that it will be useful, but | |
24 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
26 * General Public License for more details. | |
27 * | |
28 * You should have received a copy of the GNU General Public License | |
29 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
30 **/ | |
31 | |
32 | |
33 #define _USE_MATH_DEFINES // To access M_PI in Visual Studio | |
34 #include <cmath> | |
35 | |
36 #include "LayeredSceneWidget.h" | |
37 | |
38 #include "../Orthanc/Core/OrthancException.h" | |
39 | |
40 namespace OrthancStone | |
41 { | |
42 class LayeredSceneWidget::Renderers : public boost::noncopyable | |
43 { | |
44 private: | |
45 boost::mutex mutex_; | |
46 std::vector<ILayerRenderer*> renderers_; | |
47 std::vector<bool> assigned_; | |
48 | |
49 void Assign(size_t index, | |
50 ILayerRenderer* renderer) | |
51 { | |
52 if (index >= renderers_.size()) | |
53 { | |
54 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
55 } | |
56 | |
57 if (renderers_[index] != NULL) | |
58 { | |
59 delete renderers_[index]; | |
60 } | |
61 | |
62 renderers_[index] = renderer; | |
63 assigned_[index] = true; | |
64 } | |
65 | |
66 public: | |
67 Renderers(size_t size) | |
68 { | |
69 renderers_.resize(size); | |
70 assigned_.resize(size, false); | |
71 } | |
72 | |
73 ~Renderers() | |
74 { | |
75 for (size_t i = 0; i < renderers_.size(); i++) | |
76 { | |
77 Assign(i, NULL); | |
78 } | |
79 } | |
80 | |
81 static void Merge(Renderers& target, | |
82 Renderers& source) | |
83 { | |
84 boost::mutex::scoped_lock lockSource(source.mutex_); | |
85 boost::mutex::scoped_lock lockTarget(target.mutex_); | |
86 | |
87 size_t count = target.renderers_.size(); | |
88 if (count != source.renderers_.size()) | |
89 { | |
90 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
91 } | |
92 | |
93 for (size_t i = 0; i < count; i++) | |
94 { | |
95 if (source.assigned_[i]) | |
96 { | |
97 target.Assign(i, source.renderers_[i]); // Transfers ownership | |
98 source.renderers_[i] = NULL; | |
99 source.assigned_[i] = false; | |
100 } | |
101 } | |
102 } | |
103 | |
104 void SetRenderer(size_t index, | |
105 ILayerRenderer* renderer) // Takes ownership | |
106 { | |
107 boost::mutex::scoped_lock lock(mutex_); | |
108 Assign(index, renderer); | |
109 } | |
110 | |
111 bool RenderScene(CairoContext& context, | |
112 const ViewportGeometry& view) | |
113 { | |
114 boost::mutex::scoped_lock lock(mutex_); | |
115 | |
116 bool fullQuality = true; | |
117 | |
118 for (size_t i = 0; i < renderers_.size(); i++) | |
119 { | |
120 if (renderers_[i] != NULL && | |
121 !renderers_[i]->RenderLayer(context, view)) | |
122 { | |
123 return false; | |
124 } | |
125 | |
126 if (renderers_[i] != NULL && | |
127 !renderers_[i]->IsFullQuality()) | |
128 { | |
129 fullQuality = false; | |
130 } | |
131 } | |
132 | |
133 if (!fullQuality) | |
134 { | |
135 double x, y; | |
136 view.MapDisplayToScene(x, y, static_cast<double>(view.GetDisplayWidth()) / 2.0, 10); | |
137 | |
138 cairo_t *cr = context.GetObject(); | |
139 cairo_translate(cr, x, y); | |
140 cairo_arc(cr, 0, 0, 5.0 / view.GetZoom(), 0, 2 * M_PI); | |
141 cairo_set_line_width(cr, 2.0 / view.GetZoom()); | |
142 cairo_set_source_rgb(cr, 1, 1, 1); | |
143 cairo_stroke_preserve(cr); | |
144 cairo_set_source_rgb(cr, 1, 0, 0); | |
145 cairo_fill(cr); | |
146 } | |
147 | |
148 return true; | |
149 } | |
150 | |
151 void SetLayerStyle(size_t index, | |
152 const RenderStyle& style) | |
153 { | |
154 boost::mutex::scoped_lock lock(mutex_); | |
155 | |
156 if (renderers_[index] != NULL) | |
157 { | |
158 renderers_[index]->SetLayerStyle(style); | |
159 } | |
160 } | |
161 }; | |
162 | |
163 | |
164 | |
165 class LayeredSceneWidget::PendingLayers : public boost::noncopyable | |
166 { | |
167 private: | |
168 boost::mutex mutex_; | |
169 boost::condition_variable elementAvailable_; | |
170 size_t layerCount_; | |
171 std::list<size_t> queue_; | |
172 std::vector<bool> layersToUpdate_; | |
173 bool continue_; | |
174 | |
175 void TagAllLayers() | |
176 { | |
177 queue_.clear(); | |
178 | |
179 for (unsigned int i = 0; i < layerCount_; i++) | |
180 { | |
181 queue_.push_back(i); | |
182 layersToUpdate_[i] = true; | |
183 } | |
184 | |
185 if (layerCount_ != 0) | |
186 { | |
187 elementAvailable_.notify_one(); | |
188 } | |
189 } | |
190 | |
191 public: | |
192 PendingLayers() : | |
193 layerCount_(0), | |
194 continue_(true) | |
195 { | |
196 } | |
197 | |
198 void Stop() | |
199 { | |
200 continue_ = false; | |
201 elementAvailable_.notify_one(); | |
202 } | |
203 | |
204 void SetLayerCount(size_t count) | |
205 { | |
206 boost::mutex::scoped_lock lock(mutex_); | |
207 | |
208 layerCount_ = count; | |
209 layersToUpdate_.resize(count); | |
210 | |
211 TagAllLayers(); | |
212 } | |
213 | |
214 void InvalidateAllLayers() | |
215 { | |
216 boost::mutex::scoped_lock lock(mutex_); | |
217 TagAllLayers(); | |
218 } | |
219 | |
220 void InvalidateLayer(size_t layer) | |
221 { | |
222 boost::mutex::scoped_lock lock(mutex_); | |
223 | |
224 if (layer < layerCount_) | |
225 { | |
226 if (layersToUpdate_[layer]) | |
227 { | |
228 // The layer is already scheduled for update, ignore this | |
229 // invalidation | |
230 } | |
231 else | |
232 { | |
233 queue_.push_back(layer); | |
234 layersToUpdate_[layer] = true; | |
235 elementAvailable_.notify_one(); | |
236 } | |
237 } | |
238 } | |
239 | |
240 bool Dequeue(size_t& layer, | |
241 bool& isLast) | |
242 { | |
243 boost::mutex::scoped_lock lock(mutex_); | |
244 | |
245 // WARNING: Do NOT use "timed_wait" on condition variables, as | |
246 // sleeping is not properly supported by Boost for Google NaCl | |
247 while (queue_.empty() && | |
248 continue_) | |
249 { | |
250 elementAvailable_.wait(lock); | |
251 } | |
252 | |
253 if (!continue_) | |
254 { | |
255 return false; | |
256 } | |
257 | |
258 layer = queue_.front(); | |
259 layersToUpdate_[layer] = false; | |
260 queue_.pop_front(); | |
261 | |
262 isLast = queue_.empty(); | |
263 | |
264 return true; | |
265 } | |
266 }; | |
267 | |
268 | |
269 class LayeredSceneWidget::Layer : public ISliceableVolume::IChangeObserver | |
270 { | |
271 private: | |
272 boost::mutex mutex_; | |
273 std::auto_ptr<ILayerRendererFactory> factory_; | |
274 PendingLayers& layers_; | |
275 size_t index_; | |
276 std::auto_ptr<RenderStyle> style_; | |
277 | |
278 public: | |
279 Layer(ILayerRendererFactory* factory, | |
280 PendingLayers& layers, | |
281 size_t index) : | |
282 factory_(factory), | |
283 layers_(layers), | |
284 index_(index) | |
285 { | |
286 if (factory == NULL) | |
287 { | |
288 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
289 } | |
290 } | |
291 | |
292 virtual void NotifyChange(const OrthancStone::ISliceableVolume&) | |
293 { | |
294 layers_.InvalidateLayer(index_); | |
295 } | |
296 | |
297 void Start() | |
298 { | |
299 if (factory_->HasSourceVolume()) | |
300 { | |
301 factory_->GetSourceVolume().Register(*this); | |
302 } | |
303 } | |
304 | |
305 void Stop() | |
306 { | |
307 if (factory_->HasSourceVolume()) | |
308 { | |
309 factory_->GetSourceVolume().Unregister(*this); | |
310 } | |
311 } | |
312 | |
313 bool GetExtent(double& x1, | |
314 double& y1, | |
315 double& x2, | |
316 double& y2, | |
317 const SliceGeometry& displaySlice) | |
318 { | |
319 boost::mutex::scoped_lock lock(mutex_); | |
320 assert(factory_.get() != NULL); | |
321 return factory_->GetExtent(x1, y1, x2, y2, displaySlice); | |
322 } | |
323 | |
324 RenderStyle GetStyle() | |
325 { | |
326 boost::mutex::scoped_lock lock(mutex_); | |
327 | |
328 if (style_.get() == NULL) | |
329 { | |
330 return RenderStyle(); | |
331 } | |
332 else | |
333 { | |
334 return *style_; | |
335 } | |
336 } | |
337 | |
338 void SetStyle(const RenderStyle& style) | |
339 { | |
340 boost::mutex::scoped_lock lock(mutex_); | |
341 style_.reset(new RenderStyle(style)); | |
342 } | |
343 | |
344 | |
345 ILayerRenderer* CreateRenderer(const SliceGeometry& displaySlice) | |
346 { | |
347 boost::mutex::scoped_lock lock(mutex_); | |
348 assert(factory_.get() != NULL); | |
349 | |
350 std::auto_ptr<ILayerRenderer> renderer(factory_->CreateLayerRenderer(displaySlice)); | |
351 | |
352 if (renderer.get() != NULL && | |
353 style_.get() != NULL) | |
354 { | |
355 renderer->SetLayerStyle(*style_); | |
356 } | |
357 | |
358 return renderer.release(); | |
359 } | |
360 }; | |
361 | |
362 | |
363 | |
364 SliceGeometry LayeredSceneWidget::GetSlice() | |
365 { | |
366 boost::mutex::scoped_lock lock(sliceMutex_); | |
367 return slice_; | |
368 } | |
369 | |
370 | |
371 void LayeredSceneWidget::UpdateStep() | |
372 { | |
373 size_t layer = 0; | |
374 bool isLast = true; | |
375 if (!pendingLayers_->Dequeue(layer, isLast)) | |
376 { | |
377 return; | |
378 } | |
379 | |
380 SliceGeometry slice = GetSlice(); | |
381 | |
382 std::auto_ptr<ILayerRenderer> renderer; | |
383 renderer.reset(layers_[layer]->CreateRenderer(slice)); | |
384 | |
385 if (renderer.get() != NULL) | |
386 { | |
387 pendingRenderers_->SetRenderer(layer, renderer.release()); | |
388 } | |
389 else | |
390 { | |
391 pendingRenderers_->SetRenderer(layer, NULL); | |
392 } | |
393 | |
394 if (isLast) | |
395 { | |
396 Renderers::Merge(*renderers_, *pendingRenderers_); | |
397 NotifyChange(); | |
398 } | |
399 } | |
400 | |
401 | |
402 bool LayeredSceneWidget::RenderScene(CairoContext& context, | |
403 const ViewportGeometry& view) | |
404 { | |
405 assert(IsStarted()); | |
406 return renderers_->RenderScene(context, view); | |
407 } | |
408 | |
409 | |
410 LayeredSceneWidget::LayeredSceneWidget() | |
411 { | |
412 pendingLayers_.reset(new PendingLayers); | |
413 SetBackgroundCleared(true); | |
414 } | |
415 | |
416 | |
417 LayeredSceneWidget::~LayeredSceneWidget() | |
418 { | |
419 for (size_t i = 0; i < layers_.size(); i++) | |
420 { | |
421 assert(layers_[i] != NULL); | |
422 delete layers_[i]; | |
423 } | |
424 } | |
425 | |
426 | |
427 void LayeredSceneWidget::GetSceneExtent(double& x1, | |
428 double& y1, | |
429 double& x2, | |
430 double& y2) | |
431 { | |
432 boost::mutex::scoped_lock lock(sliceMutex_); | |
433 | |
434 bool first = true; | |
435 | |
436 for (size_t i = 0; i < layers_.size(); i++) | |
437 { | |
438 double ax, ay, bx, by; | |
439 | |
440 assert(layers_[i] != NULL); | |
441 if (layers_[i]->GetExtent(ax, ay, bx, by, slice_)) | |
442 { | |
443 if (ax > bx) | |
444 { | |
445 std::swap(ax, bx); | |
446 } | |
447 | |
448 if (ay > by) | |
449 { | |
450 std::swap(ay, by); | |
451 } | |
452 | |
453 if (first) | |
454 { | |
455 x1 = ax; | |
456 y1 = ay; | |
457 x2 = bx; | |
458 y2 = by; | |
459 first = false; | |
460 } | |
461 else | |
462 { | |
463 x1 = std::min(x1, ax); | |
464 y1 = std::min(y1, ay); | |
465 x2 = std::max(x2, bx); | |
466 y2 = std::max(y2, by); | |
467 } | |
468 } | |
469 } | |
470 | |
471 if (first) | |
472 { | |
473 x1 = -1; | |
474 y1 = -1; | |
475 x2 = 1; | |
476 y2 = 1; | |
477 } | |
478 | |
479 // Ensure the extent is non-empty | |
480 if (x1 >= x2) | |
481 { | |
482 double tmp = x1; | |
483 x1 = tmp - 0.5; | |
484 x2 = tmp + 0.5; | |
485 } | |
486 | |
487 if (y1 >= y2) | |
488 { | |
489 double tmp = y1; | |
490 y1 = tmp - 0.5; | |
491 y2 = tmp + 0.5; | |
492 } | |
493 } | |
494 | |
495 | |
496 | |
497 ILayerRendererFactory& LayeredSceneWidget::AddLayer(size_t& layerIndex, | |
498 ILayerRendererFactory* factory) | |
499 { | |
500 if (IsStarted()) | |
501 { | |
502 // Start() has already been invoked | |
503 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
504 } | |
505 | |
506 layerIndex = layers_.size(); | |
507 layers_.push_back(new Layer(factory, *pendingLayers_, layers_.size())); | |
508 | |
509 return *factory; | |
510 } | |
511 | |
512 | |
513 void LayeredSceneWidget::AddLayer(ILayerRendererFactory* factory) | |
514 { | |
515 size_t layerIndex; // Ignored | |
516 AddLayer(layerIndex, factory); | |
517 } | |
518 | |
519 | |
520 RenderStyle LayeredSceneWidget::GetLayerStyle(size_t layer) | |
521 { | |
522 if (layer >= layers_.size()) | |
523 { | |
524 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
525 } | |
526 | |
527 return layers_[layer]->GetStyle(); | |
528 } | |
529 | |
530 | |
531 void LayeredSceneWidget::SetLayerStyle(size_t layer, | |
532 const RenderStyle& style) | |
533 { | |
534 if (layer >= layers_.size()) | |
535 { | |
536 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
537 } | |
538 | |
539 layers_[layer]->SetStyle(style); | |
540 | |
541 if (renderers_.get() != NULL) | |
542 { | |
543 renderers_->SetLayerStyle(layer, style); | |
544 } | |
545 | |
546 InvalidateLayer(layer); | |
547 } | |
548 | |
549 | |
550 | |
551 struct LayeredSceneWidget::SliceChangeFunctor | |
552 { | |
553 const SliceGeometry& slice_; | |
554 | |
555 SliceChangeFunctor(const SliceGeometry& slice) : | |
556 slice_(slice) | |
557 { | |
558 } | |
559 | |
560 void operator() (ISliceObserver& observer, | |
561 const LayeredSceneWidget& source) | |
562 { | |
563 observer.NotifySliceChange(source, slice_); | |
564 } | |
565 }; | |
566 | |
567 | |
568 void LayeredSceneWidget::SetSlice(const SliceGeometry& slice) | |
569 { | |
570 { | |
571 boost::mutex::scoped_lock lock(sliceMutex_); | |
572 slice_ = slice; | |
573 } | |
574 | |
575 InvalidateAllLayers(); | |
576 | |
577 SliceChangeFunctor functor(slice); | |
578 observers_.Notify(this, functor); | |
579 } | |
580 | |
581 | |
582 void LayeredSceneWidget::InvalidateLayer(unsigned int layer) | |
583 { | |
584 pendingLayers_->InvalidateLayer(layer); | |
585 //NotifyChange(); // TODO Understand why this makes the SDL engine not update the display subsequently | |
586 } | |
587 | |
588 | |
589 void LayeredSceneWidget::InvalidateAllLayers() | |
590 { | |
591 pendingLayers_->InvalidateAllLayers(); | |
592 //NotifyChange(); // TODO Understand why this makes the SDL engine not update the display subsequently | |
593 } | |
594 | |
595 | |
596 void LayeredSceneWidget::Start() | |
597 { | |
598 if (IsStarted()) | |
599 { | |
600 // Start() has already been invoked | |
601 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
602 } | |
603 | |
604 for (size_t i = 0; i < layers_.size(); i++) | |
605 { | |
606 layers_[i]->Start(); | |
607 } | |
608 | |
609 renderers_.reset(new Renderers(layers_.size())); | |
610 pendingRenderers_.reset(new Renderers(layers_.size())); | |
611 | |
612 pendingLayers_->SetLayerCount(layers_.size()); | |
613 | |
614 WorldSceneWidget::Start(); | |
615 } | |
616 | |
617 | |
618 void LayeredSceneWidget::Stop() | |
619 { | |
620 if (!IsStarted()) | |
621 { | |
622 // Stop() has already been invoked | |
623 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
624 } | |
625 | |
626 pendingLayers_->Stop(); | |
627 WorldSceneWidget::Stop(); | |
628 | |
629 renderers_.reset(NULL); | |
630 pendingRenderers_.reset(NULL); | |
631 | |
632 for (size_t i = 0; i < layers_.size(); i++) | |
633 { | |
634 layers_[i]->Stop(); | |
635 } | |
636 } | |
637 } |