comparison Framework/Widgets/SliceViewerWidget.cpp @ 388:20f149669c1f

renamed LayerWidget as SliceViewerWidget
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 09 Nov 2018 17:26:39 +0100
parents Framework/Widgets/LayerWidget.cpp@e33659decec5
children 17d54c028805
comparison
equal deleted inserted replaced
387:a8b5cf760473 388:20f149669c1f
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-2018 Osimis S.A., 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 "SliceViewerWidget.h"
23
24 #include "../Layers/SliceOutlineRenderer.h"
25 #include "../Toolbox/GeometryToolbox.h"
26 #include "Framework/Layers/FrameRenderer.h"
27
28 #include <Core/Logging.h>
29
30 #include <boost/math/constants/constants.hpp>
31
32
33 static const double THIN_SLICE_THICKNESS = 100.0 * std::numeric_limits<double>::epsilon();
34
35 namespace OrthancStone
36 {
37 class SliceViewerWidget::Scene : public boost::noncopyable
38 {
39 private:
40 CoordinateSystem3D slice_;
41 double thickness_;
42 size_t countMissing_;
43 std::vector<ILayerRenderer*> renderers_;
44
45 public:
46 void DeleteLayer(size_t index)
47 {
48 if (index >= renderers_.size())
49 {
50 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
51 }
52
53 assert(countMissing_ <= renderers_.size());
54
55 if (renderers_[index] != NULL)
56 {
57 assert(countMissing_ < renderers_.size());
58 delete renderers_[index];
59 renderers_[index] = NULL;
60 countMissing_++;
61 }
62 }
63
64 Scene(const CoordinateSystem3D& slice,
65 double thickness,
66 size_t countLayers) :
67 slice_(slice),
68 thickness_(thickness),
69 countMissing_(countLayers),
70 renderers_(countLayers, NULL)
71 {
72 if (thickness <= 0)
73 {
74 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
75 }
76 }
77
78 ~Scene()
79 {
80 for (size_t i = 0; i < renderers_.size(); i++)
81 {
82 DeleteLayer(i);
83 }
84 }
85
86 void SetLayer(size_t index,
87 ILayerRenderer* renderer) // Takes ownership
88 {
89 if (renderer == NULL)
90 {
91 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
92 }
93
94 DeleteLayer(index);
95
96 renderers_[index] = renderer;
97 countMissing_--;
98 }
99
100 const CoordinateSystem3D& GetSlice() const
101 {
102 return slice_;
103 }
104
105 bool HasRenderer(size_t index)
106 {
107 return renderers_[index] != NULL;
108 }
109
110 bool IsComplete() const
111 {
112 return countMissing_ == 0;
113 }
114
115 unsigned int GetCountMissing() const
116 {
117 return countMissing_;
118 }
119
120 bool RenderScene(CairoContext& context,
121 const ViewportGeometry& view,
122 const CoordinateSystem3D& viewportSlice)
123 {
124 bool fullQuality = true;
125 cairo_t *cr = context.GetObject();
126
127 for (size_t i = 0; i < renderers_.size(); i++)
128 {
129 if (renderers_[i] != NULL)
130 {
131 const CoordinateSystem3D& frameSlice = renderers_[i]->GetLayerSlice();
132
133 double x0, y0, x1, y1, x2, y2;
134 viewportSlice.ProjectPoint(x0, y0, frameSlice.GetOrigin());
135 viewportSlice.ProjectPoint(x1, y1, frameSlice.GetOrigin() + frameSlice.GetAxisX());
136 viewportSlice.ProjectPoint(x2, y2, frameSlice.GetOrigin() + frameSlice.GetAxisY());
137
138 /**
139 * Now we solve the system of linear equations Ax + b = x', given:
140 * A [0 ; 0] + b = [x0 ; y0]
141 * A [1 ; 0] + b = [x1 ; y1]
142 * A [0 ; 1] + b = [x2 ; y2]
143 * <=>
144 * b = [x0 ; y0]
145 * A [1 ; 0] = [x1 ; y1] - b = [x1 - x0 ; y1 - y0]
146 * A [0 ; 1] = [x2 ; y2] - b = [x2 - x0 ; y2 - y0]
147 * <=>
148 * b = [x0 ; y0]
149 * [a11 ; a21] = [x1 - x0 ; y1 - y0]
150 * [a12 ; a22] = [x2 - x0 ; y2 - y0]
151 **/
152
153 cairo_matrix_t transform;
154 cairo_matrix_init(&transform, x1 - x0, y1 - y0, x2 - x0, y2 - y0, x0, y0);
155
156 cairo_save(cr);
157 cairo_transform(cr, &transform);
158
159 if (!renderers_[i]->RenderLayer(context, view))
160 {
161 cairo_restore(cr);
162 return false;
163 }
164
165 cairo_restore(cr);
166 }
167
168 if (renderers_[i] != NULL &&
169 !renderers_[i]->IsFullQuality())
170 {
171 fullQuality = false;
172 }
173 }
174
175 if (!fullQuality)
176 {
177 double x, y;
178 view.MapDisplayToScene(x, y, static_cast<double>(view.GetDisplayWidth()) / 2.0, 10);
179
180 cairo_translate(cr, x, y);
181
182 #if 1
183 double s = 5.0 / view.GetZoom();
184 cairo_rectangle(cr, -s, -s, 2.0 * s, 2.0 * s);
185 #else
186 // TODO Drawing filled circles makes WebAssembly crash!
187 cairo_arc(cr, 0, 0, 5.0 / view.GetZoom(), 0, 2.0 * boost::math::constants::pi<double>());
188 #endif
189
190 cairo_set_line_width(cr, 2.0 / view.GetZoom());
191 cairo_set_source_rgb(cr, 1, 1, 1);
192 cairo_stroke_preserve(cr);
193 cairo_set_source_rgb(cr, 1, 0, 0);
194 cairo_fill(cr);
195 }
196
197 return true;
198 }
199
200 void SetLayerStyle(size_t index,
201 const RenderStyle& style)
202 {
203 if (renderers_[index] != NULL)
204 {
205 renderers_[index]->SetLayerStyle(style);
206 }
207 }
208
209 bool ContainsPlane(const CoordinateSystem3D& slice) const
210 {
211 bool isOpposite;
212 if (!GeometryToolbox::IsParallelOrOpposite(isOpposite,
213 slice.GetNormal(),
214 slice_.GetNormal()))
215 {
216 return false;
217 }
218 else
219 {
220 double z = (slice_.ProjectAlongNormal(slice.GetOrigin()) -
221 slice_.ProjectAlongNormal(slice_.GetOrigin()));
222
223 if (z < 0)
224 {
225 z = -z;
226 }
227
228 return z <= thickness_;
229 }
230 }
231
232 double GetThickness() const
233 {
234 return thickness_;
235 }
236 };
237
238
239 bool SliceViewerWidget::LookupLayer(size_t& index /* out */,
240 const ILayerSource& layer) const
241 {
242 LayersIndex::const_iterator found = layersIndex_.find(&layer);
243
244 if (found == layersIndex_.end())
245 {
246 return false;
247 }
248 else
249 {
250 index = found->second;
251 assert(index < layers_.size() &&
252 layers_[index] == &layer);
253 return true;
254 }
255 }
256
257
258 void SliceViewerWidget::GetLayerExtent(Extent2D& extent,
259 ILayerSource& source) const
260 {
261 extent.Reset();
262
263 std::vector<Vector> points;
264 if (source.GetExtent(points, slice_))
265 {
266 for (size_t i = 0; i < points.size(); i++)
267 {
268 double x, y;
269 slice_.ProjectPoint(x, y, points[i]);
270 extent.AddPoint(x, y);
271 }
272 }
273 }
274
275
276 Extent2D SliceViewerWidget::GetSceneExtent()
277 {
278 Extent2D sceneExtent;
279
280 for (size_t i = 0; i < layers_.size(); i++)
281 {
282 assert(layers_[i] != NULL);
283 Extent2D layerExtent;
284 GetLayerExtent(layerExtent, *layers_[i]);
285
286 sceneExtent.Union(layerExtent);
287 }
288
289 return sceneExtent;
290 }
291
292
293 bool SliceViewerWidget::RenderScene(CairoContext& context,
294 const ViewportGeometry& view)
295 {
296 if (currentScene_.get() != NULL)
297 {
298 return currentScene_->RenderScene(context, view, slice_);
299 }
300 else
301 {
302 return true;
303 }
304 }
305
306
307 void SliceViewerWidget::ResetPendingScene()
308 {
309 double thickness;
310 if (pendingScene_.get() == NULL)
311 {
312 thickness = 1.0;
313 }
314 else
315 {
316 thickness = pendingScene_->GetThickness();
317 }
318
319 pendingScene_.reset(new Scene(slice_, thickness, layers_.size()));
320 }
321
322
323 void SliceViewerWidget::UpdateLayer(size_t index,
324 ILayerRenderer* renderer,
325 const CoordinateSystem3D& slice)
326 {
327 LOG(INFO) << "Updating layer " << index;
328
329 std::auto_ptr<ILayerRenderer> tmp(renderer);
330
331 if (renderer == NULL)
332 {
333 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
334 }
335
336 if (index >= layers_.size())
337 {
338 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
339 }
340
341 assert(layers_.size() == styles_.size());
342 renderer->SetLayerStyle(styles_[index]);
343
344 if (currentScene_.get() != NULL &&
345 currentScene_->ContainsPlane(slice))
346 {
347 currentScene_->SetLayer(index, tmp.release());
348 NotifyContentChanged();
349 }
350 else if (pendingScene_.get() != NULL &&
351 pendingScene_->ContainsPlane(slice))
352 {
353 pendingScene_->SetLayer(index, tmp.release());
354
355 if (currentScene_.get() == NULL ||
356 !currentScene_->IsComplete() ||
357 pendingScene_->IsComplete())
358 {
359 currentScene_ = pendingScene_;
360 NotifyContentChanged();
361 }
362 }
363 }
364
365
366 SliceViewerWidget::SliceViewerWidget(MessageBroker& broker,
367 const std::string& name) :
368 WorldSceneWidget(name),
369 IObserver(broker),
370 IObservable(broker),
371 started_(false)
372 {
373 SetBackgroundCleared(true);
374 }
375
376
377 SliceViewerWidget::~SliceViewerWidget()
378 {
379 for (size_t i = 0; i < layers_.size(); i++)
380 {
381 delete layers_[i];
382 }
383 }
384
385 void SliceViewerWidget::ObserveLayer(ILayerSource& layer)
386 {
387 layer.RegisterObserverCallback(new Callable<SliceViewerWidget, ILayerSource::GeometryReadyMessage>
388 (*this, &SliceViewerWidget::OnGeometryReady));
389 // currently ignore errors layer->RegisterObserverCallback(new Callable<SliceViewerWidget, ILayerSource::GeometryErrorMessage>(*this, &SliceViewerWidget::...));
390 layer.RegisterObserverCallback(new Callable<SliceViewerWidget, ILayerSource::SliceChangedMessage>
391 (*this, &SliceViewerWidget::OnSliceChanged));
392 layer.RegisterObserverCallback(new Callable<SliceViewerWidget, ILayerSource::ContentChangedMessage>
393 (*this, &SliceViewerWidget::OnContentChanged));
394 layer.RegisterObserverCallback(new Callable<SliceViewerWidget, ILayerSource::LayerReadyMessage>
395 (*this, &SliceViewerWidget::OnLayerReady));
396 layer.RegisterObserverCallback(new Callable<SliceViewerWidget, ILayerSource::LayerErrorMessage>
397 (*this, &SliceViewerWidget::OnLayerError));
398 }
399
400
401 size_t SliceViewerWidget::AddLayer(ILayerSource* layer) // Takes ownership
402 {
403 if (layer == NULL)
404 {
405 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
406 }
407
408 size_t index = layers_.size();
409 layers_.push_back(layer);
410 styles_.push_back(RenderStyle());
411 layersIndex_[layer] = index;
412
413 ResetPendingScene();
414
415 ObserveLayer(*layer);
416
417 ResetChangedLayers();
418
419 return index;
420 }
421
422
423 void SliceViewerWidget::ReplaceLayer(size_t index, ILayerSource* layer) // Takes ownership
424 {
425 if (layer == NULL)
426 {
427 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
428 }
429
430 if (index >= layers_.size())
431 {
432 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
433 }
434
435 delete layers_[index];
436 layers_[index] = layer;
437 layersIndex_[layer] = index;
438
439 ResetPendingScene();
440
441 ObserveLayer(*layer);
442
443 InvalidateLayer(index);
444 }
445
446
447 void SliceViewerWidget::RemoveLayer(size_t index)
448 {
449 if (index >= layers_.size())
450 {
451 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
452 }
453
454 ILayerSource* previousLayer = layers_[index];
455 layersIndex_.erase(layersIndex_.find(previousLayer));
456 layers_.erase(layers_.begin() + index);
457 changedLayers_.erase(changedLayers_.begin() + index);
458 styles_.erase(styles_.begin() + index);
459
460 delete layers_[index];
461
462 currentScene_->DeleteLayer(index);
463 ResetPendingScene();
464
465 NotifyContentChanged();
466 }
467
468
469 const RenderStyle& SliceViewerWidget::GetLayerStyle(size_t layer) const
470 {
471 if (layer >= layers_.size())
472 {
473 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
474 }
475
476 assert(layers_.size() == styles_.size());
477 return styles_[layer];
478 }
479
480
481 void SliceViewerWidget::SetLayerStyle(size_t layer,
482 const RenderStyle& style)
483 {
484 if (layer >= layers_.size())
485 {
486 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
487 }
488
489 assert(layers_.size() == styles_.size());
490 styles_[layer] = style;
491
492 if (currentScene_.get() != NULL)
493 {
494 currentScene_->SetLayerStyle(layer, style);
495 }
496
497 if (pendingScene_.get() != NULL)
498 {
499 pendingScene_->SetLayerStyle(layer, style);
500 }
501
502 NotifyContentChanged();
503 }
504
505
506 void SliceViewerWidget::SetSlice(const CoordinateSystem3D& slice)
507 {
508 LOG(INFO) << "Setting slice origin: (" << slice.GetOrigin()[0]
509 << "," << slice.GetOrigin()[1]
510 << "," << slice.GetOrigin()[2] << ")";
511
512 Slice displayedSlice(slice_, THIN_SLICE_THICKNESS);
513
514 //if (!displayedSlice.ContainsPlane(slice))
515 {
516 if (currentScene_.get() == NULL ||
517 (pendingScene_.get() != NULL &&
518 pendingScene_->IsComplete()))
519 {
520 currentScene_ = pendingScene_;
521 }
522
523 slice_ = slice;
524 ResetPendingScene();
525
526 InvalidateAllLayers(); // TODO Removing this line avoid loading twice the image in WASM
527 }
528 }
529
530
531 void SliceViewerWidget::OnGeometryReady(const ILayerSource::GeometryReadyMessage& message)
532 {
533 size_t i;
534 if (LookupLayer(i, message.GetOrigin()))
535 {
536 LOG(INFO) << ": Geometry ready for layer " << i << " in " << GetName();
537
538 changedLayers_[i] = true;
539 //layers_[i]->ScheduleLayerCreation(slice_);
540 }
541 EmitMessage(GeometryChangedMessage(*this));
542 }
543
544
545 void SliceViewerWidget::InvalidateAllLayers()
546 {
547 for (size_t i = 0; i < layers_.size(); i++)
548 {
549 assert(layers_[i] != NULL);
550 changedLayers_[i] = true;
551
552 //layers_[i]->ScheduleLayerCreation(slice_);
553 }
554 }
555
556
557 void SliceViewerWidget::InvalidateLayer(size_t layer)
558 {
559 if (layer >= layers_.size())
560 {
561 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
562 }
563
564 assert(layers_[layer] != NULL);
565 changedLayers_[layer] = true;
566
567 //layers_[layer]->ScheduleLayerCreation(slice_);
568 }
569
570
571 void SliceViewerWidget::OnContentChanged(const ILayerSource::ContentChangedMessage& message)
572 {
573 size_t index;
574 if (LookupLayer(index, message.GetOrigin()))
575 {
576 InvalidateLayer(index);
577 }
578
579 EmitMessage(SliceViewerWidget::ContentChangedMessage(*this));
580 }
581
582
583 void SliceViewerWidget::OnSliceChanged(const ILayerSource::SliceChangedMessage& message)
584 {
585 if (message.GetSlice().ContainsPlane(slice_))
586 {
587 size_t index;
588 if (LookupLayer(index, message.GetOrigin()))
589 {
590 InvalidateLayer(index);
591 }
592 }
593
594 EmitMessage(SliceViewerWidget::ContentChangedMessage(*this));
595 }
596
597
598 void SliceViewerWidget::OnLayerReady(const ILayerSource::LayerReadyMessage& message)
599 {
600 size_t index;
601 if (LookupLayer(index, message.GetOrigin()))
602 {
603 LOG(INFO) << "Renderer ready for layer " << index;
604 UpdateLayer(index, message.CreateRenderer(), message.GetSlice());
605 }
606
607 EmitMessage(SliceViewerWidget::ContentChangedMessage(*this));
608 }
609
610
611 void SliceViewerWidget::OnLayerError(const ILayerSource::LayerErrorMessage& message)
612 {
613 size_t index;
614 if (LookupLayer(index, message.GetOrigin()))
615 {
616 LOG(ERROR) << "Using error renderer on layer " << index;
617
618 // TODO
619 //UpdateLayer(index, new SliceOutlineRenderer(slice), slice);
620
621 EmitMessage(SliceViewerWidget::ContentChangedMessage(*this));
622 }
623 }
624
625
626 void SliceViewerWidget::ResetChangedLayers()
627 {
628 changedLayers_.resize(layers_.size());
629
630 for (size_t i = 0; i < changedLayers_.size(); i++)
631 {
632 changedLayers_[i] = false;
633 }
634 }
635
636
637 void SliceViewerWidget::DoAnimation()
638 {
639 assert(changedLayers_.size() <= layers_.size());
640
641 for (size_t i = 0; i < changedLayers_.size(); i++)
642 {
643 if (changedLayers_[i])
644 {
645 layers_[i]->ScheduleLayerCreation(slice_);
646 }
647 }
648
649 ResetChangedLayers();
650 }
651 }