comparison OrthancStone/Sources/Deprecated/Widgets/SliceViewerWidget.cpp @ 1512:244ad1e4e76a

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