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