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 void Execute(vtkObject *, unsigned long ev, void*) override
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(nullptr) {}
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 = nullptr;
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 nullptr;
227 }
228
229 //----------------------------------------------------------------------------
UpdateOrientation()230 void vtkResliceImageViewer::UpdateOrientation()
231 {
232 // Set the camera position
233
234 vtkCamera *cam = this->Renderer ? this->Renderer->GetActiveCamera() : nullptr;
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 nullptr;
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 nullptr;
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, nullptr );
569 this->InvokeEvent( vtkCommand::InteractionEvent, nullptr );
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, nullptr );
597 this->InvokeEvent( vtkCommand::InteractionEvent, nullptr );
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