1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkResliceImageViewer.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 "vtkResliceImageViewer.h"
16 
17 #include "vtkCamera.h"
18 #include "vtkCommand.h"
19 #include "vtkImageActor.h"
20 #include "vtkResliceCursorWidget.h"
21 #include "vtkResliceCursorLineRepresentation.h"
22 #include "vtkResliceCursorThickLineRepresentation.h"
23 #include "vtkResliceCursorActor.h"
24 #include "vtkResliceCursorPolyDataAlgorithm.h"
25 #include "vtkPlane.h"
26 #include "vtkResliceCursor.h"
27 #include "vtkImageData.h"
28 #include "vtkImageMapToWindowLevelColors.h"
29 #include "vtkInteractorStyleImage.h"
30 #include "vtkObjectFactory.h"
31 #include "vtkRenderWindow.h"
32 #include "vtkRenderWindowInteractor.h"
33 #include "vtkRenderer.h"
34 #include "vtkSmartPointer.h"
35 #include "vtkImageReslice.h"
36 #include "vtkScalarsToColors.h"
37 #include "vtkBoundedPlanePointPlacer.h"
38 #include "vtkPlane.h"
39 #include "vtkMath.h"
40 #include "vtkResliceImageViewerMeasurements.h"
41 
42 vtkStandardNewMacro(vtkResliceImageViewer);
43 
44 //----------------------------------------------------------------------------
45 // This class is used to scroll slices with the scroll bar. In the case of MPR
46 // view, it moves one "normalized spacing" in the direction of the normal to
47 // the resliced plane, provided the new center will continue to lie within the
48 // volume.
49 class vtkResliceImageViewerScrollCallback : public vtkCommand
50 {
51 public:
New()52   static vtkResliceImageViewerScrollCallback *New()
53     { return new vtkResliceImageViewerScrollCallback; }
54 
Execute(vtkObject *,unsigned long ev,void *)55   virtual void Execute(vtkObject *, unsigned long ev, void*)
56     {
57     if (!this->Viewer->GetSliceScrollOnMouseWheel())
58       {
59       return;
60       }
61 
62     // Do not process if any modifiers are ON
63     if (this->Viewer->GetInteractor()->GetShiftKey() ||
64         this->Viewer->GetInteractor()->GetControlKey() ||
65         this->Viewer->GetInteractor()->GetAltKey())
66       {
67       return;
68       }
69 
70     // forwards or backwards
71     int sign = (ev == vtkCommand::MouseWheelForwardEvent) ? 1 : -1;
72     this->Viewer->IncrementSlice(sign);
73 
74     // Abort further event processing for the scroll.
75     this->SetAbortFlag(1);
76     }
77 
vtkResliceImageViewerScrollCallback()78   vtkResliceImageViewerScrollCallback():Viewer(0) {}
79   vtkResliceImageViewer *Viewer;
80 };
81 
82 //----------------------------------------------------------------------------
vtkResliceImageViewer()83 vtkResliceImageViewer::vtkResliceImageViewer()
84 {
85   // Default is to not use the reslice cursor widget, ie use fast
86   // 3D texture mapping to display slices.
87   this->ResliceMode = vtkResliceImageViewer::RESLICE_AXIS_ALIGNED;
88 
89   // Set up the reslice cursor widget, should it be used.
90 
91   this->ResliceCursorWidget = vtkResliceCursorWidget::New();
92 
93   vtkSmartPointer< vtkResliceCursor > resliceCursor =
94     vtkSmartPointer< vtkResliceCursor >::New();
95   resliceCursor->SetThickMode(0);
96   resliceCursor->SetThickness(10, 10, 10);
97 
98   vtkSmartPointer< vtkResliceCursorLineRepresentation >
99     resliceCursorRep = vtkSmartPointer<
100       vtkResliceCursorLineRepresentation >::New();
101   resliceCursorRep->GetResliceCursorActor()->
102       GetCursorAlgorithm()->SetResliceCursor(resliceCursor);
103   resliceCursorRep->GetResliceCursorActor()->
104       GetCursorAlgorithm()->SetReslicePlaneNormal(this->SliceOrientation);
105   this->ResliceCursorWidget->SetRepresentation(resliceCursorRep);
106 
107   this->PointPlacer = vtkBoundedPlanePointPlacer::New();
108 
109   this->Measurements = vtkResliceImageViewerMeasurements::New();
110   this->Measurements->SetResliceImageViewer(this);
111 
112   this->ScrollCallback = vtkResliceImageViewerScrollCallback::New();
113   this->ScrollCallback->Viewer = this;
114   this->SliceScrollOnMouseWheel = 1;
115 
116   this->InstallPipeline();
117 }
118 
119 //----------------------------------------------------------------------------
~vtkResliceImageViewer()120 vtkResliceImageViewer::~vtkResliceImageViewer()
121 {
122   this->Measurements->Delete();
123 
124   if (this->ResliceCursorWidget)
125     {
126     this->ResliceCursorWidget->Delete();
127     this->ResliceCursorWidget = NULL;
128     }
129 
130   this->PointPlacer->Delete();
131   this->ScrollCallback->Delete();
132 }
133 
134 //----------------------------------------------------------------------------
SetThickMode(int t)135 void vtkResliceImageViewer::SetThickMode( int t )
136 {
137   vtkSmartPointer< vtkResliceCursor > rc = this->GetResliceCursor();
138 
139   if (t == this->GetThickMode())
140     {
141     return;
142     }
143 
144   vtkSmartPointer< vtkResliceCursorLineRepresentation >
145     resliceCursorRepOld = vtkResliceCursorLineRepresentation::SafeDownCast(
146                           this->ResliceCursorWidget->GetRepresentation());
147   vtkSmartPointer< vtkResliceCursorLineRepresentation > resliceCursorRepNew;
148 
149   this->GetResliceCursor()->SetThickMode(t);
150 
151   if (t)
152     {
153     resliceCursorRepNew = vtkSmartPointer<
154         vtkResliceCursorThickLineRepresentation >::New();
155     }
156   else
157     {
158     resliceCursorRepNew = vtkSmartPointer<
159         vtkResliceCursorLineRepresentation >::New();
160     }
161 
162   int e = this->ResliceCursorWidget->GetEnabled();
163   this->ResliceCursorWidget->SetEnabled(0);
164 
165   resliceCursorRepNew->GetResliceCursorActor()->
166       GetCursorAlgorithm()->SetResliceCursor(rc);
167   resliceCursorRepNew->GetResliceCursorActor()->
168       GetCursorAlgorithm()->SetReslicePlaneNormal(this->SliceOrientation);
169   this->ResliceCursorWidget->SetRepresentation(resliceCursorRepNew);
170   resliceCursorRepNew->SetLookupTable(resliceCursorRepOld->GetLookupTable());
171 
172   resliceCursorRepNew->SetWindowLevel(
173       resliceCursorRepOld->GetWindow(),
174       resliceCursorRepOld->GetLevel(), 1);
175 
176   this->ResliceCursorWidget->SetEnabled(e);
177 }
178 
179 //----------------------------------------------------------------------------
SetResliceCursor(vtkResliceCursor * rc)180 void vtkResliceImageViewer::SetResliceCursor( vtkResliceCursor * rc )
181 {
182   vtkResliceCursorRepresentation *rep =
183     vtkResliceCursorRepresentation::SafeDownCast(
184           this->GetResliceCursorWidget()->GetRepresentation());
185   rep->GetCursorAlgorithm()->SetResliceCursor(rc);
186 
187   // Rehook the observer to this reslice cursor.
188   this->Measurements->SetResliceImageViewer(this);
189 }
190 
191 //----------------------------------------------------------------------------
GetThickMode()192 int vtkResliceImageViewer::GetThickMode()
193 {
194   return (vtkResliceCursorThickLineRepresentation::
195     SafeDownCast(this->ResliceCursorWidget->GetRepresentation())) ? 1 : 0;
196 }
197 
198 //----------------------------------------------------------------------------
SetLookupTable(vtkScalarsToColors * l)199 void vtkResliceImageViewer::SetLookupTable( vtkScalarsToColors * l )
200 {
201   if (vtkResliceCursorRepresentation *rep =
202         vtkResliceCursorRepresentation::SafeDownCast(
203           this->ResliceCursorWidget->GetRepresentation()))
204     {
205     rep->SetLookupTable(l);
206     }
207 
208   if (this->WindowLevel)
209     {
210     this->WindowLevel->SetLookupTable(l);
211     this->WindowLevel->SetOutputFormatToRGBA();
212     this->WindowLevel->PassAlphaToOutputOn();
213     }
214 }
215 
216 //----------------------------------------------------------------------------
GetLookupTable()217 vtkScalarsToColors * vtkResliceImageViewer::GetLookupTable()
218 {
219   if (vtkResliceCursorRepresentation *rep =
220         vtkResliceCursorRepresentation::SafeDownCast(
221           this->ResliceCursorWidget->GetRepresentation()))
222     {
223     return rep->GetLookupTable();
224     }
225 
226   return NULL;
227 }
228 
229 //----------------------------------------------------------------------------
UpdateOrientation()230 void vtkResliceImageViewer::UpdateOrientation()
231 {
232     // Set the camera position
233 
234     vtkCamera *cam = this->Renderer ? this->Renderer->GetActiveCamera() : NULL;
235     if (cam)
236       {
237       switch (this->SliceOrientation)
238         {
239         case vtkImageViewer2::SLICE_ORIENTATION_XY:
240           cam->SetFocalPoint(0,0,0);
241           cam->SetPosition(0,0,1); // -1 if medical ?
242           cam->SetViewUp(0,1,0);
243           break;
244 
245         case vtkImageViewer2::SLICE_ORIENTATION_XZ:
246           cam->SetFocalPoint(0,0,0);
247           cam->SetPosition(0,-1,0); // 1 if medical ?
248           cam->SetViewUp(0,0,1);
249           break;
250 
251         case vtkImageViewer2::SLICE_ORIENTATION_YZ:
252           cam->SetFocalPoint(0,0,0);
253           cam->SetPosition(1,0,0); // -1 if medical ?
254           cam->SetViewUp(0,0,1);
255           break;
256         }
257       }
258 }
259 
260 //----------------------------------------------------------------------------
UpdateDisplayExtent()261 void vtkResliceImageViewer::UpdateDisplayExtent()
262 {
263   // Only update the display extent in axis aligned mode
264 
265   if (this->ResliceMode == RESLICE_AXIS_ALIGNED)
266     {
267     this->Superclass::UpdateDisplayExtent();
268     }
269 }
270 
271 //----------------------------------------------------------------------------
InstallPipeline()272 void vtkResliceImageViewer::InstallPipeline()
273 {
274   this->Superclass::InstallPipeline();
275 
276   if (this->Interactor)
277     {
278     this->ResliceCursorWidget->SetInteractor(this->Interactor);
279 
280     // Observe the scroll for slice manipulation at a higher priority
281     // than the interactor style.
282     this->Interactor->RemoveObserver(this->ScrollCallback);
283     this->Interactor->AddObserver(vtkCommand::MouseWheelForwardEvent,
284         this->ScrollCallback, 0.55 );
285     this->Interactor->AddObserver(vtkCommand::MouseWheelBackwardEvent,
286         this->ScrollCallback, 0.55 );
287     }
288 
289   if (this->Renderer)
290     {
291     this->ResliceCursorWidget->SetDefaultRenderer(this->Renderer);
292     vtkCamera *cam = this->Renderer->GetActiveCamera();
293     cam->ParallelProjectionOn();
294     }
295 
296   if (this->ResliceMode == RESLICE_OBLIQUE)
297     {
298     this->ResliceCursorWidget->SetEnabled(1);
299     this->ImageActor->SetVisibility(0);
300     this->UpdateOrientation();
301 
302     double bounds[6] = {0, 1, 0, 1, 0, 1};
303 
304     vtkCamera *cam = this->Renderer->GetActiveCamera();
305     double onespacing[3] = {1, 1, 1};
306     double *spacing = onespacing;
307     if (this->GetResliceCursor()->GetImage())
308       {
309       this->GetResliceCursor()->GetImage()->GetBounds(bounds);
310       spacing = this->GetResliceCursor()->GetImage()->GetSpacing();
311       }
312     double avg_spacing =
313       (spacing[0] + spacing[1] + spacing[2]) / 3.0;
314     cam->SetClippingRange(
315       bounds[this->SliceOrientation * 2] - 100 * avg_spacing,
316       bounds[this->SliceOrientation * 2 + 1] + 100 * avg_spacing);
317     }
318   else
319     {
320     this->ResliceCursorWidget->SetEnabled(0);
321     this->ImageActor->SetVisibility(1);
322     this->UpdateOrientation();
323     }
324 
325   if (this->WindowLevel)
326     {
327     this->WindowLevel->SetLookupTable(this->GetLookupTable());
328     }
329 }
330 
331 //----------------------------------------------------------------------------
UnInstallPipeline()332 void vtkResliceImageViewer::UnInstallPipeline()
333 {
334   this->ResliceCursorWidget->SetEnabled(0);
335 
336   if (this->Interactor)
337     {
338     this->Interactor->RemoveObserver(this->ScrollCallback);
339     }
340 
341   this->Superclass::UnInstallPipeline();
342 }
343 
344 //----------------------------------------------------------------------------
UpdatePointPlacer()345 void vtkResliceImageViewer::UpdatePointPlacer()
346 {
347   if (this->ResliceMode == RESLICE_OBLIQUE)
348     {
349     this->PointPlacer->SetProjectionNormalToOblique();
350     if (vtkResliceCursorRepresentation *rep =
351         vtkResliceCursorRepresentation::SafeDownCast(
352           this->ResliceCursorWidget->GetRepresentation()))
353       {
354       const int planeOrientation =
355         rep->GetCursorAlgorithm()->GetReslicePlaneNormal();
356       vtkPlane *plane = this->GetResliceCursor()->GetPlane(planeOrientation);
357       this->PointPlacer->SetObliquePlane(plane);
358       }
359     }
360   else
361     {
362 
363     if (!this->WindowLevel->GetInput())
364       {
365       return;
366       }
367 
368     vtkImageData *input = this->ImageActor->GetInput();
369     if ( !input )
370       {
371       return;
372       }
373 
374     double spacing[3];
375     input->GetSpacing(spacing);
376 
377     double origin[3];
378     input->GetOrigin(origin);
379 
380     double bounds[6];
381     this->ImageActor->GetBounds(bounds);
382 
383     int displayExtent[6];
384     this->ImageActor->GetDisplayExtent(displayExtent);
385 
386     int axis = vtkBoundedPlanePointPlacer::XAxis;
387     double position = 0.0;
388     if ( displayExtent[0] == displayExtent[1] )
389       {
390       axis = vtkBoundedPlanePointPlacer::XAxis;
391       position = origin[0] + displayExtent[0]*spacing[0];
392       }
393     else if ( displayExtent[2] == displayExtent[3] )
394       {
395       axis = vtkBoundedPlanePointPlacer::YAxis;
396       position = origin[1] + displayExtent[2]*spacing[1];
397       }
398     else if ( displayExtent[4] == displayExtent[5] )
399       {
400       axis = vtkBoundedPlanePointPlacer::ZAxis;
401       position = origin[2] + displayExtent[4]*spacing[2];
402       }
403 
404     this->PointPlacer->SetProjectionNormal(axis);
405     this->PointPlacer->SetProjectionPosition(position);
406     }
407 
408 }
409 
410 //----------------------------------------------------------------------------
Render()411 void vtkResliceImageViewer::Render()
412 {
413   if (!this->WindowLevel->GetInput())
414     {
415     return;
416     }
417 
418   this->UpdatePointPlacer();
419 
420   this->Superclass::Render();
421 }
422 
423 //----------------------------------------------------------------------------
GetResliceCursor()424 vtkResliceCursor * vtkResliceImageViewer::GetResliceCursor()
425 {
426   if (vtkResliceCursorRepresentation *rep =
427         vtkResliceCursorRepresentation::SafeDownCast(
428           this->ResliceCursorWidget->GetRepresentation()))
429     {
430     return rep->GetResliceCursor();
431     }
432 
433   return NULL;
434 }
435 
436 //----------------------------------------------------------------------------
SetInputData(vtkImageData * in)437 void vtkResliceImageViewer::SetInputData(vtkImageData *in)
438 {
439   if(!in)
440     {
441     return;
442     }
443 
444   this->WindowLevel->SetInputData(in);
445   this->GetResliceCursor()->SetImage(in);
446   this->GetResliceCursor()->SetCenter(in->GetCenter());
447   this->UpdateDisplayExtent();
448 
449   double range[2];
450   in->GetScalarRange(range);
451   if (vtkResliceCursorRepresentation *rep =
452         vtkResliceCursorRepresentation::SafeDownCast(
453           this->ResliceCursorWidget->GetRepresentation()))
454     {
455     if (vtkImageReslice *reslice =
456         vtkImageReslice::SafeDownCast(rep->GetReslice()))
457       {
458       // default background color is the min value of the image scalar range
459       reslice->SetBackgroundColor(range[0],range[0],range[0],range[0]);
460       this->SetColorWindow(range[1]-range[0]);
461       this->SetColorLevel((range[0]+range[1])/2.0);
462       }
463     }
464 }
465 
466 //----------------------------------------------------------------------------
SetInputConnection(vtkAlgorithmOutput * input)467 void vtkResliceImageViewer::SetInputConnection(vtkAlgorithmOutput* input)
468 {
469   vtkErrorMacro( << "Use SetInputData instead. " );
470   this->WindowLevel->SetInputConnection(input);
471   this->UpdateDisplayExtent();
472 }
473 
474 //----------------------------------------------------------------------------
SetResliceMode(int r)475 void vtkResliceImageViewer::SetResliceMode( int r )
476 {
477   if (r == this->ResliceMode)
478     {
479     return;
480     }
481 
482   this->ResliceMode = r;
483   this->Modified();
484 
485   this->InstallPipeline();
486 }
487 
488 //----------------------------------------------------------------------------
SetColorWindow(double w)489 void vtkResliceImageViewer::SetColorWindow( double w )
490 {
491   double rmin = this->GetColorLevel() - 0.5*fabs( w );
492   double rmax = rmin + fabs( w );
493   this->GetLookupTable()->SetRange( rmin, rmax );
494 
495   this->WindowLevel->SetWindow(w);
496   if (vtkResliceCursorRepresentation *rep =
497         vtkResliceCursorRepresentation::SafeDownCast(
498           this->ResliceCursorWidget->GetRepresentation()))
499     {
500     rep->SetWindowLevel(w, rep->GetLevel(), 1);
501     }
502 }
503 
504 //----------------------------------------------------------------------------
SetColorLevel(double w)505 void vtkResliceImageViewer::SetColorLevel( double w )
506 {
507   double rmin = w - 0.5*fabs( this->GetColorWindow() );
508   double rmax = rmin + fabs( this->GetColorWindow() );
509   this->GetLookupTable()->SetRange( rmin, rmax );
510 
511   this->WindowLevel->SetLevel(w);
512   if (vtkResliceCursorRepresentation *rep =
513         vtkResliceCursorRepresentation::SafeDownCast(
514           this->ResliceCursorWidget->GetRepresentation()))
515     {
516     rep->SetWindowLevel(rep->GetWindow(), w, 1);
517     }
518 }
519 
520 //----------------------------------------------------------------------------
Reset()521 void vtkResliceImageViewer::Reset()
522 {
523   this->ResliceCursorWidget->ResetResliceCursor();
524 }
525 
526 //----------------------------------------------------------------------------
GetReslicePlane()527 vtkPlane * vtkResliceImageViewer::GetReslicePlane()
528 {
529   // Get the reslice plane
530   if (vtkResliceCursorRepresentation *rep =
531       vtkResliceCursorRepresentation::SafeDownCast(
532         this->ResliceCursorWidget->GetRepresentation()))
533     {
534     const int planeOrientation =
535       rep->GetCursorAlgorithm()->GetReslicePlaneNormal();
536     vtkPlane *plane = this->GetResliceCursor()->GetPlane(planeOrientation);
537     return plane;
538     }
539 
540   return NULL;
541 }
542 
543 //----------------------------------------------------------------------------
GetInterSliceSpacingInResliceMode()544 double vtkResliceImageViewer::GetInterSliceSpacingInResliceMode()
545 {
546   double n[3], imageSpacing[3], resliceSpacing = 0;
547 
548   if (vtkPlane *plane = this->GetReslicePlane())
549     {
550     plane->GetNormal(n);
551     this->GetResliceCursor()->GetImage()->GetSpacing(imageSpacing);
552     resliceSpacing = fabs(vtkMath::Dot(n, imageSpacing));
553     }
554 
555   return resliceSpacing;
556 }
557 
558 //----------------------------------------------------------------------------
IncrementSlice(int inc)559 void vtkResliceImageViewer::IncrementSlice( int inc )
560 {
561   if (this->GetResliceMode() ==
562       vtkResliceImageViewer::RESLICE_AXIS_ALIGNED)
563     {
564     int oldSlice = this->GetSlice();
565     this->SetSlice(this->GetSlice() + inc);
566     if (this->GetSlice() != oldSlice)
567       {
568       this->InvokeEvent( vtkResliceImageViewer::SliceChangedEvent, NULL );
569       this->InvokeEvent( vtkCommand::InteractionEvent, NULL );
570       }
571     }
572   else
573     {
574     if (vtkPlane *p = this->GetReslicePlane())
575       {
576       double n[3], c[3], bounds[6];
577       p->GetNormal(n);
578       const double spacing =
579           this->GetInterSliceSpacingInResliceMode() * inc;
580       this->GetResliceCursor()->GetCenter(c);
581       vtkMath::MultiplyScalar(n, spacing);
582       c[0] += n[0];
583       c[1] += n[1];
584       c[2] += n[2];
585 
586       // If the new center is inside, put it there...
587       if (vtkImageData *image = this->GetResliceCursor()->GetImage())
588         {
589         image->GetBounds(bounds);
590         if (c[0] >= bounds[0] && c[0] <= bounds[1] &&
591             c[1] >= bounds[2] && c[1] <= bounds[3] &&
592             c[2] >= bounds[4] && c[2] <= bounds[5])
593           {
594           this->GetResliceCursor()->SetCenter(c);
595 
596           this->InvokeEvent( vtkResliceImageViewer::SliceChangedEvent, NULL );
597           this->InvokeEvent( vtkCommand::InteractionEvent, NULL );
598           }
599         }
600       }
601     }
602 }
603 
604 //----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)605 void vtkResliceImageViewer::PrintSelf(ostream& os, vtkIndent indent)
606 {
607   this->Superclass::PrintSelf(os, indent);
608 
609   os << indent << "ResliceCursorWidget:\n";
610   this->ResliceCursorWidget->PrintSelf(os,indent.GetNextIndent());
611   os << indent << "ResliceMode: " << this->ResliceMode << endl;
612   os << indent << "SliceScrollOnMouseWheel: " << this->SliceScrollOnMouseWheel << endl;
613   os << indent << "Point Placer: ";
614   this->PointPlacer->PrintSelf(os,indent.GetNextIndent());
615   os << indent << "Measurements: ";
616   this->Measurements->PrintSelf(os,indent.GetNextIndent());
617   os << indent << "Interactor: " << this->Interactor << "\n";
618   if (this->Interactor)
619     {
620     this->Interactor->PrintSelf(os,indent.GetNextIndent());
621     }
622 }
623