comparison Framework/dev.h @ 551:90f3a60576a9 dev rtviewer19

Merged in ct-pet-dose-struct (pull request #2) Ct pet dose struct
author Benjamin Golinvaux <bgo@osimis.io>
date Tue, 02 Apr 2019 14:02:12 +0000
parents 1d9deb4ee84c
children 573e35378999
comparison
equal deleted inserted replaced
545:e1ba16436d59 551:90f3a60576a9
49 OrthancSlicesLoader loader_; 49 OrthancSlicesLoader loader_;
50 std::auto_ptr<ImageBuffer3D> image_; 50 std::auto_ptr<ImageBuffer3D> image_;
51 std::auto_ptr<DownloadStack> downloadStack_; 51 std::auto_ptr<DownloadStack> downloadStack_;
52 bool computeRange_; 52 bool computeRange_;
53 size_t pendingSlices_; 53 size_t pendingSlices_;
54 54
55 void ScheduleSliceDownload() 55 void ScheduleSliceDownload()
56 { 56 {
57 assert(downloadStack_.get() != NULL); 57 assert(downloadStack_.get() != NULL);
58 58
59 unsigned int slice; 59 unsigned int slice;
68 const Slice& b) 68 const Slice& b)
69 { 69 {
70 if (!GeometryToolbox::IsParallel(a.GetGeometry().GetNormal(), 70 if (!GeometryToolbox::IsParallel(a.GetGeometry().GetNormal(),
71 b.GetGeometry().GetNormal())) 71 b.GetGeometry().GetNormal()))
72 { 72 {
73 LOG(ERROR) << "Some slice in the volume image is not parallel to the others"; 73 LOG(ERROR) << "A slice in the volume image is not parallel to the others.";
74 return false; 74 return false;
75 } 75 }
76 76
77 if (a.GetConverter().GetExpectedPixelFormat() != b.GetConverter().GetExpectedPixelFormat()) 77 if (a.GetConverter().GetExpectedPixelFormat() != b.GetConverter().GetExpectedPixelFormat())
78 { 78 {
79 LOG(ERROR) << "The pixel format changes across the slices of the volume image"; 79 LOG(ERROR) << "The pixel format changes across the slices of the volume image.";
80 return false; 80 return false;
81 } 81 }
82 82
83 if (a.GetWidth() != b.GetWidth() || 83 if (a.GetWidth() != b.GetWidth() ||
84 a.GetHeight() != b.GetHeight()) 84 a.GetHeight() != b.GetHeight())
85 { 85 {
86 LOG(ERROR) << "The width/height of the slices change across the volume image"; 86 LOG(ERROR) << "The slices dimensions (width/height) are varying throughout the volume image";
87 return false; 87 return false;
88 } 88 }
89 89
90 if (!LinearAlgebra::IsNear(a.GetPixelSpacingX(), b.GetPixelSpacingX()) || 90 if (!LinearAlgebra::IsNear(a.GetPixelSpacingX(), b.GetPixelSpacingX()) ||
91 !LinearAlgebra::IsNear(a.GetPixelSpacingY(), b.GetPixelSpacingY())) 91 !LinearAlgebra::IsNear(a.GetPixelSpacingY(), b.GetPixelSpacingY()))
107 107
108 108
109 void OnSliceGeometryReady(const OrthancSlicesLoader::SliceGeometryReadyMessage& message) 109 void OnSliceGeometryReady(const OrthancSlicesLoader::SliceGeometryReadyMessage& message)
110 { 110 {
111 assert(&message.GetOrigin() == &loader_); 111 assert(&message.GetOrigin() == &loader_);
112 112
113 if (loader_.GetSliceCount() == 0) 113 if (loader_.GetSliceCount() == 0)
114 { 114 {
115 LOG(ERROR) << "Empty volume image"; 115 LOG(ERROR) << "Empty volume image";
116 EmitMessage(ISlicedVolume::GeometryErrorMessage(*this)); 116 EmitMessage(ISlicedVolume::GeometryErrorMessage(*this));
117 return; 117 return;
154 unsigned int height = loader_.GetSlice(0).GetHeight(); 154 unsigned int height = loader_.GetSlice(0).GetHeight();
155 Orthanc::PixelFormat format = loader_.GetSlice(0).GetConverter().GetExpectedPixelFormat(); 155 Orthanc::PixelFormat format = loader_.GetSlice(0).GetConverter().GetExpectedPixelFormat();
156 LOG(INFO) << "Creating a volume image of size " << width << "x" << height 156 LOG(INFO) << "Creating a volume image of size " << width << "x" << height
157 << "x" << loader_.GetSliceCount() << " in " << Orthanc::EnumerationToString(format); 157 << "x" << loader_.GetSliceCount() << " in " << Orthanc::EnumerationToString(format);
158 158
159 image_.reset(new ImageBuffer3D(format, width, height, loader_.GetSliceCount(), computeRange_)); 159 image_.reset(new ImageBuffer3D(format, width, height, static_cast<unsigned int>(loader_.GetSliceCount()), computeRange_));
160 image_->SetAxialGeometry(loader_.GetSlice(0).GetGeometry()); 160 image_->SetAxialGeometry(loader_.GetSlice(0).GetGeometry());
161 image_->SetVoxelDimensions(loader_.GetSlice(0).GetPixelSpacingX(), 161 image_->SetVoxelDimensions(loader_.GetSlice(0).GetPixelSpacingX(),
162 loader_.GetSlice(0).GetPixelSpacingY(), spacingZ); 162 loader_.GetSlice(0).GetPixelSpacingY(), spacingZ);
163 image_->Clear(); 163 image_->Clear();
164 164
165 downloadStack_.reset(new DownloadStack(loader_.GetSliceCount())); 165 downloadStack_.reset(new DownloadStack(static_cast<unsigned int>(loader_.GetSliceCount())));
166 pendingSlices_ = loader_.GetSliceCount(); 166 pendingSlices_ = loader_.GetSliceCount();
167 167
168 for (unsigned int i = 0; i < 4; i++) // Limit to 4 simultaneous downloads 168 for (unsigned int i = 0; i < 4; i++) // Limit to 4 simultaneous downloads
169 { 169 {
170 ScheduleSliceDownload(); 170 ScheduleSliceDownload();
173 // TODO Check the DicomFrameConverter are constant 173 // TODO Check the DicomFrameConverter are constant
174 174
175 EmitMessage(ISlicedVolume::GeometryReadyMessage(*this)); 175 EmitMessage(ISlicedVolume::GeometryReadyMessage(*this));
176 } 176 }
177 177
178 178
179 void OnSliceGeometryError(const OrthancSlicesLoader::SliceGeometryErrorMessage& message) 179 void OnSliceGeometryError(const OrthancSlicesLoader::SliceGeometryErrorMessage& message)
180 { 180 {
181 assert(&message.GetOrigin() == &loader_); 181 assert(&message.GetOrigin() == &loader_);
182 182
183 LOG(ERROR) << "Unable to download a volume image"; 183 LOG(ERROR) << "Unable to download a volume image";
184 EmitMessage(ISlicedVolume::GeometryErrorMessage(*this)); 184 EmitMessage(ISlicedVolume::GeometryErrorMessage(*this));
185 } 185 }
186 186
187 187
188 void OnSliceImageReady(const OrthancSlicesLoader::SliceImageReadyMessage& message) 188 void OnSliceImageReady(const OrthancSlicesLoader::SliceImageReadyMessage& message)
189 { 189 {
190 assert(&message.GetOrigin() == &loader_); 190 assert(&message.GetOrigin() == &loader_);
191 191
192 { 192 {
208 } 208 }
209 209
210 ScheduleSliceDownload(); 210 ScheduleSliceDownload();
211 } 211 }
212 212
213 213
214 void OnSliceImageError(const OrthancSlicesLoader::SliceImageErrorMessage& message) 214 void OnSliceImageError(const OrthancSlicesLoader::SliceImageErrorMessage& message)
215 { 215 {
216 assert(&message.GetOrigin() == &loader_); 216 assert(&message.GetOrigin() == &loader_);
217 217
218 LOG(ERROR) << "Cannot download slice " << message.GetSliceIndex() << " in a volume image"; 218 LOG(ERROR) << "Cannot download slice " << message.GetSliceIndex() << " in a volume image";
310 double pixelSpacingX_; 310 double pixelSpacingX_;
311 double pixelSpacingY_; 311 double pixelSpacingY_;
312 double sliceThickness_; 312 double sliceThickness_;
313 CoordinateSystem3D reference_; 313 CoordinateSystem3D reference_;
314 DicomFrameConverter converter_; 314 DicomFrameConverter converter_;
315 315
316 double ComputeAxialThickness(const OrthancVolumeImage& volume) const 316 double ComputeAxialThickness(const OrthancVolumeImage& volume) const
317 { 317 {
318 double thickness; 318 double thickness;
319 319
320 size_t n = volume.GetSliceCount(); 320 size_t n = volume.GetSliceCount();
321 if (n > 1) 321 if (n > 1)
322 { 322 {
323 const Slice& a = volume.GetSlice(0); 323 const Slice& a = volume.GetSlice(0);
324 const Slice& b = volume.GetSlice(n - 1); 324 const Slice& b = volume.GetSlice(n - 1);
340 else 340 else
341 { 341 {
342 return thickness; 342 return thickness;
343 } 343 }
344 } 344 }
345 345
346 void SetupAxial(const OrthancVolumeImage& volume) 346 void SetupAxial(const OrthancVolumeImage& volume)
347 { 347 {
348 const Slice& axial = volume.GetSlice(0); 348 const Slice& axial = volume.GetSlice(0);
349 349
350 width_ = axial.GetWidth(); 350 width_ = axial.GetWidth();
351 height_ = axial.GetHeight(); 351 height_ = axial.GetHeight();
352 depth_ = volume.GetSliceCount(); 352 depth_ = volume.GetSliceCount();
353 353
354 pixelSpacingX_ = axial.GetPixelSpacingX(); 354 pixelSpacingX_ = axial.GetPixelSpacingX();
362 { 362 {
363 const Slice& axial = volume.GetSlice(0); 363 const Slice& axial = volume.GetSlice(0);
364 double axialThickness = ComputeAxialThickness(volume); 364 double axialThickness = ComputeAxialThickness(volume);
365 365
366 width_ = axial.GetWidth(); 366 width_ = axial.GetWidth();
367 height_ = volume.GetSliceCount(); 367 height_ = static_cast<unsigned int>(volume.GetSliceCount());
368 depth_ = axial.GetHeight(); 368 depth_ = axial.GetHeight();
369 369
370 pixelSpacingX_ = axial.GetPixelSpacingX(); 370 pixelSpacingX_ = axial.GetPixelSpacingX();
371 pixelSpacingY_ = axialThickness; 371 pixelSpacingY_ = axialThickness;
372 sliceThickness_ = axial.GetPixelSpacingY(); 372 sliceThickness_ = axial.GetPixelSpacingY();
373 373
374 Vector origin = axial.GetGeometry().GetOrigin(); 374 Vector origin = axial.GetGeometry().GetOrigin();
375 origin += (static_cast<double>(volume.GetSliceCount() - 1) * 375 origin += (static_cast<double>(volume.GetSliceCount() - 1) *
376 axialThickness * axial.GetGeometry().GetNormal()); 376 axialThickness * axial.GetGeometry().GetNormal());
377 377
378 reference_ = CoordinateSystem3D(origin, 378 reference_ = CoordinateSystem3D(origin,
379 axial.GetGeometry().GetAxisX(), 379 axial.GetGeometry().GetAxisX(),
380 -axial.GetGeometry().GetNormal()); 380 - axial.GetGeometry().GetNormal());
381 } 381 }
382 382
383 void SetupSagittal(const OrthancVolumeImage& volume) 383 void SetupSagittal(const OrthancVolumeImage& volume)
384 { 384 {
385 const Slice& axial = volume.GetSlice(0); 385 const Slice& axial = volume.GetSlice(0);
386 double axialThickness = ComputeAxialThickness(volume); 386 double axialThickness = ComputeAxialThickness(volume);
387 387
388 width_ = axial.GetHeight(); 388 width_ = axial.GetHeight();
389 height_ = volume.GetSliceCount(); 389 height_ = static_cast<unsigned int>(volume.GetSliceCount());
390 depth_ = axial.GetWidth(); 390 depth_ = axial.GetWidth();
391 391
392 pixelSpacingX_ = axial.GetPixelSpacingY(); 392 pixelSpacingX_ = axial.GetPixelSpacingY();
393 pixelSpacingY_ = axialThickness; 393 pixelSpacingY_ = axialThickness;
394 sliceThickness_ = axial.GetPixelSpacingX(); 394 sliceThickness_ = axial.GetPixelSpacingX();
395 395
396 Vector origin = axial.GetGeometry().GetOrigin(); 396 Vector origin = axial.GetGeometry().GetOrigin();
397 origin += (static_cast<double>(volume.GetSliceCount() - 1) * 397 origin += (static_cast<double>(volume.GetSliceCount() - 1) *
398 axialThickness * axial.GetGeometry().GetNormal()); 398 axialThickness * axial.GetGeometry().GetNormal());
399 399
400 reference_ = CoordinateSystem3D(origin, 400 reference_ = CoordinateSystem3D(origin,
401 axial.GetGeometry().GetAxisY(), 401 axial.GetGeometry().GetAxisY(),
402 axial.GetGeometry().GetNormal()); 402 axial.GetGeometry().GetNormal());
403 } 403 }
404 404
413 413
414 converter_ = volume.GetSlice(0).GetConverter(); 414 converter_ = volume.GetSlice(0).GetConverter();
415 415
416 switch (projection) 416 switch (projection)
417 { 417 {
418 case VolumeProjection_Axial: 418 case VolumeProjection_Axial:
419 SetupAxial(volume); 419 SetupAxial(volume);
420 break; 420 break;
421 421
422 case VolumeProjection_Coronal: 422 case VolumeProjection_Coronal:
423 SetupCoronal(volume); 423 SetupCoronal(volume);
424 break; 424 break;
425 425
426 case VolumeProjection_Sagittal: 426 case VolumeProjection_Sagittal:
427 SetupSagittal(volume); 427 SetupSagittal(volume);
428 break; 428 break;
429 429
430 default: 430 default:
431 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); 431 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
432 } 432 }
433 } 433 }
434 434
435 size_t GetSliceCount() const 435 size_t GetSliceCount() const
436 { 436 {
439 439
440 const Vector& GetNormal() const 440 const Vector& GetNormal() const
441 { 441 {
442 return reference_.GetNormal(); 442 return reference_.GetNormal();
443 } 443 }
444 444
445 bool LookupSlice(size_t& index, 445 bool LookupSlice(size_t& index,
446 const CoordinateSystem3D& slice) const 446 const CoordinateSystem3D& slice) const
447 { 447 {
448 bool opposite; 448 bool opposite;
449 if (!GeometryToolbox::IsParallelOrOpposite(opposite, 449 if (!GeometryToolbox::IsParallelOrOpposite(opposite,
450 reference_.GetNormal(), 450 reference_.GetNormal(),
451 slice.GetNormal())) 451 slice.GetNormal()))
452 { 452 {
453 return false; 453 return false;
454 } 454 }
455 455
456 double z = (reference_.ProjectAlongNormal(slice.GetOrigin()) - 456 double z = (reference_.ProjectAlongNormal(slice.GetOrigin()) -
457 reference_.ProjectAlongNormal(reference_.GetOrigin())) / sliceThickness_; 457 reference_.ProjectAlongNormal(reference_.GetOrigin())) / sliceThickness_;
458 458
459 int s = static_cast<int>(boost::math::iround(z)); 459 int s = static_cast<int>(boost::math::iround(z));
460 460
461 if (s < 0 || 461 if (s < 0 ||
462 s >= static_cast<int>(depth_)) 462 s >= static_cast<int>(depth_))
463 { 463 {
464 return false; 464 return false;
465 } 465 }
466 else 466 else
467 { 467 {
505 505
506 public: 506 public:
507 RendererFactory(const Orthanc::ImageAccessor& frame, 507 RendererFactory(const Orthanc::ImageAccessor& frame,
508 const Slice& slice, 508 const Slice& slice,
509 bool isFullQuality) : 509 bool isFullQuality) :
510 frame_(frame), 510 frame_(frame),
511 slice_(slice), 511 slice_(slice),
512 isFullQuality_(isFullQuality) 512 isFullQuality_(isFullQuality)
513 { 513 {
514 } 514 }
515 515
516 virtual ILayerRenderer* CreateRenderer() const 516 virtual ILayerRenderer* CreateRenderer() const
517 { 517 {
523 OrthancVolumeImage& volume_; 523 OrthancVolumeImage& volume_;
524 std::auto_ptr<VolumeImageGeometry> axialGeometry_; 524 std::auto_ptr<VolumeImageGeometry> axialGeometry_;
525 std::auto_ptr<VolumeImageGeometry> coronalGeometry_; 525 std::auto_ptr<VolumeImageGeometry> coronalGeometry_;
526 std::auto_ptr<VolumeImageGeometry> sagittalGeometry_; 526 std::auto_ptr<VolumeImageGeometry> sagittalGeometry_;
527 527
528 528
529 bool IsGeometryReady() const 529 bool IsGeometryReady() const
530 { 530 {
531 return axialGeometry_.get() != NULL; 531 return axialGeometry_.get() != NULL;
532 } 532 }
533 533
534 void OnGeometryReady(const ISlicedVolume::GeometryReadyMessage& message) 534 void OnGeometryReady(const ISlicedVolume::GeometryReadyMessage& message)
535 { 535 {
536 assert(&message.GetOrigin() == &volume_); 536 assert(&message.GetOrigin() == &volume_);
537 537
538 // These 3 values are only used to speed up the IVolumeSlicer 538 // These 3 values are only used to speed up the IVolumeSlicer
539 axialGeometry_.reset(new VolumeImageGeometry(volume_, VolumeProjection_Axial)); 539 axialGeometry_.reset(new VolumeImageGeometry(volume_, VolumeProjection_Axial));
540 coronalGeometry_.reset(new VolumeImageGeometry(volume_, VolumeProjection_Coronal)); 540 coronalGeometry_.reset(new VolumeImageGeometry(volume_, VolumeProjection_Coronal));
541 sagittalGeometry_.reset(new VolumeImageGeometry(volume_, VolumeProjection_Sagittal)); 541 sagittalGeometry_.reset(new VolumeImageGeometry(volume_, VolumeProjection_Sagittal));
542 542
543 EmitMessage(IVolumeSlicer::GeometryReadyMessage(*this)); 543 EmitMessage(IVolumeSlicer::GeometryReadyMessage(*this));
544 } 544 }
545 545
546 void OnGeometryError(const ISlicedVolume::GeometryErrorMessage& message) 546 void OnGeometryError(const ISlicedVolume::GeometryErrorMessage& message)
547 { 547 {
548 assert(&message.GetOrigin() == &volume_); 548 assert(&message.GetOrigin() == &volume_);
549 549
550 EmitMessage(IVolumeSlicer::GeometryErrorMessage(*this)); 550 EmitMessage(IVolumeSlicer::GeometryErrorMessage(*this));
551 } 551 }
552 552
553 void OnContentChanged(const ISlicedVolume::ContentChangedMessage& message) 553 void OnContentChanged(const ISlicedVolume::ContentChangedMessage& message)
554 { 554 {
555 assert(&message.GetOrigin() == &volume_); 555 assert(&message.GetOrigin() == &volume_);
556 556
557 EmitMessage(IVolumeSlicer::ContentChangedMessage(*this)); 557 EmitMessage(IVolumeSlicer::ContentChangedMessage(*this));
558 } 558 }
559 559
560 void OnSliceContentChanged(const ISlicedVolume::SliceContentChangedMessage& message) 560 void OnSliceContentChanged(const ISlicedVolume::SliceContentChangedMessage& message)
561 { 561 {
574 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 574 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
575 } 575 }
576 576
577 switch (projection) 577 switch (projection)
578 { 578 {
579 case VolumeProjection_Axial: 579 case VolumeProjection_Axial:
580 return *axialGeometry_; 580 return *axialGeometry_;
581 581
582 case VolumeProjection_Sagittal: 582 case VolumeProjection_Sagittal:
583 return *sagittalGeometry_; 583 return *sagittalGeometry_;
584 584
585 case VolumeProjection_Coronal: 585 case VolumeProjection_Coronal:
586 return *coronalGeometry_; 586 return *coronalGeometry_;
587 587
588 default: 588 default:
589 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); 589 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
590 } 590 }
591 } 591 }
592 592
593 593
594 bool DetectProjection(VolumeProjection& projection, 594 bool DetectProjection(VolumeProjection& projection,
623 } 623 }
624 } 624 }
625 625
626 626
627 public: 627 public:
628 VolumeImageMPRSlicer(MessageBroker& broker, 628 VolumeImageMPRSlicer(MessageBroker& broker,
629 OrthancVolumeImage& volume) : 629 OrthancVolumeImage& volume) :
630 IVolumeSlicer(broker), 630 IVolumeSlicer(broker),
631 IObserver(broker), 631 IObserver(broker),
632 volume_(volume) 632 volume_(volume)
633 { 633 {
634 volume_.RegisterObserverCallback( 634 volume_.RegisterObserverCallback(
635 new Callable<VolumeImageMPRSlicer, ISlicedVolume::GeometryReadyMessage> 635 new Callable<VolumeImageMPRSlicer, ISlicedVolume::GeometryReadyMessage>
636 (*this, &VolumeImageMPRSlicer::OnGeometryReady)); 636 (*this, &VolumeImageMPRSlicer::OnGeometryReady));
637 637
650 650
651 virtual bool GetExtent(std::vector<Vector>& points, 651 virtual bool GetExtent(std::vector<Vector>& points,
652 const CoordinateSystem3D& viewportSlice) ORTHANC_OVERRIDE 652 const CoordinateSystem3D& viewportSlice) ORTHANC_OVERRIDE
653 { 653 {
654 VolumeProjection projection; 654 VolumeProjection projection;
655 655
656 if (!IsGeometryReady() || 656 if (!IsGeometryReady() ||
657 !DetectProjection(projection, viewportSlice)) 657 !DetectProjection(projection, viewportSlice))
658 { 658 {
659 return false; 659 return false;
660 } 660 }
662 { 662 {
663 // As the slices of the volumic image are arranged in a box, 663 // As the slices of the volumic image are arranged in a box,
664 // we only consider one single reference slice (the one with index 0). 664 // we only consider one single reference slice (the one with index 0).
665 std::auto_ptr<Slice> slice(GetProjectionGeometry(projection).GetSlice(0)); 665 std::auto_ptr<Slice> slice(GetProjectionGeometry(projection).GetSlice(0));
666 slice->GetExtent(points); 666 slice->GetExtent(points);
667 667
668 return true; 668 return true;
669 } 669 }
670 } 670 }
671
672 671
673 virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice) ORTHANC_OVERRIDE 672 virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice) ORTHANC_OVERRIDE
674 { 673 {
675 VolumeProjection projection; 674 VolumeProjection projection;
676 675
677 if (IsGeometryReady() && 676 if (IsGeometryReady() &&
678 DetectProjection(projection, viewportSlice)) 677 DetectProjection(projection, viewportSlice))
679 { 678 {
680 const VolumeImageGeometry& geometry = GetProjectionGeometry(projection); 679 const VolumeImageGeometry& geometry = GetProjectionGeometry(projection);
681 680
686 bool isFullQuality = true; // TODO 685 bool isFullQuality = true; // TODO
687 686
688 std::auto_ptr<Orthanc::Image> frame; 687 std::auto_ptr<Orthanc::Image> frame;
689 688
690 { 689 {
691 ImageBuffer3D::SliceReader reader(volume_.GetImage(), projection, closest); 690 ImageBuffer3D::SliceReader reader(volume_.GetImage(), projection, static_cast<unsigned int>(closest));
692 691
693 // TODO Transfer ownership if non-axial, to avoid memcpy 692 // TODO Transfer ownership if non-axial, to avoid memcpy
694 frame.reset(Orthanc::Image::Clone(reader.GetAccessor())); 693 frame.reset(Orthanc::Image::Clone(reader.GetAccessor()));
695 } 694 }
696 695
736 } 735 }
737 736
738 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, 737 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
739 const ViewportGeometry& view, 738 const ViewportGeometry& view,
740 MouseButton button, 739 MouseButton button,
740 KeyboardModifiers modifiers,
741 int viewportX,
742 int viewportY,
741 double x, 743 double x,
742 double y, 744 double y,
743 IStatusBar* statusBar) 745 IStatusBar* statusBar,
744 { 746 const std::vector<Touch>& touches) ORTHANC_OVERRIDE
745 return NULL; 747 {
748 return NULL;
746 } 749 }
747 750
748 virtual void MouseOver(CairoContext& context, 751 virtual void MouseOver(CairoContext& context,
749 WorldSceneWidget& widget, 752 WorldSceneWidget& widget,
750 const ViewportGeometry& view, 753 const ViewportGeometry& view,
751 double x, 754 double x,
752 double y, 755 double y,
753 IStatusBar* statusBar) 756 IStatusBar* statusBar) ORTHANC_OVERRIDE
754 { 757 {
755 } 758 }
756 759
757 virtual void MouseWheel(WorldSceneWidget& widget, 760 virtual void MouseWheel(WorldSceneWidget& widget,
758 MouseWheelDirection direction, 761 MouseWheelDirection direction,
759 KeyboardModifiers modifiers, 762 KeyboardModifiers modifiers,
760 IStatusBar* statusBar) 763 IStatusBar* statusBar) ORTHANC_OVERRIDE
761 { 764 {
762 int scale = (modifiers & KeyboardModifiers_Control ? 10 : 1); 765 int scale = (modifiers & KeyboardModifiers_Control ? 10 : 1);
763 766
764 switch (direction) 767 switch (direction)
765 { 768 {
766 case MouseWheelDirection_Up: 769 case MouseWheelDirection_Up:
767 OffsetSlice(-scale); 770 OffsetSlice(-scale);
768 break; 771 break;
769 772
770 case MouseWheelDirection_Down: 773 case MouseWheelDirection_Down:
771 OffsetSlice(scale); 774 OffsetSlice(scale);
772 break; 775 break;
773 776
774 default: 777 default:
775 break; 778 break;
776 } 779 }
777 } 780 }
778 781
779 virtual void KeyPressed(WorldSceneWidget& widget, 782 virtual void KeyPressed(WorldSceneWidget& widget,
780 KeyboardKeys key, 783 KeyboardKeys key,
781 char keyChar, 784 char keyChar,
782 KeyboardModifiers modifiers, 785 KeyboardModifiers modifiers,
783 IStatusBar* statusBar) 786 IStatusBar* statusBar) ORTHANC_OVERRIDE
784 { 787 {
785 switch (keyChar) 788 switch (keyChar)
786 { 789 {
787 case 's': 790 case 's':
788 widget.FitContent(); 791 widget.FitContent();
789 break; 792 break;
790 793
791 default: 794 default:
792 break; 795 break;
793 } 796 }
794 } 797 }
795 798
796 public: 799 public:
797 VolumeImageInteractor(MessageBroker& broker, 800 VolumeImageInteractor(MessageBroker& broker,
798 OrthancVolumeImage& volume, 801 OrthancVolumeImage& volume,
799 SliceViewerWidget& widget, 802 SliceViewerWidget& widget,
800 VolumeProjection projection) : 803 VolumeProjection projection) :
801 IObserver(broker), 804 IObserver(broker),
802 widget_(widget), 805 widget_(widget),
803 projection_(projection) 806 projection_(projection)
804 { 807 {
805 widget.SetInteractor(*this); 808 widget.SetInteractor(*this);
806 809
807 volume.RegisterObserverCallback( 810 volume.RegisterObserverCallback(
808 new Callable<VolumeImageInteractor, ISlicedVolume::GeometryReadyMessage> 811 new Callable<VolumeImageInteractor, ISlicedVolume::GeometryReadyMessage>
837 slice = 0; 840 slice = 0;
838 } 841 }
839 842
840 if (slice >= static_cast<int>(slices_->GetSliceCount())) 843 if (slice >= static_cast<int>(slices_->GetSliceCount()))
841 { 844 {
842 slice = slices_->GetSliceCount() - 1; 845 slice = static_cast<unsigned int>(slices_->GetSliceCount()) - 1;
843 } 846 }
844 847
845 if (slice != static_cast<int>(slice_)) 848 if (slice != static_cast<int>(slice_))
846 { 849 {
847 SetSlice(slice); 850 SetSlice(slice);
896 }; 899 };
897 900
898 SliceViewerWidget& otherPlane_; 901 SliceViewerWidget& otherPlane_;
899 902
900 public: 903 public:
901 ReferenceLineSource(MessageBroker& broker, 904 ReferenceLineSource(MessageBroker& broker,
902 SliceViewerWidget& otherPlane) : 905 SliceViewerWidget& otherPlane) :
903 IVolumeSlicer(broker), 906 IVolumeSlicer(broker),
904 otherPlane_(otherPlane) 907 otherPlane_(otherPlane)
905 { 908 {
906 EmitMessage(IVolumeSlicer::GeometryReadyMessage(*this)); 909 EmitMessage(IVolumeSlicer::GeometryReadyMessage(*this));
913 } 916 }
914 917
915 virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice) 918 virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice)
916 { 919 {
917 Slice reference(viewportSlice, 0.001); 920 Slice reference(viewportSlice, 0.001);
918 921
919 Vector p, d; 922 Vector p, d;
920 923
921 const CoordinateSystem3D& slice = otherPlane_.GetSlice(); 924 const CoordinateSystem3D& slice = otherPlane_.GetSlice();
922 925
923 // Compute the line of intersection between the two slices 926 // Compute the line of intersection between the two slices
933 double x1, y1, x2, y2; 936 double x1, y1, x2, y2;
934 viewportSlice.ProjectPoint(x1, y1, p); 937 viewportSlice.ProjectPoint(x1, y1, p);
935 viewportSlice.ProjectPoint(x2, y2, p + 1000.0 * d); 938 viewportSlice.ProjectPoint(x2, y2, p + 1000.0 * d);
936 939
937 const Extent2D extent = otherPlane_.GetSceneExtent(); 940 const Extent2D extent = otherPlane_.GetSceneExtent();
938 941
939 if (GeometryToolbox::ClipLineToRectangle(x1, y1, x2, y2, 942 if (GeometryToolbox::ClipLineToRectangle(x1, y1, x2, y2,
940 x1, y1, x2, y2, 943 x1, y1, x2, y2,
941 extent.GetX1(), extent.GetY1(), 944 extent.GetX1(), extent.GetY1(),
942 extent.GetX2(), extent.GetY2())) 945 extent.GetX2(), extent.GetY2()))
943 { 946 {