1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkInteractorStyleImage.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkInteractorStyleImage.h"
16 
17 #include "vtkAbstractPropPicker.h"
18 #include "vtkAssemblyPath.h"
19 #include "vtkPropCollection.h"
20 
21 #include "vtkCallbackCommand.h"
22 #include "vtkCamera.h"
23 #include "vtkImageMapper3D.h"
24 #include "vtkImageProperty.h"
25 #include "vtkImageSlice.h"
26 #include "vtkMath.h"
27 #include "vtkObjectFactory.h"
28 #include "vtkRenderWindow.h"
29 #include "vtkRenderWindowInteractor.h"
30 #include "vtkRenderer.h"
31 
32 vtkStandardNewMacro(vtkInteractorStyleImage);
33 
34 //------------------------------------------------------------------------------
vtkInteractorStyleImage()35 vtkInteractorStyleImage::vtkInteractorStyleImage()
36 {
37   this->WindowLevelStartPosition[0] = 0;
38   this->WindowLevelStartPosition[1] = 0;
39 
40   this->WindowLevelCurrentPosition[0] = 0;
41   this->WindowLevelCurrentPosition[1] = 0;
42 
43   this->WindowLevelInitial[0] = 1.0; // Window
44   this->WindowLevelInitial[1] = 0.5; // Level
45 
46   this->CurrentImageProperty = nullptr;
47   this->CurrentImageNumber = -1;
48 
49   this->InteractionMode = VTKIS_IMAGE2D;
50 
51   this->XViewRightVector[0] = 0;
52   this->XViewRightVector[1] = 1;
53   this->XViewRightVector[2] = 0;
54 
55   this->XViewUpVector[0] = 0;
56   this->XViewUpVector[1] = 0;
57   this->XViewUpVector[2] = -1;
58 
59   this->YViewRightVector[0] = 1;
60   this->YViewRightVector[1] = 0;
61   this->YViewRightVector[2] = 0;
62 
63   this->YViewUpVector[0] = 0;
64   this->YViewUpVector[1] = 0;
65   this->YViewUpVector[2] = -1;
66 
67   this->ZViewRightVector[0] = 1;
68   this->ZViewRightVector[1] = 0;
69   this->ZViewRightVector[2] = 0;
70 
71   this->ZViewUpVector[0] = 0;
72   this->ZViewUpVector[1] = 1;
73   this->ZViewUpVector[2] = 0;
74 }
75 
76 //------------------------------------------------------------------------------
~vtkInteractorStyleImage()77 vtkInteractorStyleImage::~vtkInteractorStyleImage()
78 {
79   if (this->CurrentImageProperty)
80   {
81     this->CurrentImageProperty->Delete();
82   }
83 }
84 
85 //------------------------------------------------------------------------------
StartWindowLevel()86 void vtkInteractorStyleImage::StartWindowLevel()
87 {
88   if (this->State != VTKIS_NONE)
89   {
90     return;
91   }
92   this->StartState(VTKIS_WINDOW_LEVEL);
93 
94   // Get the last (the topmost) image
95   this->SetCurrentImageNumber(this->CurrentImageNumber);
96 
97   if (this->HandleObservers && this->HasObserver(vtkCommand::StartWindowLevelEvent))
98   {
99     this->InvokeEvent(vtkCommand::StartWindowLevelEvent, this);
100   }
101   else
102   {
103     if (this->CurrentImageProperty)
104     {
105       vtkImageProperty* property = this->CurrentImageProperty;
106       this->WindowLevelInitial[0] = property->GetColorWindow();
107       this->WindowLevelInitial[1] = property->GetColorLevel();
108     }
109   }
110 }
111 
112 //------------------------------------------------------------------------------
EndWindowLevel()113 void vtkInteractorStyleImage::EndWindowLevel()
114 {
115   if (this->State != VTKIS_WINDOW_LEVEL)
116   {
117     return;
118   }
119   if (this->HandleObservers)
120   {
121     this->InvokeEvent(vtkCommand::EndWindowLevelEvent, this);
122   }
123   this->StopState();
124 }
125 
126 //------------------------------------------------------------------------------
StartPick()127 void vtkInteractorStyleImage::StartPick()
128 {
129   if (this->State != VTKIS_NONE)
130   {
131     return;
132   }
133   this->StartState(VTKIS_PICK);
134   if (this->HandleObservers)
135   {
136     this->InvokeEvent(vtkCommand::StartPickEvent, this);
137   }
138 }
139 
140 //------------------------------------------------------------------------------
EndPick()141 void vtkInteractorStyleImage::EndPick()
142 {
143   if (this->State != VTKIS_PICK)
144   {
145     return;
146   }
147   if (this->HandleObservers)
148   {
149     this->InvokeEvent(vtkCommand::EndPickEvent, this);
150   }
151   this->StopState();
152 }
153 
154 //------------------------------------------------------------------------------
StartSlice()155 void vtkInteractorStyleImage::StartSlice()
156 {
157   if (this->State != VTKIS_NONE)
158   {
159     return;
160   }
161   this->StartState(VTKIS_SLICE);
162 }
163 
164 //------------------------------------------------------------------------------
EndSlice()165 void vtkInteractorStyleImage::EndSlice()
166 {
167   if (this->State != VTKIS_SLICE)
168   {
169     return;
170   }
171   this->StopState();
172 }
173 
174 //------------------------------------------------------------------------------
OnMouseMove()175 void vtkInteractorStyleImage::OnMouseMove()
176 {
177   int x = this->Interactor->GetEventPosition()[0];
178   int y = this->Interactor->GetEventPosition()[1];
179 
180   switch (this->State)
181   {
182     case VTKIS_WINDOW_LEVEL:
183       this->FindPokedRenderer(x, y);
184       this->WindowLevel();
185       this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
186       break;
187 
188     case VTKIS_PICK:
189       this->FindPokedRenderer(x, y);
190       this->Pick();
191       this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
192       break;
193 
194     case VTKIS_SLICE:
195       this->FindPokedRenderer(x, y);
196       this->Slice();
197       this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
198       break;
199   }
200 
201   // Call parent to handle all other states and perform additional work
202 
203   this->Superclass::OnMouseMove();
204 }
205 
206 //------------------------------------------------------------------------------
OnLeftButtonDown()207 void vtkInteractorStyleImage::OnLeftButtonDown()
208 {
209   int x = this->Interactor->GetEventPosition()[0];
210   int y = this->Interactor->GetEventPosition()[1];
211 
212   this->FindPokedRenderer(x, y);
213   if (this->CurrentRenderer == nullptr)
214   {
215     return;
216   }
217 
218   // Redefine this button to handle window/level
219   this->GrabFocus(this->EventCallbackCommand);
220   if (!this->Interactor->GetShiftKey() && !this->Interactor->GetControlKey())
221   {
222     this->WindowLevelStartPosition[0] = x;
223     this->WindowLevelStartPosition[1] = y;
224     this->StartWindowLevel();
225   }
226 
227   // If shift is held down, do a rotation
228   else if (this->InteractionMode == VTKIS_IMAGE3D && this->Interactor->GetShiftKey())
229   {
230     this->StartRotate();
231   }
232 
233   // If ctrl is held down in slicing mode, slice the image
234   else if (this->InteractionMode == VTKIS_IMAGE_SLICING && this->Interactor->GetControlKey())
235   {
236     this->StartSlice();
237   }
238 
239   // The rest of the button + key combinations remain the same
240 
241   else
242   {
243     this->Superclass::OnLeftButtonDown();
244   }
245 }
246 
247 //------------------------------------------------------------------------------
OnLeftButtonUp()248 void vtkInteractorStyleImage::OnLeftButtonUp()
249 {
250   switch (this->State)
251   {
252     case VTKIS_WINDOW_LEVEL:
253       this->EndWindowLevel();
254       if (this->Interactor)
255       {
256         this->ReleaseFocus();
257       }
258       break;
259 
260     case VTKIS_SLICE:
261       this->EndSlice();
262       if (this->Interactor)
263       {
264         this->ReleaseFocus();
265       }
266       break;
267   }
268 
269   // Call parent to handle all other states and perform additional work
270 
271   this->Superclass::OnLeftButtonUp();
272 }
273 
274 //------------------------------------------------------------------------------
OnMiddleButtonDown()275 void vtkInteractorStyleImage::OnMiddleButtonDown()
276 {
277   this->FindPokedRenderer(
278     this->Interactor->GetEventPosition()[0], this->Interactor->GetEventPosition()[1]);
279   if (this->CurrentRenderer == nullptr)
280   {
281     return;
282   }
283 
284   // If shift is held down, change the slice
285   if ((this->InteractionMode == VTKIS_IMAGE3D || this->InteractionMode == VTKIS_IMAGE_SLICING) &&
286     this->Interactor->GetShiftKey())
287   {
288     this->StartSlice();
289   }
290 
291   // The rest of the button + key combinations remain the same
292 
293   else
294   {
295     this->Superclass::OnMiddleButtonDown();
296   }
297 }
298 
299 //------------------------------------------------------------------------------
OnMiddleButtonUp()300 void vtkInteractorStyleImage::OnMiddleButtonUp()
301 {
302   switch (this->State)
303   {
304     case VTKIS_SLICE:
305       this->EndSlice();
306       if (this->Interactor)
307       {
308         this->ReleaseFocus();
309       }
310       break;
311   }
312 
313   // Call parent to handle all other states and perform additional work
314 
315   this->Superclass::OnMiddleButtonUp();
316 }
317 
318 //------------------------------------------------------------------------------
OnRightButtonDown()319 void vtkInteractorStyleImage::OnRightButtonDown()
320 {
321   int x = this->Interactor->GetEventPosition()[0];
322   int y = this->Interactor->GetEventPosition()[1];
323 
324   this->FindPokedRenderer(x, y);
325   if (this->CurrentRenderer == nullptr)
326   {
327     return;
328   }
329 
330   // Redefine this button + shift to handle pick
331   this->GrabFocus(this->EventCallbackCommand);
332   if (this->Interactor->GetShiftKey())
333   {
334     this->StartPick();
335   }
336 
337   else if (this->InteractionMode == VTKIS_IMAGE3D && this->Interactor->GetControlKey())
338   {
339     this->StartSlice();
340   }
341   else if (this->InteractionMode == VTKIS_IMAGE_SLICING && this->Interactor->GetControlKey())
342   {
343     this->StartSpin();
344   }
345 
346   // The rest of the button + key combinations remain the same
347 
348   else
349   {
350     this->Superclass::OnRightButtonDown();
351   }
352 }
353 
354 //------------------------------------------------------------------------------
OnRightButtonUp()355 void vtkInteractorStyleImage::OnRightButtonUp()
356 {
357   switch (this->State)
358   {
359     case VTKIS_PICK:
360       this->EndPick();
361       if (this->Interactor)
362       {
363         this->ReleaseFocus();
364       }
365       break;
366 
367     case VTKIS_SLICE:
368       this->EndSlice();
369       if (this->Interactor)
370       {
371         this->ReleaseFocus();
372       }
373       break;
374 
375     case VTKIS_SPIN:
376       if (this->Interactor)
377       {
378         this->EndSpin();
379       }
380       break;
381   }
382 
383   // Call parent to handle all other states and perform additional work
384 
385   this->Superclass::OnRightButtonUp();
386 }
387 
388 //------------------------------------------------------------------------------
OnChar()389 void vtkInteractorStyleImage::OnChar()
390 {
391   vtkRenderWindowInteractor* rwi = this->Interactor;
392 
393   switch (rwi->GetKeyCode())
394   {
395     case 'f':
396     case 'F':
397     {
398       this->AnimState = VTKIS_ANIM_ON;
399       vtkAssemblyPath* path = nullptr;
400       this->FindPokedRenderer(rwi->GetEventPosition()[0], rwi->GetEventPosition()[1]);
401       rwi->GetPicker()->Pick(
402         rwi->GetEventPosition()[0], rwi->GetEventPosition()[1], 0.0, this->CurrentRenderer);
403       vtkAbstractPropPicker* picker;
404       if ((picker = vtkAbstractPropPicker::SafeDownCast(rwi->GetPicker())))
405       {
406         path = picker->GetPath();
407       }
408       if (path != nullptr)
409       {
410         rwi->FlyToImage(this->CurrentRenderer, picker->GetPickPosition());
411       }
412       this->AnimState = VTKIS_ANIM_OFF;
413       break;
414     }
415 
416     case 'r':
417     case 'R':
418       // Allow either shift/ctrl to trigger the usual 'r' binding
419       // otherwise trigger reset window level event
420       if (rwi->GetShiftKey() || rwi->GetControlKey())
421       {
422         this->Superclass::OnChar();
423       }
424       else if (this->HandleObservers && this->HasObserver(vtkCommand::ResetWindowLevelEvent))
425       {
426         this->InvokeEvent(vtkCommand::ResetWindowLevelEvent, this);
427       }
428       else if (this->CurrentImageProperty)
429       {
430         vtkImageProperty* property = this->CurrentImageProperty;
431         property->SetColorWindow(this->WindowLevelInitial[0]);
432         property->SetColorLevel(this->WindowLevelInitial[1]);
433         this->Interactor->Render();
434       }
435       break;
436 
437     case 'x':
438     case 'X':
439     {
440       this->SetImageOrientation(this->XViewRightVector, this->XViewUpVector);
441       this->Interactor->Render();
442     }
443     break;
444 
445     case 'y':
446     case 'Y':
447     {
448       this->SetImageOrientation(this->YViewRightVector, this->YViewUpVector);
449       this->Interactor->Render();
450     }
451     break;
452 
453     case 'z':
454     case 'Z':
455     {
456       this->SetImageOrientation(this->ZViewRightVector, this->ZViewUpVector);
457       this->Interactor->Render();
458     }
459     break;
460 
461     default:
462       this->Superclass::OnChar();
463       break;
464   }
465 }
466 
467 //------------------------------------------------------------------------------
WindowLevel()468 void vtkInteractorStyleImage::WindowLevel()
469 {
470   vtkRenderWindowInteractor* rwi = this->Interactor;
471 
472   this->WindowLevelCurrentPosition[0] = rwi->GetEventPosition()[0];
473   this->WindowLevelCurrentPosition[1] = rwi->GetEventPosition()[1];
474 
475   if (this->HandleObservers && this->HasObserver(vtkCommand::WindowLevelEvent))
476   {
477     this->InvokeEvent(vtkCommand::WindowLevelEvent, this);
478   }
479   else if (this->CurrentImageProperty)
480   {
481     const int* size = this->CurrentRenderer->GetSize();
482 
483     double window = this->WindowLevelInitial[0];
484     double level = this->WindowLevelInitial[1];
485 
486     // Compute normalized delta
487 
488     double dx =
489       (this->WindowLevelCurrentPosition[0] - this->WindowLevelStartPosition[0]) * 4.0 / size[0];
490     double dy =
491       (this->WindowLevelStartPosition[1] - this->WindowLevelCurrentPosition[1]) * 4.0 / size[1];
492 
493     // Scale by current values
494 
495     if (fabs(window) > 0.01)
496     {
497       dx = dx * window;
498     }
499     else
500     {
501       dx = dx * (window < 0 ? -0.01 : 0.01);
502     }
503     if (fabs(level) > 0.01)
504     {
505       dy = dy * level;
506     }
507     else
508     {
509       dy = dy * (level < 0 ? -0.01 : 0.01);
510     }
511 
512     // Abs so that direction does not flip
513 
514     if (window < 0.0)
515     {
516       dx = -1 * dx;
517     }
518     if (level < 0.0)
519     {
520       dy = -1 * dy;
521     }
522 
523     // Compute new window level
524 
525     double newWindow = dx + window;
526     double newLevel = level - dy;
527 
528     if (newWindow < 0.01)
529     {
530       newWindow = 0.01;
531     }
532 
533     this->CurrentImageProperty->SetColorWindow(newWindow);
534     this->CurrentImageProperty->SetColorLevel(newLevel);
535 
536     this->Interactor->Render();
537   }
538 }
539 
540 //------------------------------------------------------------------------------
Pick()541 void vtkInteractorStyleImage::Pick()
542 {
543   this->InvokeEvent(vtkCommand::PickEvent, this);
544 }
545 
546 //------------------------------------------------------------------------------
Slice()547 void vtkInteractorStyleImage::Slice()
548 {
549   if (this->CurrentRenderer == nullptr)
550   {
551     return;
552   }
553 
554   vtkRenderWindowInteractor* rwi = this->Interactor;
555   int dy = rwi->GetEventPosition()[1] - rwi->GetLastEventPosition()[1];
556 
557   vtkCamera* camera = this->CurrentRenderer->GetActiveCamera();
558   double* range = camera->GetClippingRange();
559   double distance = camera->GetDistance();
560 
561   // scale the interaction by the height of the viewport
562   double viewportHeight = 0.0;
563   if (camera->GetParallelProjection())
564   {
565     viewportHeight = camera->GetParallelScale();
566   }
567   else
568   {
569     double angle = vtkMath::RadiansFromDegrees(camera->GetViewAngle());
570     viewportHeight = 2.0 * distance * tan(0.5 * angle);
571   }
572 
573   const int* size = this->CurrentRenderer->GetSize();
574   double delta = dy * viewportHeight / size[1];
575   distance += delta;
576 
577   // clamp the distance to the clipping range
578   if (distance < range[0])
579   {
580     distance = range[0] + viewportHeight * 1e-3;
581   }
582   if (distance > range[1])
583   {
584     distance = range[1] - viewportHeight * 1e-3;
585   }
586   camera->SetDistance(distance);
587 
588   rwi->Render();
589 }
590 
591 //------------------------------------------------------------------------------
SetImageOrientation(const double leftToRight[3],const double bottomToTop[3])592 void vtkInteractorStyleImage::SetImageOrientation(
593   const double leftToRight[3], const double bottomToTop[3])
594 {
595   if (this->CurrentRenderer)
596   {
597     // the cross product points out of the screen
598     double vector[3];
599     vtkMath::Cross(leftToRight, bottomToTop, vector);
600     double focus[3];
601     vtkCamera* camera = this->CurrentRenderer->GetActiveCamera();
602     camera->GetFocalPoint(focus);
603     double d = camera->GetDistance();
604     camera->SetPosition(
605       focus[0] + d * vector[0], focus[1] + d * vector[1], focus[2] + d * vector[2]);
606     camera->SetFocalPoint(focus);
607     camera->SetViewUp(bottomToTop);
608   }
609 }
610 
611 //------------------------------------------------------------------------------
612 // This is a way of dealing with images as if they were layers.
613 // It looks through the renderer's list of props and sets the
614 // interactor ivars from the Nth image that it finds.  You can
615 // also use negative numbers, i.e. -1 will return the last image,
616 // -2 will return the second-to-last image, etc.
SetCurrentImageNumber(int i)617 void vtkInteractorStyleImage::SetCurrentImageNumber(int i)
618 {
619   this->CurrentImageNumber = i;
620 
621   if (!this->CurrentRenderer)
622   {
623     return;
624   }
625 
626   vtkPropCollection* props = this->CurrentRenderer->GetViewProps();
627   vtkProp* prop = nullptr;
628   vtkAssemblyPath* path;
629   vtkImageSlice* imageProp = nullptr;
630   vtkCollectionSimpleIterator pit;
631 
632   for (int k = 0; k < 2; k++)
633   {
634     int j = 0;
635     for (props->InitTraversal(pit); (prop = props->GetNextProp(pit));)
636     {
637       bool foundImageProp = false;
638       for (prop->InitPathTraversal(); (path = prop->GetNextPath());)
639       {
640         vtkProp* tryProp = path->GetLastNode()->GetViewProp();
641         imageProp = vtkImageSlice::SafeDownCast(tryProp);
642         if (imageProp)
643         {
644           if (j == i && imageProp->GetPickable())
645           {
646             foundImageProp = true;
647             break;
648           }
649           imageProp = nullptr;
650           j++;
651         }
652       }
653       if (foundImageProp)
654       {
655         break;
656       }
657     }
658     if (i < 0)
659     {
660       i += j;
661     }
662   }
663 
664   vtkImageProperty* property = nullptr;
665   if (imageProp)
666   {
667     property = imageProp->GetProperty();
668   }
669 
670   if (property != this->CurrentImageProperty)
671   {
672     if (this->CurrentImageProperty)
673     {
674       this->CurrentImageProperty->Delete();
675     }
676 
677     this->CurrentImageProperty = property;
678 
679     if (this->CurrentImageProperty)
680     {
681       this->CurrentImageProperty->Register(this);
682     }
683   }
684 }
685 
686 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)687 void vtkInteractorStyleImage::PrintSelf(ostream& os, vtkIndent indent)
688 {
689   this->Superclass::PrintSelf(os, indent);
690 
691   os << indent << "Window Level Current Position: (" << this->WindowLevelCurrentPosition[0] << ", "
692      << this->WindowLevelCurrentPosition[1] << ")\n";
693 
694   os << indent << "Window Level Start Position: (" << this->WindowLevelStartPosition[0] << ", "
695      << this->WindowLevelStartPosition[1] << ")\n";
696 
697   os << indent << "Interaction Mode: ";
698   if (this->InteractionMode == VTKIS_IMAGE2D)
699   {
700     os << "Image2D\n";
701   }
702   else if (this->InteractionMode == VTKIS_IMAGE3D)
703   {
704     os << "Image3D\n";
705   }
706   else if (this->InteractionMode == VTKIS_IMAGE_SLICING)
707   {
708     os << "ImageSlicing\n";
709   }
710   else
711   {
712     os << "Unknown\n";
713   }
714 
715   os << indent << "X View Right Vector: (" << this->XViewRightVector[0] << ", "
716      << this->XViewRightVector[1] << ", " << this->XViewRightVector[2] << ")\n";
717 
718   os << indent << "X View Up Vector: (" << this->XViewUpVector[0] << ", " << this->XViewUpVector[1]
719      << ", " << this->XViewUpVector[2] << ")\n";
720 
721   os << indent << "Y View Right Vector: (" << this->YViewRightVector[0] << ", "
722      << this->YViewRightVector[1] << ", " << this->YViewRightVector[2] << ")\n";
723 
724   os << indent << "Y View Up Vector: (" << this->YViewUpVector[0] << ", " << this->YViewUpVector[1]
725      << ", " << this->YViewUpVector[2] << ")\n";
726 
727   os << indent << "Z View Right Vector: (" << this->ZViewRightVector[0] << ", "
728      << this->ZViewRightVector[1] << ", " << this->ZViewRightVector[2] << ")\n";
729 
730   os << indent << "Z View Up Vector: (" << this->ZViewUpVector[0] << ", " << this->ZViewUpVector[1]
731      << ", " << this->ZViewUpVector[2] << ")\n";
732 }
733