]> Creatis software - cpPlugins.git/blob - lib/cpExtensions/Visualization/ImageSliceActors.cxx
merge async example
[cpPlugins.git] / lib / cpExtensions / Visualization / ImageSliceActors.cxx
1 #include <cpExtensions/Visualization/ImageSliceActors.h>
2
3 #include <cmath>
4 #include <sstream>
5
6 #include <vtkAlgorithmOutput.h>
7 #include <vtkCamera.h>
8 #include <vtkCellArray.h>
9 #include <vtkImageData.h>
10 #include <vtkInformation.h>
11 #include <vtkPlane.h>
12 #include <vtkPoints.h>
13 #include <vtkProperty.h>
14 #include <vtkRenderer.h>
15 #include <vtkRendererCollection.h>
16 #include <vtkRenderWindow.h>
17 #include <vtkRenderWindowInteractor.h>
18 #include <vtkStreamingDemandDrivenPipeline.h>
19 #include <vtkTextProperty.h>
20 #include <vtkWindowLevelLookupTable.h>
21
22 // -------------------------------------------------------------------------
23 cpExtensions::Visualization::ImageSliceActors*
24 cpExtensions::Visualization::ImageSliceActors::
25 New( )
26 {
27   return( new Self( ) );
28 }
29
30 // -------------------------------------------------------------------------
31 void cpExtensions::Visualization::ImageSliceActors::
32 AddInputConnection( vtkAlgorithmOutput* aout, int axis )
33 {
34   vtkImageData* data = dynamic_cast< vtkImageData* >(
35     aout->GetProducer( )->GetOutputDataObject( aout->GetIndex( ) )
36     );
37   if( data == NULL )
38     return;
39
40   vtkImageMapToColors* new_map =
41     dynamic_cast< vtkImageMapToColors* >( aout->GetProducer( ) );
42   if( new_map == NULL )
43   {
44     // Configure LUT
45     this->_ConfigureNewLUT( data );
46     new_map = *( this->ImageMaps.rbegin( ) );
47     if( new_map != NULL )
48     {
49       new_map->SetInputConnection( aout );
50       new_map->Update( );
51
52     } // fi
53   }
54   else
55     this->ImageMaps.push_back( new_map );
56   
57   // Create mapper and actors
58   vtkSmartPointer< vtkImageSliceMapper > mapper =
59     vtkSmartPointer< vtkImageSliceMapper >::New( );
60   if( new_map != NULL )
61     mapper->SetInputConnection( new_map->GetOutputPort( ) );
62   else
63     mapper->SetInputConnection( aout );
64   this->SliceMappers.push_back( mapper );
65   this->_ConfigureNewInput( axis );
66 }
67
68 // -------------------------------------------------------------------------
69 void cpExtensions::Visualization::ImageSliceActors::
70 AddInputData( vtkImageData* data, int axis )
71 {
72   // Configure LUT
73   this->_ConfigureNewLUT( data );
74   vtkImageMapToColors* new_map = *( this->ImageMaps.rbegin( ) );
75   if( new_map != NULL )
76   {
77     new_map->SetInputData( data );
78     new_map->Update( );
79
80   } // fi
81
82   // Create mapper and actors
83   vtkSmartPointer< vtkImageSliceMapper > mapper =
84     vtkSmartPointer< vtkImageSliceMapper >::New( );
85   if( new_map != NULL )
86     mapper->SetInputConnection( new_map->GetOutputPort( ) );
87   else
88     mapper->SetInputData( data );
89   this->SliceMappers.push_back( mapper );
90   this->_ConfigureNewInput( axis );
91 }
92
93 // -------------------------------------------------------------------------
94 void cpExtensions::Visualization::ImageSliceActors::
95 Clear( )
96 {
97   // Unbind from container
98   this->RemoveAllItems( );
99
100   // Delete all images
101   this->ImageMaps.clear( );
102   this->SliceMappers.clear( );
103   this->ImageActors.clear( );
104   this->AssociatedSlices.clear( );
105   this->AssociatedActors.clear( );
106
107   // Reconfigure unique objects
108   this->Cursor        = vtkSmartPointer< vtkPolyData >::New( );
109   this->CursorMapper  = vtkSmartPointer< vtkPolyDataMapper >::New( );
110   this->CursorActor   = vtkSmartPointer< vtkActor >::New( );
111   this->PlaneFunction = vtkSmartPointer< vtkPlane >::New( );
112   this->Plane         = vtkSmartPointer< vtkPolyData >::New( );
113   this->PlaneMapper   = vtkSmartPointer< vtkPolyDataMapper >::New( );
114   this->TextActor     = vtkSmartPointer< vtkTextActor >::New( );
115   this->PlaneActor    = vtkSmartPointer< vtkActor >::New( );
116   this->TextBuffer[ 0 ] = '\0';
117
118   // Unique objects configuration
119   vtkSmartPointer< vtkPoints > cursor_points =
120     vtkSmartPointer< vtkPoints >::New( );
121   vtkSmartPointer< vtkCellArray > cursor_lines =
122     vtkSmartPointer< vtkCellArray >::New( );
123   cursor_points->InsertNextPoint( 0, 0, 0 );
124   cursor_points->InsertNextPoint( 0, 0, 0 );
125   cursor_points->InsertNextPoint( 0, 0, 0 );
126   cursor_points->InsertNextPoint( 0, 0, 0 );
127   cursor_points->InsertNextPoint( 0, 0, 0 );
128   cursor_points->InsertNextPoint( 0, 0, 0 );
129   cursor_points->InsertNextPoint( 0, 0, 0 );
130   cursor_points->InsertNextPoint( 0, 0, 0 );
131   cursor_lines->InsertNextCell( 2 );
132   cursor_lines->InsertCellPoint( 0 );
133   cursor_lines->InsertCellPoint( 1 );
134   cursor_lines->InsertNextCell( 2 );
135   cursor_lines->InsertCellPoint( 2 );
136   cursor_lines->InsertCellPoint( 3 );
137   cursor_lines->InsertNextCell( 2 );
138   cursor_lines->InsertCellPoint( 4 );
139   cursor_lines->InsertCellPoint( 5 );
140   cursor_lines->InsertNextCell( 2 );
141   cursor_lines->InsertCellPoint( 6 );
142   cursor_lines->InsertCellPoint( 7 );
143   this->Cursor->SetPoints( cursor_points );
144   this->Cursor->SetLines( cursor_lines );
145   this->CursorMapper->SetInputData( this->Cursor );
146   this->CursorActor->SetMapper( this->CursorMapper );
147
148   vtkSmartPointer< vtkPoints > plane_points =
149     vtkSmartPointer< vtkPoints >::New( );
150   vtkSmartPointer< vtkCellArray > plane_lines =
151     vtkSmartPointer< vtkCellArray >::New( );
152
153   plane_points->InsertNextPoint( 0, 0, 0 );
154   plane_points->InsertNextPoint( 0, 1, 0 );
155   plane_points->InsertNextPoint( 1, 1, 0 );
156   plane_points->InsertNextPoint( 1, 0, 0 );
157   plane_lines->InsertNextCell( 5 );
158   plane_lines->InsertCellPoint( 0 );
159   plane_lines->InsertCellPoint( 1 );
160   plane_lines->InsertCellPoint( 2 );
161   plane_lines->InsertCellPoint( 3 );
162   plane_lines->InsertCellPoint( 0 );
163   this->Plane->SetPoints( plane_points );
164   this->Plane->SetLines( plane_lines );
165
166   this->PlaneMapper->SetInputData( this->Plane );
167   this->PlaneActor->SetMapper( this->PlaneMapper );
168
169   this->TextActor->SetTextScaleModeToNone( );
170   vtkTextProperty* textprop = this->TextActor->GetTextProperty( );
171   textprop->SetColor( 1, 1, 1 );
172   textprop->SetFontFamilyToCourier( );
173   textprop->SetFontSize( 18 );
174   textprop->BoldOff( );
175   textprop->ItalicOff( );
176   textprop->ShadowOff( );
177   textprop->SetJustificationToLeft( );
178   textprop->SetVerticalJustificationToBottom( );
179   vtkCoordinate* coord = this->TextActor->GetPositionCoordinate( );
180   coord->SetCoordinateSystemToNormalizedViewport( );
181   coord->SetValue( 0.01, 0.01 );
182 }
183
184 // -------------------------------------------------------------------------
185 void cpExtensions::Visualization::ImageSliceActors::
186 AssociateSlice( Self* other )
187 {
188   this->AssociatedSlices.push_back( other );
189   this->Modified( );
190 }
191
192 // -------------------------------------------------------------------------
193 void cpExtensions::Visualization::ImageSliceActors::
194 SetSlicesCommand( TCursorCommand cmd, void* data )
195 {
196   this->SlicesCommand = cmd;
197   this->SlicesData = data;
198   this->Modified( );
199 }
200
201 // -------------------------------------------------------------------------
202 vtkInteractorStyle* cpExtensions::Visualization::ImageSliceActors::
203 GetStyle( )
204 {
205   return( this->Style.GetPointer( ) );
206 }
207
208 // -------------------------------------------------------------------------
209 const vtkInteractorStyle* cpExtensions::Visualization::ImageSliceActors::
210 GetStyle( ) const
211 {
212   return( this->Style.GetPointer( ) );
213 }
214
215 // -------------------------------------------------------------------------
216 void cpExtensions::Visualization::ImageSliceActors::
217 PushActorsInto( vtkRenderWindow* window, bool force_style )
218 {
219   if( window == NULL )
220     return;
221   vtkRenderWindowInteractor* rwi = window->GetInteractor( );
222   vtkRenderer* renderer = window->GetRenderers( )->GetFirstRenderer( );
223   if( rwi == NULL || renderer == NULL )
224     return;
225   
226   // Update style
227   if( force_style )
228     if( rwi->GetInteractorStyle( ) != this->Style.GetPointer( ) )
229       rwi->SetInteractorStyle( this->Style );
230
231   // Update actors
232   unsigned int N = this->GetNumberOfImageActors( );
233   for( unsigned int n = 0; n < N; ++n )
234     renderer->AddActor( this->GetImageActor( n ) );
235   renderer->AddActor( this->CursorActor );
236   renderer->AddActor( this->PlaneActor );
237   renderer->AddActor( this->TextActor );
238   renderer->Modified( );
239
240   // Configure camera
241   vtkCamera* camera = renderer->GetActiveCamera( );
242   if( camera == NULL )
243     return;
244
245   // Parallel projections are better when displaying 2D images
246   if( force_style )
247   {
248     int axis = this->GetAxis( );
249     camera->ParallelProjectionOn( );
250     camera->SetFocalPoint( double( 0 ), double( 0 ), double( 0 ) );
251     if( axis == 0 )
252     {
253       camera->SetPosition( double( 1 ), double( 0 ), double( 0 ) );
254       camera->SetViewUp  ( double( 0 ), double( 1 ), double( 0 ) );
255     }
256     else if( axis == 1 )
257     {
258       camera->SetPosition( double( 0 ), double( 1 ), double(  0 ) );
259       camera->SetViewUp  ( double( 0 ), double( 0 ), double( -1 ) );
260     }
261     else // if( axis == 2 )
262     {
263       camera->SetPosition( double( 0 ), double( 0 ), double( 1 ) );
264       camera->SetViewUp  ( double( 0 ), double( 1 ), double( 0 ) );
265
266     } // fi
267
268   } // fi
269   renderer->ResetCamera( );
270   rwi->Render( );
271 }
272
273 // -------------------------------------------------------------------------
274 void cpExtensions::Visualization::ImageSliceActors::
275 PopActorsFrom( vtkRenderWindow* window )
276 {
277   vtkRenderWindowInteractor* rwi = window->GetInteractor( );
278   vtkRenderer* renderer = window->GetRenderers( )->GetFirstRenderer( );
279
280   if( renderer != NULL )
281   {
282     // Update actors
283     unsigned int N = this->GetNumberOfImageActors( );
284     for( unsigned int n = 0; n < N; ++n )
285       renderer->RemoveActor( this->GetImageActor( n ) );
286     renderer->RemoveActor( this->CursorActor );
287     renderer->RemoveActor( this->PlaneActor );
288     renderer->RemoveActor( this->TextActor );
289     renderer->Modified( );
290
291   } // fi
292 }
293
294 // -------------------------------------------------------------------------
295 unsigned int cpExtensions::Visualization::ImageSliceActors::
296 GetNumberOfImageActors( ) const
297 {
298   return( this->ImageActors.size( ) );
299 }
300
301 // -------------------------------------------------------------------------
302 vtkImageActor* cpExtensions::Visualization::ImageSliceActors::
303 GetImageActor( unsigned int id )
304 {
305   if( id < this->ImageActors.size( ) )
306     return( this->ImageActors[ id ] );
307   else
308     return( NULL );
309 }
310
311 // -------------------------------------------------------------------------
312 const vtkImageActor* cpExtensions::Visualization::ImageSliceActors::
313 GetImageActor( unsigned int id ) const
314 {
315   if( id < this->ImageActors.size( ) )
316     return( this->ImageActors[ id ] );
317   else
318     return( NULL );
319 }
320
321 // -------------------------------------------------------------------------
322 vtkTextActor* cpExtensions::Visualization::ImageSliceActors::
323 GetTextActor( )
324 {
325   return( this->TextActor );
326 }
327
328 // -------------------------------------------------------------------------
329 const vtkTextActor* cpExtensions::Visualization::ImageSliceActors::
330 GetTextActor( ) const
331 {
332   return( this->TextActor );
333 }
334
335 // -------------------------------------------------------------------------
336 vtkActor* cpExtensions::Visualization::ImageSliceActors::
337 GetPlaneActor( )
338 {
339   return( this->PlaneActor );
340 }
341
342 // -------------------------------------------------------------------------
343 const vtkActor* cpExtensions::Visualization::ImageSliceActors::
344 GetPlaneActor( ) const
345 {
346   return( this->PlaneActor );
347 }
348
349 // -------------------------------------------------------------------------
350 vtkPlane* cpExtensions::Visualization::ImageSliceActors::
351 GetPlaneFunction( )
352 {
353   return( this->PlaneFunction );
354 }
355
356 // -------------------------------------------------------------------------
357 const vtkPlane* cpExtensions::Visualization::ImageSliceActors::
358 GetPlaneFunction( ) const
359 {
360   return( this->PlaneFunction );
361 }
362
363 // -------------------------------------------------------------------------
364 void cpExtensions::Visualization::ImageSliceActors::
365 AddActor( vtkAlgorithm* algorithm, vtkActor* actor )
366 {
367   this->AssociatedActors.push_back( TAssociatedActor( algorithm, actor ) );
368   this->AddItem( actor );
369 }
370
371 // -------------------------------------------------------------------------
372 void cpExtensions::Visualization::ImageSliceActors::
373 AddActor( vtkActor* actor )
374 {
375   this->AddActor( NULL, actor );
376 }
377
378 // -------------------------------------------------------------------------
379 void cpExtensions::Visualization::ImageSliceActors::
380 SetInterpolate( bool v )
381 {
382   if( this->Interpolate != v )
383   {
384     for( unsigned int i = 0; i < this->ImageActors.size( ); ++i )
385       this->ImageActors[ i ]->SetInterpolate( v );
386     this->Interpolate = v;
387     this->Modified( );
388
389   } // fi
390 }
391
392 // -------------------------------------------------------------------------
393 void cpExtensions::Visualization::ImageSliceActors::
394 InterpolateOn( )
395 {
396   this->SetInterpolate( true );
397 }
398
399 // -------------------------------------------------------------------------
400 void cpExtensions::Visualization::ImageSliceActors::
401 InterpolateOff( )
402 {
403   this->SetInterpolate( false );
404 }
405
406 // -------------------------------------------------------------------------
407 double* cpExtensions::Visualization::ImageSliceActors::
408 GetDisplayBounds( ) const
409 {
410   if( this->ImageActors.size( ) > 0 )
411     return( this->ImageActors[ 0 ]->GetDisplayBounds( ) );
412   else
413     return( NULL );
414 }
415
416 // -------------------------------------------------------------------------
417 void cpExtensions::Visualization::ImageSliceActors::
418 GetDisplayBounds( double bounds[ 6 ] ) const
419 {
420   if( this->ImageActors.size( ) == 0 )
421   {
422     bounds[ 0 ] = bounds[ 2 ] = bounds[ 4 ] = double( -1 );
423     bounds[ 1 ] = bounds[ 3 ] = bounds[ 5 ] = double( -1 );
424   }
425   else
426     this->ImageActors[ 0 ]->GetDisplayBounds( bounds );
427 }
428
429 // -------------------------------------------------------------------------
430 void cpExtensions::Visualization::ImageSliceActors::
431 ResetCursor( )
432 {
433   vtkPoints* points = this->Cursor->GetPoints( );
434   points->SetPoint( 0, 0, 0, 0 );
435   points->SetPoint( 1, 0, 0, 0 );
436   points->SetPoint( 2, 0, 0, 0 );
437   points->SetPoint( 3, 0, 0, 0 );
438   points->SetPoint( 4, 0, 0, 0 );
439   points->SetPoint( 5, 0, 0, 0 );
440   points->SetPoint( 6, 0, 0, 0 );
441   points->SetPoint( 7, 0, 0, 0 );
442   this->Cursor->Modified( );
443   this->CursorMapper->Modified( );
444   this->CursorActor->Modified( );
445 }
446
447 // -------------------------------------------------------------------------
448 void cpExtensions::Visualization::ImageSliceActors::
449 SetCursor( double pos[ 3 ] )
450 {
451   if( this->SliceMappers.size( ) == 0 )
452     return;
453
454   // Get ordered axes
455   int a0 = this->GetAxis( );
456   int a1 = ( a0 + 1 ) % 3;
457   int a2 = ( a0 + 2 ) % 3;
458   int ma0 = a0 << 1;
459   int ma1 = a1 << 1;
460   int ma2 = a2 << 1;
461
462   double bounds[ 6 ];
463   this->SliceMappers[ 0 ]->GetInput( )->GetBounds( bounds );
464
465   double
466     p0[ 3 ], p1[ 3 ], p2[ 3 ], p3[ 3 ],
467     p4[ 3 ], p5[ 3 ], p6[ 3 ], p7[ 3 ];
468
469   p0[ a2 ] = p1[ a2 ] = p4[ a2 ] = p5[ a2 ] = pos[ a2 ];
470   p2[ a1 ] = p3[ a1 ] = p6[ a1 ] = p7[ a1 ] = pos[ a1 ];
471   p0[ a0 ] = p1[ a0 ] = p2[ a0 ] = p3[ a0 ] = bounds[ ma0 ];
472   p4[ a0 ] = p5[ a0 ] = p6[ a0 ] = p7[ a0 ] = bounds[ ma0 + 1 ];
473   p0[ a1 ] = p4[ a1 ] = bounds[ ma1 ];
474   p1[ a1 ] = p5[ a1 ] = bounds[ ma1 + 1 ];
475   p2[ a2 ] = p6[ a2 ] = bounds[ ma2 ];
476   p3[ a2 ] = p7[ a2 ] = bounds[ ma2 + 1 ];
477
478   vtkPoints* points = this->Cursor->GetPoints( );
479   points->SetPoint( 0, p0 );
480   points->SetPoint( 1, p1 );
481   points->SetPoint( 2, p2 );
482   points->SetPoint( 3, p3 );
483   points->SetPoint( 4, p4 );
484   points->SetPoint( 5, p5 );
485   points->SetPoint( 6, p6 );
486   points->SetPoint( 7, p7 );
487   this->Cursor->Modified( );
488   this->CursorMapper->Modified( );
489   this->CursorActor->Modified( );
490 }
491
492 // -------------------------------------------------------------------------
493 vtkImageMapToColors* cpExtensions::Visualization::ImageSliceActors::
494 GetImageMap( unsigned int id )
495 {
496   if( id < this->ImageMaps.size( ) )
497     return( this->ImageMaps[ id ].GetPointer( ) );
498   else
499     return( NULL );
500 }
501
502 // -------------------------------------------------------------------------
503 const vtkImageMapToColors* cpExtensions::Visualization::ImageSliceActors::
504 GetImageMap( unsigned int id ) const
505 {
506   if( id < this->ImageMaps.size( ) )
507     return( this->ImageMaps[ id ].GetPointer( ) );
508   else
509     return( NULL );
510 }
511
512 // -------------------------------------------------------------------------
513 double cpExtensions::Visualization::ImageSliceActors::
514 GetWindow( ) const
515 {
516   if( this->ImageMaps.size( ) > 0 )
517   {
518     if( this->ImageMaps[ 0 ].GetPointer( ) != NULL )
519     {
520       vtkWindowLevelLookupTable* lut =
521         dynamic_cast< vtkWindowLevelLookupTable* >(
522           this->ImageMaps[ 0 ]->GetLookupTable( )
523           );
524       if( lut != NULL )
525         return( lut->GetWindow( ) );
526       else
527         return( double( 0 ) );
528     }
529     else
530       return( double( 0 ) );
531   }
532   else
533     return( double( 0 ) );
534 }
535
536 // -------------------------------------------------------------------------
537 double cpExtensions::Visualization::ImageSliceActors::
538 GetLevel( ) const
539 {
540   if( this->ImageMaps.size( ) > 0 )
541   {
542     if( this->ImageMaps[ 0 ].GetPointer( ) != NULL )
543     {
544       vtkWindowLevelLookupTable* lut =
545         dynamic_cast< vtkWindowLevelLookupTable* >(
546           this->ImageMaps[ 0 ]->GetLookupTable( )
547           );
548       if( lut != NULL )
549         return( lut->GetLevel( ) );
550       else
551         return( double( 0 ) );
552     }
553     else
554       return( double( 0 ) );
555   }
556   else
557     return( double( 0 ) );
558 }
559
560 // -------------------------------------------------------------------------
561 void cpExtensions::Visualization::ImageSliceActors::
562 SetWindow( double w )
563 {
564   if( this->ImageMaps.size( ) > 0 )
565   {
566     if( this->ImageMaps[ 0 ].GetPointer( ) != NULL )
567     {
568       vtkWindowLevelLookupTable* lut =
569         dynamic_cast< vtkWindowLevelLookupTable* >(
570           this->ImageMaps[ 0 ]->GetLookupTable( )
571           );
572       if( lut != NULL )
573       {
574         /* TODO: Min and Max values are assigned 0!!!
575            if( w < this->MinWindow )
576            w = this->MinWindow;
577            if( w > this->MaxWindow )
578            w = this->MaxWindow;
579         */
580         lut->SetWindow( w );
581         lut->Build( );
582         this->ImageMaps[ 0 ]->Modified( );
583         this->Modified( );
584
585       } // fi
586
587     } // fi
588
589   } // fi
590 }
591
592 // -------------------------------------------------------------------------
593 void cpExtensions::Visualization::ImageSliceActors::
594 SetLevel( double l )
595 {
596   if( this->ImageMaps.size( ) > 0 )
597   {
598     if( this->ImageMaps[ 0 ].GetPointer( ) != NULL )
599     {
600       vtkWindowLevelLookupTable* lut =
601         dynamic_cast< vtkWindowLevelLookupTable* >(
602           this->ImageMaps[ 0 ]->GetLookupTable( )
603           );
604       if( lut != NULL )
605       {
606         /* TODO: Min and Max values are assigned 0!!!
607            if( l < this->MinLevel )
608            l = this->MinLevel;
609            if( l > this->MaxLevel )
610            l = this->MaxLevel;
611         */
612         lut->SetLevel( l );
613         lut->Build( );
614         this->ImageMaps[ 0 ]->Modified( );
615         this->Modified( );
616
617       } // fi
618
619     } // fi
620
621   } // fi
622 }
623
624 // -------------------------------------------------------------------------
625 void cpExtensions::Visualization::ImageSliceActors::
626 SetWindowLevel( double w, double l )
627 {
628   if( this->ImageMaps.size( ) > 0 )
629   {
630     if( this->ImageMaps[ 0 ].GetPointer( ) != NULL )
631     {
632       vtkWindowLevelLookupTable* lut =
633         dynamic_cast< vtkWindowLevelLookupTable* >(
634           this->ImageMaps[ 0 ]->GetLookupTable( )
635           );
636       if( lut != NULL )
637       {
638         /* TODO: Min and Max values are assigned 0!!!
639            std::cout << this->MinWindow << " " << this->MaxWindow << std::endl;
640            std::cout << this->MinLevel << " " << this->MaxLevel << std::endl;
641            std::cout << w << " " << l << " <-> " << std::ends;
642            if( w < this->MinWindow )
643            w = this->MinWindow;
644            if( w > this->MaxWindow )
645            w = this->MaxWindow;
646            if( l < this->MinLevel )
647            l = this->MinLevel;
648            if( l > this->MaxLevel )
649            l = this->MaxLevel;
650            std::cout << w << " " << l << std::endl;
651         */
652         lut->SetWindow( w );
653         lut->SetLevel( l );
654         lut->Build( );
655         this->ImageMaps[ 0 ]->Modified( );
656         this->Modified( );
657
658       } // fi
659
660     } // fi
661
662   } // fi
663 }
664
665 // -------------------------------------------------------------------------
666 void cpExtensions::Visualization::ImageSliceActors::
667 ResetWindowLevel( )
668 {
669   this->SetWindowLevel(
670     this->MaxWindow,
671     ( this->MaxLevel + this->MinLevel ) / double( 2 )
672     );
673 }
674
675 // -------------------------------------------------------------------------
676 void cpExtensions::Visualization::ImageSliceActors::
677 SetLookupTable( unsigned int id, vtkLookupTable* lut )
678 {
679   if( id < this->ImageMaps.size( ) && id > 0 )
680   {
681     if( this->ImageMaps[ id ].GetPointer( ) != NULL )
682     {
683       this->ImageMaps[ id ]->SetLookupTable( lut );
684       this->ImageMaps[ id ]->Modified( );
685       this->Modified( );
686
687     } // fi
688
689   } // fi
690 }
691
692 // -------------------------------------------------------------------------
693 void cpExtensions::Visualization::ImageSliceActors::
694 SetLookupTableAsColor( unsigned int id, double r, double g, double b )
695 {
696   static const double _0 = double( 0 );
697   static const double _2 = double( 2 );
698   static const double _4 = double( 4 );
699   static const double _6 = double( 6 );
700   static const double _OPACITY = double( 0.6 );
701
702   // Check ID consistency
703   if( id == 0 || id >= this->ImageMaps.size( ) )
704     return;
705
706   // Get image scalar range
707   vtkAlgorithmOutput* aout = this->ImageMaps[ id ]->GetOutputPort( );
708   vtkImageData* image = dynamic_cast< vtkImageData* >(
709     aout->GetProducer( )->GetOutputDataObject( aout->GetIndex( ) )
710     );
711   double range[ 2 ];
712   image->GetScalarRange( range );
713
714   // Get HSV from display color
715   double cmax = ( r > g )? r: g; cmax = ( b > cmax )? b: cmax;
716   double cmin = ( r < g )? r: g; cmin = ( b < cmin )? b: cmin;
717   double d = cmax - cmin;
718
719   double saturation = ( std::fabs( cmax ) > _0 )? d / cmax: _0;
720   double value = cmax;
721   double hue = _0;
722   if( d > _0 )
723   {
724     if( r == cmax )
725       hue = std::fmod( ( g - b ) / d, _6 );
726     else if( g == cmax )
727       hue = ( ( b - r ) / d ) + _2;
728     else if( b == cmax )
729       hue = ( ( r - g ) / d ) + _4;
730     hue /= _6;
731
732   } // fi
733
734   // Define new lookup table
735   vtkSmartPointer< vtkLookupTable > lut =
736     vtkSmartPointer< vtkLookupTable >::New( );
737   lut->SetScaleToLinear( );
738   lut->SetNanColor( _0, _0, _0, _0 );
739   lut->SetTableRange( range[ 0 ], range[ 1 ] );
740   lut->SetAlphaRange( _0, _OPACITY );
741   lut->SetHueRange( _0, hue );
742   lut->SetSaturationRange( _0, saturation );
743   lut->SetValueRange( _0, value );
744   lut->Build( );
745   this->SetLookupTable( id, lut );
746 }
747
748 // -------------------------------------------------------------------------
749 int cpExtensions::Visualization::ImageSliceActors::
750 GetAxis( ) const
751 {
752   if( this->SliceMappers.size( ) > 0 )
753     return( this->SliceMappers[ 0 ]->GetOrientation( ) );
754   else
755     return( -1 );
756 }
757
758 // -------------------------------------------------------------------------
759 int cpExtensions::Visualization::ImageSliceActors::
760 GetSliceNumber( ) const
761 {
762   if( this->SliceMappers.size( ) > 0 )
763     return( this->SliceMappers[ 0 ]->GetSliceNumber( ) );
764   else
765     return( -1 );
766 }
767
768 // -------------------------------------------------------------------------
769 int cpExtensions::Visualization::ImageSliceActors::
770 GetSliceNumberMinValue( ) const
771 {
772   if( this->SliceMappers.size( ) > 0 )
773     return( this->SliceMappers[ 0 ]->GetSliceNumberMinValue( ) );
774   else
775     return( -1 );
776 }
777
778 // -------------------------------------------------------------------------
779 int cpExtensions::Visualization::ImageSliceActors::
780 GetSliceNumberMaxValue( ) const
781 {
782   if( this->SliceMappers.size( ) > 0 )
783     return( this->SliceMappers[ 0 ]->GetSliceNumberMaxValue( ) );
784   else
785     return( -1 );
786 }
787
788 // -------------------------------------------------------------------------
789 void cpExtensions::Visualization::ImageSliceActors::
790 SetSliceNumber( const int& slice )
791 {
792   unsigned int nImages = this->SliceMappers.size( );
793   if( nImages == 0 )
794     return;
795
796   // Change visualization extent
797   for( unsigned int i = 0; i < nImages; ++i )
798   {
799     this->SliceMappers[ i ]->SetSliceNumber( slice );
800     this->SliceMappers[ i ]->Modified( );
801     this->ImageActors[ i ]->Modified( );
802     this->SliceMappers[ i ]->Update( );
803
804   } // rof
805
806   // Compute plane
807   vtkAlgorithm* algo = this->SliceMappers[ 0 ]->GetInputAlgorithm( );
808   vtkInformation* info = algo->GetOutputInformation( 0 );
809   int ext[ 6 ];
810   double ori[ 3 ], spac[ 3 ], pos[ 3 ];
811   info->Get( vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT( ), ext );
812   info->Get( vtkDataObject::ORIGIN( ), ori );
813   info->Get( vtkDataObject::SPACING( ), spac );
814   this->SliceMappers[ 0 ]->GetSlicePlane( )->GetOrigin( pos );
815
816   // Prevent obscuring voxels by offsetting the plane geometry
817   double xbnds[ ] =
818     {
819       ori[ 0 ] + ( spac[ 0 ] * double( ext[ 0 ] ) ),
820       ori[ 0 ] + ( spac[ 0 ] * double( ext[ 1 ] ) )
821     };
822   double ybnds[ ] =
823     {
824       ori[ 1 ] + ( spac[ 1 ] * double( ext[ 2 ] ) ),
825       ori[ 1 ] + ( spac[ 1 ] * double( ext[ 3 ] ) )
826     };
827   double zbnds[ ] =
828     {
829       ori[ 2 ] + ( spac[ 2 ] * double( ext[ 4 ] ) ),
830       ori[ 2 ] + ( spac[ 2 ] * double( ext[ 5 ] ) )
831     };
832
833   if( spac[ 0 ] < double( 0 ) )
834   {
835     double t = xbnds[ 0 ];
836     xbnds[ 0 ] = xbnds[ 1 ];
837     xbnds[ 1 ] = t;
838
839   } // fi
840   if( spac[ 1 ] < double( 0 ) )
841   {
842     double t = ybnds[ 0 ];
843     ybnds[ 0 ] = ybnds[ 1 ];
844     ybnds[ 1 ] = t;
845
846   } // fi
847   if( spac[ 2 ] < double( 0 ) )
848   {
849     double t = zbnds[ 0 ];
850     zbnds[ 0 ] = zbnds[ 1 ];
851     zbnds[ 1 ] = t;
852
853   } // fi
854
855   // Plane function origin
856   this->PlaneFunction->SetOrigin( pos );
857
858   // Configure visualization and implicit plane orientation
859   int axis = this->SliceMappers[ 0 ]->GetOrientation( );
860   this->PlaneActor->GetProperty( )->SetRepresentationToWireframe( );
861   this->PlaneActor->GetProperty( )->SetLineWidth( 2 );
862   vtkPoints* plane_points = this->Plane->GetPoints( );
863   if( axis == 0 ) // YZ, x-normal
864   {
865     this->PlaneFunction->SetNormal( 1, 0, 0 );
866     plane_points->SetPoint( 0, pos[ 0 ], ybnds[ 0 ], zbnds[ 0 ] );
867     plane_points->SetPoint( 1, pos[ 0 ], ybnds[ 1 ], zbnds[ 0 ] );
868     plane_points->SetPoint( 2, pos[ 0 ], ybnds[ 1 ], zbnds[ 1 ] );
869     plane_points->SetPoint( 3, pos[ 0 ], ybnds[ 0 ], zbnds[ 1 ] );
870     this->PlaneActor->GetProperty( )->SetColor( 1, 0, 0 );
871   }
872   else if( axis == 1 ) // ZX, y-normal
873   {
874     this->PlaneFunction->SetNormal( 0, 1, 0 );
875     plane_points->SetPoint( 0, xbnds[ 0 ], pos[ 1 ], zbnds[ 0 ] );
876     plane_points->SetPoint( 1, xbnds[ 0 ], pos[ 1 ], zbnds[ 1 ] );
877     plane_points->SetPoint( 2, xbnds[ 1 ], pos[ 1 ], zbnds[ 1 ] );
878     plane_points->SetPoint( 3, xbnds[ 1 ], pos[ 1 ], zbnds[ 0 ] );
879     this->PlaneActor->GetProperty( )->SetColor( 0, 1, 0 );
880   }
881   else // XY, z-normal
882   {
883     this->PlaneFunction->SetNormal( 0, 0, 1 );
884     plane_points->SetPoint( 0, xbnds[ 0 ], ybnds[ 0 ], pos[ 2 ] );
885     plane_points->SetPoint( 1, xbnds[ 1 ], ybnds[ 0 ], pos[ 2 ] );
886     plane_points->SetPoint( 2, xbnds[ 1 ], ybnds[ 1 ], pos[ 2 ] );
887     plane_points->SetPoint( 3, xbnds[ 0 ], ybnds[ 1 ], pos[ 2 ] );
888     this->PlaneActor->GetProperty( )->SetColor( 0, 0, 1 );
889
890   } // fi
891   this->PlaneFunction->Modified( );
892   this->Plane->Modified( );
893   this->PlaneMapper->Modified( );
894   this->PlaneActor->Modified( );
895
896   // Prepare other actors to update
897   /* TODO
898      for( unsigned int i = 0; i < this->OtherActors.size( ); ++i )
899      {
900      if( this->OtherActors[ i ].first.GetPointer( ) != NULL )
901      {
902      this->OtherActors[ i ].first->Modified( );
903      this->OtherActors[ i ].first->Update( );
904
905      } // fi
906
907      if( this->OtherActors[ i ].second.GetPointer( ) != NULL )
908      {
909      this->OtherActors[ i ].second->GetMapper( )->Modified( );
910      this->OtherActors[ i ].second->Modified( );
911
912      } // fi
913
914      } // rof
915
916      if( this->m_UpdateCommand != NULL )
917      this->m_UpdateCommand( this->m_UpdateData );
918   */
919
920   // Update text
921   this->UpdateText( );
922
923   // Associated slices
924   for( unsigned int i = 0; i < this->AssociatedSlices.size( ); ++i )
925     if( this->AssociatedSlices[ i ]->GetAxis( ) == this->GetAxis( ) )
926       this->AssociatedSlices[ i ]->SetSliceNumber( slice );
927 }
928
929 // -------------------------------------------------------------------------
930 void cpExtensions::Visualization::ImageSliceActors::
931 SetSlice( double* pos )
932 {
933   vtkImageData* image;
934   if( this->ImageMaps[ 0 ] != NULL )
935     image =
936       dynamic_cast< vtkImageData* >( this->ImageMaps[ 0 ]->GetInput( ) );
937   else
938     image = this->SliceMappers[ 0 ]->GetInput( );
939
940   int ijk[ 3 ];
941   double pcoords[ 3 ];
942   image->ComputeStructuredCoordinates( pos, ijk, pcoords );
943   this->SetSliceNumber( ijk[ this->GetAxis( ) ] );
944 }
945
946 // -------------------------------------------------------------------------
947 void cpExtensions::Visualization::ImageSliceActors::
948 UpdateText( )
949 {
950   if( this->SliceMappers.size( ) > 0 )
951   {
952     char axis;
953     int axId = this->SliceMappers[ 0 ]->GetOrientation( );
954     if     ( axId == 0 ) axis = 'X';
955     else if( axId == 1 ) axis = 'Y';
956     else if( axId == 2 ) axis = 'Z';
957
958     std::sprintf(
959       this->TextBuffer, "Axis: %c (%d)",
960       axis, this->SliceMappers[ 0 ]->GetSliceNumber( )
961       );
962   }
963   else
964     this->TextBuffer[ 0 ] = '\0';
965   this->TextActor->SetInput( this->TextBuffer );
966   this->TextActor->Modified( );
967   this->Modified( );
968 }
969
970 // -------------------------------------------------------------------------
971 void cpExtensions::Visualization::ImageSliceActors::
972 UpdateText( double pos[ 3 ] )
973 {
974   /* TODO
975   if( this->SliceMappers.size( ) > 0 )
976   {
977     char axis;
978     int axId = this->SliceMappers[ 0 ]->GetOrientation( );
979     if     ( axId == 0 ) axis = 'X';
980     else if( axId == 1 ) axis = 'Y';
981     else if( axId == 2 ) axis = 'Z';
982     int slice = this->GetSliceNumber( );
983
984     vtkImageData* image;
985     if( this->ImageMaps[ 0 ] != NULL )
986       image =
987         dynamic_cast< vtkImageData* >( this->ImageMaps[ 0 ]->GetInput( ) );
988     else
989       image = this->SliceMappers[ 0 ]->GetInput( );
990
991     int ijk[ 3 ];
992     double pcoords[ 3 ];
993     image->ComputeStructuredCoordinates( pos, ijk, pcoords );
994     ijk[ axId ] = slice;
995
996     int ext[ 6 ];
997     image->GetExtent( ext );
998     if(
999       ext[ 0 ] <= ijk[ 0 ] && ijk[ 0 ] <= ext[ 1 ] &&
1000       ext[ 2 ] <= ijk[ 1 ] && ijk[ 1 ] <= ext[ 3 ] &&
1001       ext[ 4 ] <= ijk[ 2 ] && ijk[ 2 ] <= ext[ 5 ]
1002       )
1003     {
1004       int nScl = image->GetNumberOfScalarComponents( );
1005       std::stringstream str;
1006       str
1007         << "[" << ijk[ 0 ]
1008         << "," << ijk[ 1 ]
1009         << "," << ijk[ 2 ] << "]=(";
1010       str <<
1011         image->GetScalarComponentAsFloat( ijk[ 0 ], ijk[ 1 ], ijk[ 2 ], 0 );
1012       for( int n = 1; n < nScl; ++n )
1013         str
1014           << " "
1015           << image->GetScalarComponentAsFloat(
1016             ijk[ 0 ], ijk[ 1 ], ijk[ 2 ], n
1017             );
1018       str << ")";
1019       std::sprintf(
1020         this->TextBuffer, "Axis: %c (%d)\nPixel %s",
1021         axis, slice, str.str( ).c_str( )
1022         );
1023
1024     } // fi
1025   }
1026   else
1027     this->TextBuffer[ 0 ] = '\0';
1028   this->TextActor->SetInput( this->TextBuffer );
1029   this->TextActor->Modified( );
1030   this->Modified( );
1031   */
1032 }
1033
1034 // -------------------------------------------------------------------------
1035 void cpExtensions::Visualization::ImageSliceActors::
1036 UpdateText( const double& w, const double& l )
1037 {
1038   if( this->SliceMappers.size( ) > 0 )
1039   {
1040     char axis;
1041     int axId = this->SliceMappers[ 0 ]->GetOrientation( );
1042     if     ( axId == 0 ) axis = 'X';
1043     else if( axId == 1 ) axis = 'Y';
1044     else if( axId == 2 ) axis = 'Z';
1045
1046     std::sprintf(
1047       this->TextBuffer, "Axis: %c (%d)\nW/L (%.2f/%.2f)",
1048       axis, this->SliceMappers[ 0 ]->GetSliceNumber( ), w, l
1049       );
1050   }
1051   else
1052     this->TextBuffer[ 0 ] = '\0';
1053   this->TextActor->SetInput( this->TextBuffer );
1054   this->TextActor->Modified( );
1055   this->Modified( );
1056 }
1057
1058 // -------------------------------------------------------------------------
1059 cpExtensions::Visualization::ImageSliceActors::
1060 ImageSliceActors( )
1061   : Superclass( ),
1062     Interpolate( false )
1063 {
1064   this->Clear( );
1065
1066   this->SlicesCommand = NULL;
1067
1068   // Connect this view with a controller
1069   this->Style = vtkSmartPointer< ImageInteractorStyle >::New( );
1070   this->Style->AssociateView( this );
1071   this->Style->SetMouseMoveCommand( Self::_MouseMoveCommand );
1072   this->Style->SetMouseClickCommand( Self::_MouseClickCommand );
1073   this->Style->SetMouseDoubleClickCommand( Self::_MouseDoubleClickCommand );
1074   this->Style->SetMouseWheelCommand( Self::_MouseWheelCommand );
1075   this->Style->SetKeyCommand( Self::_KeyCommand );
1076 }
1077
1078 // -------------------------------------------------------------------------
1079 cpExtensions::Visualization::ImageSliceActors::
1080 ~ImageSliceActors( )
1081 {
1082 }
1083
1084 // -------------------------------------------------------------------------
1085 void cpExtensions::Visualization::ImageSliceActors::
1086 _ConfigureNewLUT( vtkImageData* data )
1087 {
1088   // Does the new LUT is a window-level one?
1089   unsigned int nImgs = this->ImageMaps.size( );
1090   unsigned int nCmps = data->GetNumberOfScalarComponents( );
1091
1092   if( nCmps > 1 )
1093   {
1094     this->ImageMaps.push_back( NULL );
1095     return;
1096
1097   } // fi
1098
1099   // Create LUT filter
1100   this->ImageMaps.push_back( vtkSmartPointer< vtkImageMapToColors >::New( ) );
1101   if( nImgs == 0 )
1102   {
1103     double range[ 2 ];
1104     data->GetScalarRange( range );
1105     vtkSmartPointer< vtkWindowLevelLookupTable > lut =
1106       vtkSmartPointer< vtkWindowLevelLookupTable >::New( );
1107     lut->SetScaleToLinear( );
1108     lut->SetTableRange( range );
1109     lut->SetWindow( range[ 1 ] - range[ 0 ] );
1110     lut->SetLevel( ( range[ 1 ] + range[ 0 ] ) / double( 2 ) );
1111     lut->Build( );
1112     this->ImageMaps[ 0 ]->SetLookupTable( lut );
1113
1114     this->MinWindow = double( 0 );
1115     this->MaxWindow = range[ 1 ] - range[ 0 ];
1116     this->MinLevel = range[ 0 ];
1117     this->MaxLevel = range[ 1 ];
1118   }
1119   else
1120     this->SetLookupTableAsColor( nImgs, 1, 0, 0 );
1121 }
1122
1123 // -------------------------------------------------------------------------
1124 void cpExtensions::Visualization::ImageSliceActors::
1125 _ConfigureNewInput( int axis )
1126 {
1127   unsigned int nImages = this->ImageActors.size( );
1128
1129   // Configure mapper
1130   vtkImageSliceMapper* mapper = this->SliceMappers[ nImages ];
1131   if( nImages == 0 )
1132     mapper->SetOrientation( axis );
1133   else
1134     mapper->SetOrientation( this->SliceMappers[ 0 ]->GetOrientation( ) );
1135   mapper->Update( );
1136
1137   // Create actor
1138   vtkSmartPointer< vtkImageActor > actor =
1139     vtkSmartPointer< vtkImageActor >::New( );
1140   this->ImageActors.push_back( actor );
1141   actor->SetMapper( mapper );
1142   actor->SetInterpolate( this->Interpolate );
1143   actor->Modified( );
1144
1145   if( nImages == 0 )
1146   {
1147     this->AddItem( this->CursorActor );
1148     this->AddItem( this->PlaneActor );
1149     this->AddItem( this->TextActor );
1150     this->Style->AssociateImageActor( actor );
1151
1152   } // fi
1153   this->AddItem( actor );
1154
1155   if( nImages > 1 )
1156     this->SetSliceNumber( this->GetSliceNumber( ) );
1157   this->Modified( );
1158 }
1159
1160 // -------------------------------------------------------------------------
1161 void cpExtensions::Visualization::ImageSliceActors::
1162 _MouseMoveCommand(
1163   void* data,
1164   const ImageInteractorStyle::ButtonID& btn, double* pos,
1165   bool alt, bool ctr, bool sft
1166   )
1167 {
1168   ImageSliceActors* actors = reinterpret_cast< ImageSliceActors* >( data );
1169   if( actors == NULL )
1170     return;
1171
1172   if( btn == ImageInteractorStyle::ButtonID_None )
1173   {
1174     actors->UpdateText( pos );
1175   }
1176   else if( btn == ImageInteractorStyle::ButtonID_Left )
1177   {
1178     if( !alt && ctr && !sft && actors->SlicesCommand != NULL )
1179       actors->SlicesCommand( pos, actors->GetAxis( ), actors->SlicesData );
1180   }
1181   else if( btn == ImageInteractorStyle::ButtonID_Middle )
1182   {
1183   }
1184   else if( btn == ImageInteractorStyle::ButtonID_Right )
1185   {
1186     if( !alt && !ctr && sft )
1187     {
1188       double bounds[ 6 ];
1189       actors->SliceMappers[ 0 ]->GetBounds( bounds );
1190
1191       int a0 = actors->GetAxis( );
1192       int a1 = ( a0 + 1 ) % 3;
1193       int a2 = ( a0 + 2 ) % 3;
1194       double dx = pos[ a1 ] - actors->StartWindowLevelPos[ a1 ];
1195       double dy = pos[ a2 ] - actors->StartWindowLevelPos[ a2 ];
1196       dx /= bounds[ ( a1 << 1 ) + 1 ] - bounds[ a1 << 1 ];
1197       dy /= bounds[ ( a2 << 1 ) + 1 ] - bounds[ a2 << 1 ];
1198
1199       dx *= actors->StartWindowLevel[ 0 ];
1200       dy *= actors->StartWindowLevel[ 1 ];
1201       actors->SetWindowLevel(
1202         actors->StartWindowLevel[ 0 ] + dx,
1203         actors->StartWindowLevel[ 1 ] + dy
1204         );
1205
1206     } // fi
1207
1208   } // fi
1209 }
1210
1211 // -------------------------------------------------------------------------
1212 void cpExtensions::Visualization::ImageSliceActors::
1213 _MouseClickCommand(
1214   void* data,
1215   const ImageInteractorStyle::ButtonID& btn, double* pos,
1216   bool alt, bool ctr, bool sft
1217   )
1218 {
1219   ImageSliceActors* actors = reinterpret_cast< ImageSliceActors* >( data );
1220   if( actors == NULL )
1221     return;
1222
1223   actors->StartWindowLevelPos[ 0 ] = pos[ 0 ];
1224   actors->StartWindowLevelPos[ 1 ] = pos[ 1 ];
1225   actors->StartWindowLevelPos[ 2 ] = pos[ 2 ];
1226   actors->StartWindowLevel[ 0 ] = actors->GetWindow( );
1227   actors->StartWindowLevel[ 1 ] = actors->GetLevel( );
1228 }
1229
1230 // -------------------------------------------------------------------------
1231 void cpExtensions::Visualization::ImageSliceActors::
1232 _MouseDoubleClickCommand(
1233   void* data,
1234   const ImageInteractorStyle::ButtonID& btn, double* pos,
1235   bool alt, bool ctr, bool sft
1236   )
1237 {
1238   ImageSliceActors* actors = reinterpret_cast< ImageSliceActors* >( data );
1239   if( actors == NULL )
1240     return;
1241
1242   if( btn == ImageInteractorStyle::ButtonID_Left )
1243   {
1244     if( !alt && !ctr && !sft )
1245       actors->SetCursor( pos );
1246     else if( !alt && ctr && !sft && actors->SlicesCommand != NULL )
1247     {
1248       actors->SlicesCommand( pos, actors->GetAxis( ), actors->SlicesData );
1249       for( unsigned int i = 0; i < actors->CursorCommands.size( ); ++i )
1250         actors->CursorCommands[ i ].first(
1251           pos,
1252           actors->GetAxis( ),
1253           actors->CursorCommands[ i ].second
1254           );
1255
1256     } // fi
1257   }
1258   else if( btn == ImageInteractorStyle::ButtonID_Middle )
1259   {
1260   }
1261   else if( btn == ImageInteractorStyle::ButtonID_Right )
1262   {
1263   } // fi
1264 }
1265
1266 // -------------------------------------------------------------------------
1267 void cpExtensions::Visualization::ImageSliceActors::
1268 _MouseWheelCommand(
1269   void* data,
1270   const int& dir, bool alt, bool ctr, bool sft
1271   )
1272 {
1273   ImageSliceActors* actors = reinterpret_cast< ImageSliceActors* >( data );
1274   if( actors == NULL )
1275     return;
1276
1277   if( !alt && !ctr && !sft )
1278   {
1279     actors->SetSliceNumber( actors->GetSliceNumber( ) + dir );
1280   }
1281   else if( !alt && !ctr && sft )
1282   {
1283     actors->SetSliceNumber( actors->GetSliceNumber( ) + ( dir * 10 ) );
1284
1285   } // fi
1286 }
1287
1288 // -------------------------------------------------------------------------
1289 void cpExtensions::Visualization::ImageSliceActors::
1290 _KeyCommand(
1291   void* data,
1292   const char& key
1293   )
1294 {
1295 }
1296
1297 // eof - $RCSfile$