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