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 }