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