comparison Framework/Deprecated/Widgets/SliceViewerWidget.cpp @ 732:c35e98d22764

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