/*=========================================================================

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/VolViewCopyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkKW2DRenderWidget.h"

#include "vtkCamera.h"
#include "vtkColorTransferFunction.h"
#include "vtkCommand.h"
#include "vtkCornerAnnotation.h"
#include "vtkImageData.h"
#include "vtkKWApplication.h"
#include "vtkKWEvent.h"
#include "vtkKWEventMap.h"
#include "vtkKWIcon.h"
#include "vtkKWOptions.h"
#include "vtkKWImageMapToWindowLevelColors.h"
#include "vtkKWInternationalization.h"
#include "vtkKWMenu.h"
#include "vtkKWScaleWithEntry.h"
#include "vtkKWTkUtilities.h"
#include "vtkKWWindow.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkProperty2D.h"
#include "vtkRenderWindow.h"
#include "vtkRenderer.h"
#include "vtkSideAnnotation.h"
#include "vtkTextProperty.h"
#include "vtkVolumeProperty.h"

#include <vtksys/stl/string>
#include <vtksys/ios/sstream>

vtkCxxRevisionMacro(vtkKW2DRenderWidget, "$Revision: 1.168 $");

#ifdef KWCommonPro_USE_XML_RW
#include "XML/vtkXMLKW2DRenderWidgetReader.h"
#include "XML/vtkXMLKW2DRenderWidgetWriter.h"
#endif
vtkKWGetXMLReaderWriterObjectsImplementationMacro(vtkKW2DRenderWidget, vtkXMLKW2DRenderWidgetReader, vtkXMLKW2DRenderWidgetWriter);

//---------------------------------------------------------------------------
vtkKW2DRenderWidget::vtkKW2DRenderWidget()
{
  this->SideAnnotation = vtkSideAnnotation::New();
  this->SideAnnotation->VisibilityOff();
  this->SupportSideAnnotation = 1;
  
  this->ImageMapToRGBA = NULL;
  
  this->HasSliceControl = 1;
  this->SliceScale = vtkKWScaleWithEntry::New();
  this->ResetWindowLevelForSelectedSliceOnly = 0;
  
  this->SliceOrientation        = vtkKW2DRenderWidget::SLICE_ORIENTATION_XY;
  this->SliceType               = vtkKW2DRenderWidget::SLICE_TYPE_DEFAULT;
  this->InteractionMode         = vtkKW2DRenderWidget::INTERACTION_MODE_ALL;
  this->CoordinateSystemMedical = vtkKW2DRenderWidget::COORDINATE_SYSTEM_MEDICAL_LPS;

  this->SetRendererBackgroundColorRegKey(
    "2DRenderWidgetRendererBackgroundColor");
  this->SetRendererBackgroundColor2RegKey(
    "2DRenderWidgetRendererBackgroundColor2");
  this->SetRendererGradientBackgroundRegKey(
    "2DRenderWidgetRendererGradientBackground");
}

//----------------------------------------------------------------------------
vtkKW2DRenderWidget::~vtkKW2DRenderWidget()
{
  this->SetInput(NULL);
 
  if (this->SideAnnotation)
    {
    this->SideAnnotation->Delete();
    this->SideAnnotation = NULL;
    }
  
  if (this->ImageMapToRGBA)
    {
    this->ImageMapToRGBA->Delete();
    this->ImageMapToRGBA = NULL;
    }
  
  if (this->SliceScale)
    {
    this->SliceScale->Delete();
    this->SliceScale = NULL;
    }
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::ConfigureEventMap()
{
  // Get the render-widget common event map settings

  this->Superclass::ConfigureEventMap();

  // Configure the 2d-widget specific settings

  switch (this->InteractionMode)
    {
    case vtkKW2DRenderWidget::INTERACTION_MODE_PAN:
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::LeftButton,vtkKWEventMap::NoModifier, "Pan");
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::LeftButton,vtkKWEventMap::ShiftModifier, "WindowLevel");
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::LeftButton,vtkKWEventMap::ControlModifier, "Zoom");
      /*
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::MiddleButton, vtkKWEventMap::NoModifier, "Zoom");
      */
      break;

    case vtkKW2DRenderWidget::INTERACTION_MODE_ZOOM:
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::LeftButton,vtkKWEventMap::NoModifier, "Zoom");
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::LeftButton,vtkKWEventMap::ShiftModifier, "WindowLevel");
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::LeftButton,vtkKWEventMap::ControlModifier, "Pan");

      /*
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::MiddleButton, vtkKWEventMap::NoModifier, "Zoom");
      */
      break;

    case vtkKW2DRenderWidget::INTERACTION_MODE_WINDOWLEVEL:
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::LeftButton,vtkKWEventMap::NoModifier, "WindowLevel");
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::LeftButton,vtkKWEventMap::ShiftModifier, "Pan");
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::LeftButton,vtkKWEventMap::ControlModifier, "Zoom");

      /*
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::MiddleButton, vtkKWEventMap::NoModifier, "Zoom");
      */
      break;

    case vtkKW2DRenderWidget::INTERACTION_MODE_ALL:
    default:
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::LeftButton,vtkKWEventMap::NoModifier, "WindowLevel");
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::LeftButton,vtkKWEventMap::ShiftModifier, "Pan");
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::LeftButton,vtkKWEventMap::ControlModifier, "Zoom");

      this->EventMap->AddMouseEvent(
        vtkKWEventMap::MiddleButton,vtkKWEventMap::NoModifier, "Pan");
      this->EventMap->AddMouseEvent(
       vtkKWEventMap::MiddleButton,vtkKWEventMap::ShiftModifier,"WindowLevel");
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::MiddleButton,vtkKWEventMap::ControlModifier,"Zoom");

      this->EventMap->AddMouseEvent(
        vtkKWEventMap::RightButton, vtkKWEventMap::NoModifier, "Zoom");
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::RightButton, vtkKWEventMap::ShiftModifier, "Pan");
      this->EventMap->AddMouseEvent(
      vtkKWEventMap::RightButton,vtkKWEventMap::ControlModifier,"WindowLevel");
      break;
    }

  const char *context = "2D view";

  this->EventMap->AddKeyEvent(
    'r', vtkKWEventMap::NoModifier, "Reset",
    context, k_("Reset camera position"));

  this->EventMap->AddKeySymEvent(
    "Left", vtkKWEventMap::NoModifier, "DecrementSlice",
    context, k_("Go to previous slice"));

  this->EventMap->AddKeySymEvent(
    "Right", vtkKWEventMap::NoModifier, "IncrementSlice",
    context, k_("Go to next slice"));

  this->EventMap->AddKeySymEvent(
    "Up", vtkKWEventMap::NoModifier, "DecrementSlice",
    context, k_("Go to previous slice"));

  this->EventMap->AddKeySymEvent(
    "Down", vtkKWEventMap::NoModifier, "IncrementSlice",
    context, k_("Go to next slice"));

  this->EventMap->AddKeySymEvent(
    "Prior", vtkKWEventMap::NoModifier, "DecrementPage",
    context, k_("Go to previous +10 slice"));

  this->EventMap->AddKeySymEvent(
    "Next", vtkKWEventMap::NoModifier, "IncrementPage",
    context, k_("Go to next +10 slice"));

  this->EventMap->AddKeySymEvent(
    "Home", vtkKWEventMap::NoModifier, "GoToFirstSlice",
    context, k_("Go to first slice"));

  this->EventMap->AddKeySymEvent(
    "End", vtkKWEventMap::NoModifier, "GoToLastSlice",
    context, k_("Go to last slice"));
}

//----------------------------------------------------------------------------
int vtkKW2DRenderWidget::ConnectInternalPipeline()
{
  // Have a look at the documentation for this method (see .h).

  if (!this->Superclass::ConnectInternalPipeline())
    {
    return 0;
    }

  // Image

  vtkKWImageMapToWindowLevelColors *map = this->GetImageMapToRGBA();
  if (map)
    {
    this->ConnectImageMapToRGBA();
    map->Modified();

    this->CornerAnnotation->SetWindowLevel(map);
    }

  return 1;
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::ConnectImageMapToRGBA()
{
  vtkKWImageMapToWindowLevelColors *map = this->GetImageMapToRGBA();
  if (map)
    {
    map->SetInput(this->Input);
    }
}

//----------------------------------------------------------------------------
int vtkKW2DRenderWidget::InputBoundsHaveChanged()
{
  // Have a look at the documentation for this method (see .h).

  if (!this->Superclass::InputBoundsHaveChanged())
    {
    return 0;
    }

  // Update the slice scale range, the slice, and the display extent

  if (this->HasSliceControl)
    {
    this->UpdateSliceScale();
    this->GoToDefaultSlice();  // will call UpdateDisplayExtent())
    }
  else
    {
    this->UpdateDisplayExtent();
    }

  // Reset the camera

  this->ResetCamera();

  return 1;
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::UpdateSliceOrientationAndType()
{
  switch (this->SliceOrientation)
    {
    case vtkKW2DRenderWidget::SLICE_ORIENTATION_XY:
      if (this->SliceType == vtkKW2DRenderWidget::SLICE_TYPE_DEFAULT)
        {
        this->SetSideAnnotationTexts("-X"," X","-Y"," Y");
        this->SliceScale->SetLabelText("X-Y:");
        }
      else if (this->SliceType == vtkKW2DRenderWidget::SLICE_TYPE_MEDICAL)
        {
        // i.e., Axial
        this->SetSideAnnotationTexts(
          ks_("Annotation|Right|R"),
          ks_("Annotation|Left|L"),
          ks_("Annotation|Posterior|P"),
          ks_("Annotation|Anterior|A"));
        vtksys_stl::string label(ks_("Annotation|Anterior|A"));
        label += ":";
        this->SliceScale->SetLabelText(label.c_str());
        }
      break;

    case vtkKW2DRenderWidget::SLICE_ORIENTATION_XZ:
      if (this->SliceType == vtkKW2DRenderWidget::SLICE_TYPE_DEFAULT)
        {
        this->SetSideAnnotationTexts("-X"," X","-Z"," Z");
        this->SliceScale->SetLabelText("X-Z:");
        }
      else if (this->SliceType == vtkKW2DRenderWidget::SLICE_TYPE_MEDICAL)
        {
        // i.e., Frontal/Coronal
        this->SetSideAnnotationTexts(
          ks_("Annotation|Right|R"),
          ks_("Annotation|Left|L"),
          ks_("Annotation|Inferior|I"),
          ks_("Annotation|Superior|S"));
        vtksys_stl::string label(ks_("Annotation|Frontal|F"));
        label += ":";
        this->SliceScale->SetLabelText(label.c_str());
        }
      break;

    case vtkKW2DRenderWidget::SLICE_ORIENTATION_YZ:
      if (this->SliceType == vtkKW2DRenderWidget::SLICE_TYPE_DEFAULT)
        {
        this->SetSideAnnotationTexts("-Y"," Y","-Z"," Z");
        this->SliceScale->SetLabelText("Y-Z:");
        }
      else if (this->SliceType == vtkKW2DRenderWidget::SLICE_TYPE_MEDICAL)
        {
        // i.e., Sagittal
        this->SetSideAnnotationTexts(
          ks_("Annotation|Anterior|A"),
          ks_("Annotation|Posterior|P"),
          ks_("Annotation|Inferior|I"),
          ks_("Annotation|Superior|S"));
        vtksys_stl::string label(ks_("Annotation|Sagittal|S"));
        label += ":";
        this->SliceScale->SetLabelText(label.c_str());
        }
      break;
    }

  this->UpdateSliceScale();

  this->Reset();
}

//----------------------------------------------------------------------------
// Some info on camera orientations:
//
// The camera here might seem a little hard to follow, but here is the 
// rationale. For medical data, we fix our annotations so that, the 2-D layout
// with the slider direction (low slice below, high slice above) for the 
// LPS co-ordinate system looks like:
//
//      Axial                Coronal                   Sagittal 
// 
// S      A               P       S                  L      S
// |   R     L            |    R     L               |   A     P
// I      P               A       I                  R      I
// 
// In other words, 
// * On the axial widget, you are looking from the feet of the 
//   patient and the slider takes you from the feet to the head
// * On the coronal widget, you are standing right on top of the patient and
//   looking down, the slider takes you from his belly to the spine.
// * On the sagittal widget, ...
// 
// Why? : Cause we would still like to maintain the constraint that
// R occurs on the left side of the screen and L occurs on the right side, 
// A on top, P below and S on top, I below. This is how radiologists are used
// to looking at a dataset (from the feet). 
//
void vtkKW2DRenderWidget::ResetCamera()
{
  int i, nb_renderers = this->GetNumberOfRenderers();
  for (i = 0; i < nb_renderers; i++)
    {
    vtkRenderer *renderer = this->GetNthRenderer(i);
    if (renderer)
      {
      vtkCamera *cam = renderer->GetActiveCamera();
      if (cam)
        {
        switch (this->SliceOrientation)
          {
          case vtkKW2DRenderWidget::SLICE_ORIENTATION_XY:
            cam->SetFocalPoint(0,0,0);
            if (this->SliceType == vtkKW2DRenderWidget::SLICE_TYPE_DEFAULT)
              {
              cam->SetPosition(0,0,1);
              cam->SetViewUp(0,1,0);
              }
            else if (this->SliceType == 
                     vtkKW2DRenderWidget::SLICE_TYPE_MEDICAL)
              {
              // i.e., Axial
              if (this->CoordinateSystemMedical == vtkKW2DRenderWidget::COORDINATE_SYSTEM_MEDICAL_RAS)
                {
                cam->SetPosition(0,0,-1);
                cam->SetViewUp(0,1,0);
                }
              else if (this->CoordinateSystemMedical == vtkKW2DRenderWidget::COORDINATE_SYSTEM_MEDICAL_LPS)
                {
                cam->SetPosition(0,0,-1);
                cam->SetViewUp(0,-1,0);
                }
              }
            break;

          case vtkKW2DRenderWidget::SLICE_ORIENTATION_XZ:
            cam->SetFocalPoint(0,0,0);
            if (this->SliceType == vtkKW2DRenderWidget::SLICE_TYPE_DEFAULT)
              {
              cam->SetPosition(0,-1,0);
              cam->SetViewUp(0,0,1);
              }
            else if (this->SliceType == 
                     vtkKW2DRenderWidget::SLICE_TYPE_MEDICAL)
              {
              // i.e., Frontal/Coronal
              if (this->CoordinateSystemMedical == vtkKW2DRenderWidget::COORDINATE_SYSTEM_MEDICAL_RAS)
                {
                cam->SetPosition(0,1,0);
                cam->SetViewUp(0,0,1);
                }
              else if (this->CoordinateSystemMedical == vtkKW2DRenderWidget::COORDINATE_SYSTEM_MEDICAL_LPS)
                {
                cam->SetPosition(0,-1,0);
                cam->SetViewUp(0,0,1);
                }
              }
            break;

          case vtkKW2DRenderWidget::SLICE_ORIENTATION_YZ:
            cam->SetFocalPoint(0,0,0);
            if (this->SliceType == vtkKW2DRenderWidget::SLICE_TYPE_DEFAULT)
              {
              cam->SetPosition(1,0,0);
              cam->SetViewUp(0,0,1);
              }
            else if (this->SliceType == 
                     vtkKW2DRenderWidget::SLICE_TYPE_MEDICAL)
              {
              // i.e., Sagittal
              if (this->CoordinateSystemMedical == vtkKW2DRenderWidget::COORDINATE_SYSTEM_MEDICAL_RAS)
                {
                cam->SetPosition(-1,0,0);
                cam->SetViewUp(0,0,1);
                }
              else if (this->CoordinateSystemMedical == vtkKW2DRenderWidget::COORDINATE_SYSTEM_MEDICAL_LPS)
                {
                cam->SetPosition(1,0,0);
                cam->SetViewUp(0,0,1);
                }
              }
            break;
          }
        }
      }
    }

  for (i = 0; i < nb_renderers; i++)
    {
    vtkRenderer *renderer = this->GetNthRenderer(i);
    if (renderer)
      {
      double bounds[6];
      this->ComputeVisiblePropBounds(i, bounds);
      if (bounds[0] == VTK_LARGE_FLOAT)
        {
        vtkDebugMacro(<< "Cannot reset camera!");
        return;
        }

      double vn[3];
      vtkCamera *cam = renderer->GetActiveCamera();
      if (cam != NULL)
        {
        cam->GetViewPlaneNormal(vn);
        }
      else
        {
        vtkErrorMacro(<< "Trying to reset non-existant camera");
        return;
        }

      double center[3];
      center[0] = ((bounds[0] + bounds[1]) / 2.0);
      center[1] = ((bounds[2] + bounds[3]) / 2.0);
      center[2] = ((bounds[4] + bounds[5]) / 2.0);

      double aspect[2];
      renderer->ComputeAspect();
      renderer->GetAspect(aspect);

      double distance, width = 0, viewAngle, *vup;

      switch (this->SliceOrientation)
        {
        // Sagittal
      case vtkKW2DRenderWidget::SLICE_ORIENTATION_YZ:
        // Check Y and Z for the Y axis on the window
        width = (bounds[5] - bounds[4]) / aspect[1];
        if (((bounds[3] - bounds[2]) / aspect[0]) > width) 
          {
          width = (bounds[3] - bounds[2]) / aspect[0];
          }
        break;
        // Coronal
      case vtkKW2DRenderWidget::SLICE_ORIENTATION_XZ:
        // Check X and Z for the Y axis on the window
        width = (bounds[5] - bounds[4]) / aspect[1];
        if (((bounds[1] - bounds[0]) / aspect[0]) > width) 
          {
          width = (bounds[1] - bounds[0]) / aspect[0];
          }
        break;
        // Axial
      case vtkKW2DRenderWidget::SLICE_ORIENTATION_XY:
        width = (bounds[3] - bounds[2]) / aspect[1];
        if (((bounds[1] - bounds[0]) / aspect[0]) > width) 
          {
          width = (bounds[1] - bounds[0]) / aspect[0];
          }
        break;
      default:
        // cannot happen
        ;
        }

      if (cam->GetParallelProjection())
        {
        viewAngle = 30;  // the default in vtkCamera
        }
      else
        {
        viewAngle = cam->GetViewAngle();
        }
  
      distance = width / (double)tan(viewAngle * vtkMath::Pi() / 360.0);

      // Check view-up vector against view plane normal

      vup = cam->GetViewUp();
      if (fabs(vtkMath::Dot(vup, vn)) > 0.999)
        {
        vtkWarningMacro(
          "Resetting view-up since view plane normal is parallel");
        cam->SetViewUp(-vup[2], vup[0], vup[1]);
        }

      // Update the camera
      
      cam->SetFocalPoint(center[0], center[1], center[2]);
      cam->SetPosition((center[0] + distance * vn[0]),
                       (center[1] + distance * vn[1]),
                       (center[2] + distance * vn[2]));

      // Setup default parallel scale
      
      cam->SetParallelScale(0.5 * width);
      }
    }

  this->ResetCameraClippingRange();
}

//---------------------------------------------------------------------------
void vtkKW2DRenderWidget::ResetWindowLevel()
{
  vtkImageData *input = this->Input;
  if (!input)
    {
    return;
    }

  input->UpdateInformation();
  int *we = input->GetWholeExtent();

  if (this->ResetWindowLevelForSelectedSliceOnly)
    {
    int slice = this->GetSlice();
    if (!this->IsSliceInRange(slice))
      {
      slice = this->GoToDefaultSlice();
      }
    input->SetUpdateExtent(
      this->SliceOrientation != vtkKW2DRenderWidget::SLICE_ORIENTATION_YZ ? 
      we[0] : slice,
      this->SliceOrientation != vtkKW2DRenderWidget::SLICE_ORIENTATION_YZ ? 
      we[1] : slice,
      this->SliceOrientation != vtkKW2DRenderWidget::SLICE_ORIENTATION_XZ ? 
      we[2] : slice,
      this->SliceOrientation != vtkKW2DRenderWidget::SLICE_ORIENTATION_XZ ? 
      we[3] : slice,
      this->SliceOrientation != vtkKW2DRenderWidget::SLICE_ORIENTATION_XY ? 
      we[4] : slice,
      this->SliceOrientation != vtkKW2DRenderWidget::SLICE_ORIENTATION_XY ? 
      we[5] : slice);
    }
  else
    {
    input->SetUpdateExtent(we);
    }

  input->Update();
  this->UpdateDisplayExtent();

  double *range = input->GetScalarRange();
  float window = (float)((double)range[1] - (double)range[0]);
  float level = (float)(((double)range[1] + (double)range[0]) / 2.0);

  // For some mappings the window,level will be 255,127.5 due to post LUT
  // coloring

  vtkKWImageMapToWindowLevelColors *map = this->GetImageMapToRGBA();
  if (map &&
      (map->GetDisplayChannels() ==
       vtkKWImageMapToWindowLevelColors::DISPLAY_POST_LUT_INTENSITIES) &&
      map->GetUseOpacityModulation() == 0)
    {
    window = 255;
    level = 127.5;
    }

  if (this->GetWindow() != window || this->GetLevel() != level)
    {
    this->SetWindowLevel(window, level);
    this->InvokeEvent(vtkKWEvent::WindowLevelResetEvent, NULL);
    }
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::UpdateColorMapping()
{
  if (!this->Input)
    {
    return;
    }

  vtkKWImageMapToWindowLevelColors *map = this->GetImageMapToRGBA();
  if (!map)
    {
    return;
    }


  map->SetIndependentComponents(this->GetIndependentComponents());

  // Make sure the current settings are OK

  int disp_ch = this->DisplayChannels;
  int use_op = this->UseOpacityModulation;

  if (!map->IsValidCombination(disp_ch, use_op) &&
      !map->GetValidCombination(&disp_ch, &use_op))
    {
    vtkWarningMacro(<< "Invalid color mapping (" 
                    << disp_ch << ", " << use_op << ")");
    return;
    }

  map->SetDisplayChannels(disp_ch);
  map->SetUseOpacityModulation(use_op);

  map->SetWindow(this->Window);
  map->SetLevel(this->Level);

  int index;
  for (index = 0; index < VTK_MAX_VRCOMP; index++)
    {
    map->SetWeight(
      index, this->VolumeProperty->GetComponentWeight(index));
    map->SetLookupTable(
      index, this->VolumeProperty->GetRGBTransferFunction(index));
    }

  // WARNING: do not set this->DisplayChannels and this->UseOpacityModulation
  // to the new adjusted (valid) values. We want them to be 'hints' or
  // 'requests', so that the ImageMapToRGBA will be given a chance to match
  // them even if IndependentComponents has been changed in the meantime.

  this->UpdateImageMap();
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::SetImageMapToRGBA(
  vtkKWImageMapToWindowLevelColors *arg)
{
  if (this->ImageMapToRGBA == arg)
    {
    return;
    }

  if (this->ImageMapToRGBA)
    {
    this->ImageMapToRGBA->UnRegister(this);
    }
    
  this->ImageMapToRGBA = arg;

  if (this->ImageMapToRGBA)
    {
    this->ImageMapToRGBA->Register(this);
    }  

  this->ConnectInternalPipeline();
  this->UpdateColorMapping();
  
  this->Modified();
}

//----------------------------------------------------------------------------
vtkKWImageMapToWindowLevelColors* vtkKW2DRenderWidget::GetImageMapToRGBA()
{
  if (!this->ImageMapToRGBA)
    {
    this->ImageMapToRGBA = vtkKWImageMapToWindowLevelColors::New();
    // If the ImageMapToRGBA has never been created, assume this whole
    // widget is new and try to make a better guess for the future
    // ImageMapToRGBA parameters.
    if (this->Input && this->Input->GetNumberOfScalarComponents() > 2)
      {
      this->UseOpacityModulation = 1;
      this->DisplayChannels = 
        vtkKWImageMapToWindowLevelColors::DISPLAY_POST_LUT_INTENSITIES;
      }
    else
      {
      this->UseOpacityModulation = 0;
      this->DisplayChannels = 
        vtkKWImageMapToWindowLevelColors::DISPLAY_INTENSITIES;
      }
    }

  return this->ImageMapToRGBA;
}

//---------------------------------------------------------------------------
void vtkKW2DRenderWidget::CreateWidget()
{
  if (this->IsCreated())
    {
    vtkErrorMacro("widget already created " << this->GetClassName());
    return;
    }

  this->Superclass::CreateWidget();

  if (!this->SliceScale->GetParent())
    {
    this->SliceScale->SetParent(this);
    }
  this->SliceScale->Create();
  this->SliceScale->SetCommand(this, "SliceSelectedCallback");
  this->SliceScale->SetLabelText(ks_("Slice Number"));
  this->SliceScale->RangeVisibilityOn();
  this->SliceScale->SetEntryWidth(4);
  this->SliceScale->SetResolution(1);
  this->SliceScale->SetBalloonHelpString(
    k_("Select the slice to display. Left/Right arrow keys can also be used "
       "with the mouse in the Image window to control the slice number."));

  this->PackSliceScale();

  this->SetSideAnnotationVisibility(1);

  this->UpdateSliceOrientationAndType();
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::PackSliceScale()
{
  if (this->SliceScale && this->SliceScale->IsCreated())
    {
    if (this->HasSliceControl)
      {
      int w = (this->SliceScale->GetOrientation() == 
               vtkKWOptions::OrientationVertical ? 1 : 0);
      this->Script(
        "grid rowconfigure %s 1 -weight %d", 
        this->SliceScale->GetParent()->GetWidgetName(), w);
      this->Script(
        "grid %s -row 1 -sticky nsew",
        this->SliceScale->GetWidgetName());
      }
    else
      {
      this->Script("grid forget %s",
                   this->SliceScale->GetWidgetName());
      }
    }
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::SetSideAnnotationTexts(
  const char *minusx, const char *x,
  const char *minusy, const char *y)
{
  this->SideAnnotation->SetMinusXLabel(minusx);
  this->SideAnnotation->SetXLabel(x);
  this->SideAnnotation->SetMinusYLabel(minusy);
  this->SideAnnotation->SetYLabel(y);  

  if (this->GetSideAnnotationVisibility())
    {
    this->Render();
    }
}

//----------------------------------------------------------------------------
int vtkKW2DRenderWidget::GetSideAnnotationVisibility()
{
  return (this->SideAnnotation &&
          this->HasViewProp(this->SideAnnotation) && 
          this->SideAnnotation->GetVisibility());
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::SetSupportSideAnnotation(int s)
{
  if (this->SupportSideAnnotation == s)
    {
    return;
    }

  this->SupportSideAnnotation = s;
  this->Modified();

  this->SetSideAnnotationVisibility( 
      this->GetSideAnnotationVisibility() & s );
}
  
//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::SetSideAnnotationVisibility(int v)
{
  if (this->GetSideAnnotationVisibility() == v)
    {
    return;
    }

  if (v)
    {
    if (this->SupportSideAnnotation)
      {
      this->SideAnnotation->VisibilityOn();
      this->InvokeEvent(vtkKW2DRenderWidget::
          SideAnnotationVisibilityChangedEvent, &v);
      if (!this->HasViewProp(this->SideAnnotation))
        {
        this->AddOverlayViewProp(this->SideAnnotation);
        }
      }
    }
  else
    {
    this->SideAnnotation->VisibilityOff();
    this->InvokeEvent(vtkKW2DRenderWidget::
        SideAnnotationVisibilityChangedEvent, &v);
    if (this->HasViewProp(this->SideAnnotation))
      {
      this->RemoveViewProp(this->SideAnnotation);
      }
    }

  this->Render();
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::ToggleSideAnnotationVisibility()
{
  this->SetSideAnnotationVisibility(!this->GetSideAnnotationVisibility());
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::SetSliceOrientation(int orientation)
{
  if (orientation < vtkKW2DRenderWidget::SLICE_ORIENTATION_YZ ||
      orientation > vtkKW2DRenderWidget::SLICE_ORIENTATION_XY)
    {
    vtkErrorMacro("Error - invalid slice orientation " << orientation);
    return;
    }

  if (this->SliceOrientation == orientation)
    {
    return;
    }

  this->SliceOrientation = orientation;

  this->UpdateSliceOrientationAndType();

  this->GoToDefaultSlice();
  this->UpdateDisplayExtent();

  this->Render();

  this->InvokeEvent(
    vtkKW2DRenderWidget::SliceOrientationChangedEvent, &orientation);
}

//----------------------------------------------------------------------------
const char* 
vtkKW2DRenderWidget::GetSliceOrientationAsMedicalOrientationString(int orient)
{
  // Any modifications made here should be propagated to both:
  // GetSliceOrientationAsMedicalOrientationString and
  // GetSliceOrientationFromMedicalOrientationString

  switch (orient)
    {
    case vtkKW2DRenderWidget::SLICE_ORIENTATION_XY:
      return ks_("Slice Orientation|Axial");

    case vtkKW2DRenderWidget::SLICE_ORIENTATION_XZ:
      return ks_("Slice Orientation|Coronal");

    case vtkKW2DRenderWidget::SLICE_ORIENTATION_YZ:
      return ks_("Slice Orientation|Sagittal");
    }

  return NULL;
}

//----------------------------------------------------------------------------
int vtkKW2DRenderWidget::GetSliceOrientationFromMedicalOrientationString(
  const char *orient)
{
  // Any modifications made here should be propagated to both:
  // GetSliceOrientationAsMedicalOrientationString and
  // GetSliceOrientationFromMedicalOrientationString

  if (orient && *orient)
    {
    if (!strcmp(orient, ks_("Slice Orientation|Axial")))
      {
      return vtkKW2DRenderWidget::SLICE_ORIENTATION_XY;
      }

    if (!strcmp(orient, ks_("Slice Orientation|Coronal")))
      {
      return vtkKW2DRenderWidget::SLICE_ORIENTATION_XZ;
      }

    if (!strcmp(orient, ks_("Slice Orientation|Sagittal")))
      {
      return vtkKW2DRenderWidget::SLICE_ORIENTATION_YZ;
      }
    }

  return -1;
}

//----------------------------------------------------------------------------
const char* 
vtkKW2DRenderWidget::GetSliceOrientationAsDefaultOrientationString(int orient)
{
  // Any modifications made here should be propagated to both:
  // GetSliceOrientationAsDefaultOrientationString and
  // GetSliceOrientationFromDefaultOrientationString

  switch (orient)
    {
    case vtkKW2DRenderWidget::SLICE_ORIENTATION_XY:
      return "X-Y";

    case vtkKW2DRenderWidget::SLICE_ORIENTATION_XZ:
      return "X-Z";

    case vtkKW2DRenderWidget::SLICE_ORIENTATION_YZ:
      return "Y-Z";
    }

  return NULL;
}

//----------------------------------------------------------------------------
int vtkKW2DRenderWidget::GetSliceOrientationFromDefaultOrientationString(
  const char *orient)
{
  // Any modifications made here should be propagated to both:
  // GetSliceOrientationAsDefaultOrientationString and
  // GetSliceOrientationFromDefaultOrientationString

  if (orient && *orient)
    {
    if (!strcmp(orient, "X-Y"))
      {
      return vtkKW2DRenderWidget::SLICE_ORIENTATION_XY;
      }

    if (!strcmp(orient, "X-Z"))
      {
      return vtkKW2DRenderWidget::SLICE_ORIENTATION_XZ;
      }

    if (!strcmp(orient, "Y-Z"))
      {
      return vtkKW2DRenderWidget::SLICE_ORIENTATION_YZ;
      }
    }

  return -1;
}

//----------------------------------------------------------------------------
const char* vtkKW2DRenderWidget::GetSliceOrientationAsString()
{
  if (this->SliceType == vtkKW2DRenderWidget::SLICE_TYPE_DEFAULT)
    {
    return vtkKW2DRenderWidget::GetSliceOrientationAsDefaultOrientationString(
      this->SliceOrientation);
    }
  else if (this->SliceType == vtkKW2DRenderWidget::SLICE_TYPE_MEDICAL)
    {
    return vtkKW2DRenderWidget::GetSliceOrientationAsMedicalOrientationString(
      this->SliceOrientation);
    }

  return NULL;
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::SetSliceType(int type)
{
  if (type < vtkKW2DRenderWidget::SLICE_TYPE_DEFAULT ||
      type > vtkKW2DRenderWidget::SLICE_TYPE_GENERIC)
    {
    vtkErrorMacro("Error - invalid slice type " << type);
    return;
    }
  
  if (this->SliceType == type)
    {
    return;
    }

  this->SliceType = type;

  this->UpdateSliceOrientationAndType();

  this->Render();
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::SetInteractionMode(int type)
{
  if (this->InteractionMode == type)
    {
    return;
    }

  this->InteractionMode = type;

  this->InvokeEvent(
    vtkKWEvent::InteractionModeChangedEvent, &this->InteractionMode);
  
  this->ConfigureEventMap();
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::ToggleInterpolate()
{
  this->SetInterpolate(!this->GetInterpolate());
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::PopulateContextMenuWithOptionEntries(vtkKWMenu *menu)
{
  this->Superclass::PopulateContextMenuWithOptionEntries(menu);

  if (!menu)
    {
    return;
    }

  int tcl_major, tcl_minor, tcl_patch_level;
  Tcl_GetVersion(&tcl_major, &tcl_minor, &tcl_patch_level, NULL);
  int show_icons = (tcl_major > 8 || (tcl_major == 8 && tcl_minor >= 5));

  int index, cascade_index;

  // Interpolation Mode

  vtkKWMenu *interpolation_menu = vtkKWMenu::New();
  interpolation_menu->SetParent(this->ContextMenu);
  interpolation_menu->Create();

  cascade_index = interpolation_menu->AddRadioButton(
    "None", this, "SetInterpolate 0");
  interpolation_menu->SetItemSelectedValueAsInt(cascade_index, 0);
  if (show_icons)
    {
    interpolation_menu->SetItemImageToPredefinedIcon(
      cascade_index, vtkKWIcon::IconInterpolationNearest);
    interpolation_menu->SetItemCompoundModeToLeft(cascade_index);
    }

  cascade_index = interpolation_menu->AddRadioButton(
    "Linear", this, "SetInterpolate 1");
  interpolation_menu->SetItemSelectedValueAsInt(cascade_index, 1);
  if (show_icons)
    {
    interpolation_menu->SetItemImageToPredefinedIcon(
      cascade_index, vtkKWIcon::IconInterpolationBilinear);
    interpolation_menu->SetItemCompoundModeToLeft(cascade_index);
    }

  interpolation_menu->SelectItemWithSelectedValueAsInt(this->GetInterpolate());
  
  index = menu->AddCascade(k_("Interpolation"), interpolation_menu);
  if (show_icons)
    {
    if (this->GetInterpolate())
      {
      menu->SetItemImageToPredefinedIcon(
        index, vtkKWIcon::IconInterpolationBilinear);
      }
    else
      {
      menu->SetItemImageToPredefinedIcon(
        index, vtkKWIcon::IconInterpolationNearest);
      }
    menu->SetItemCompoundModeToLeft(index);
    }
  interpolation_menu->Delete();
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::PopulateContextMenuWithColorEntries(vtkKWMenu *menu)
{
  // Superclass is called at the end, so that the background color entry
  // remains at the bottom
  // this->Superclass::PopulateContextMenuWithColorEntries(menu);

  if (!menu)
    {
    return;
    }

  int tcl_major, tcl_minor, tcl_patch_level;
  Tcl_GetVersion(&tcl_major, &tcl_minor, &tcl_patch_level, NULL);
  int show_icons = (tcl_major > 8 || (tcl_major == 8 && tcl_minor >= 5));

  int index;

  // Reset Window/Level

  index = menu->AddCommand(k_("Reset Window/Level"), this, "ResetWindowLevel");
  if (show_icons)
    {
    menu->SetItemImageToPredefinedIcon(
      index, vtkKWIcon::IconResetContrast);
    menu->SetItemCompoundModeToLeft(index);
    }

  // Color mapping
  // Try all combination of opacity mod + disp channel
  // Include only the allowed ones

  vtkKWMenu *color_mapping_menu = vtkKWMenu::New();
  color_mapping_menu->SetParent(this->ContextMenu);
  color_mapping_menu->Create();

  vtkKWImageMapToWindowLevelColors *map = this->GetImageMapToRGBA();
  int op_mod, disp_ch, selected_icon = 0;
  for (op_mod = map->GetUseOpacityModulationMinValue(); 
       op_mod <= map->GetUseOpacityModulationMaxValue(); op_mod++)
    {
    for (disp_ch = map->GetDisplayChannelsMinValue(); 
         disp_ch <= map->GetDisplayChannelsMaxValue(); disp_ch++)
      {
      if (!map->IsValidCombination(disp_ch, op_mod))
        {
        continue;
        }

      // Create the command and command name

      int icon = 0;

      vtksys_ios::ostringstream cmd_name;
      switch (disp_ch)
        {
        case vtkKWImageMapToWindowLevelColors::DISPLAY_INTENSITIES:
          cmd_name << ks_("Color Mapping|Raw");
          icon = vtkKWIcon::IconGrayscaleSquares;
          break;
        case vtkKWImageMapToWindowLevelColors::DISPLAY_OPACITY:
          cmd_name << ks_("Color Mapping|Opacity");
          icon = vtkKWIcon::IconGrayscaleSquares;
          break;
        case vtkKWImageMapToWindowLevelColors::DISPLAY_POST_LUT_INTENSITIES:
          cmd_name << ks_("Color Mapping|Color Mapped");
          icon = vtkKWIcon::IconColorSquares;
          break;
        case vtkKWImageMapToWindowLevelColors::DISPLAY_GRAYSCALE_INTENSITIES:
          cmd_name << ks_("Color Mapping|Gray Scale");
          icon = vtkKWIcon::IconGrayscaleSquares;
          break;
        default:
          cmd_name << ks_("Color Mapping|Unknown channel");
          break;
        }
      switch (op_mod)
        {
        case 0:
          break;
        case 1:
          cmd_name << " - ";
          cmd_name << ks_("Color Mapping|Opacity Modulated");
          icon = vtkKWIcon::IconColorAlphaSquares;
          break;
        default:
          cmd_name << " - ";
          cmd_name << ks_("Color Mapping|Unknown Opacity Modulation");
          break;
        }

      vtksys_ios::ostringstream cmd;
      cmd << "ColorMappingCallback " << disp_ch << " " << op_mod;

      // Add a new entry

      int cascade_index = color_mapping_menu->AddRadioButton(
        cmd_name.str().c_str(), this, cmd.str().c_str());
      if (show_icons && icon)
        {
        color_mapping_menu->SetItemImageToPredefinedIcon(
          cascade_index, icon);
        color_mapping_menu->SetItemCompoundModeToLeft(cascade_index);
        }

      // If the entry we have added match the current value, set it at
      // the current entry

      if (disp_ch == map->GetDisplayChannels() &&
          op_mod == map->GetUseOpacityModulation())
        {
        color_mapping_menu->SetItemSelectedState(cascade_index, 1);
        selected_icon = icon;
        }
      }
    }

  index = menu->AddCascade(k_("Color Mapping"), color_mapping_menu);
  if (show_icons)
    {
    menu->SetItemImageToPredefinedIcon(index, selected_icon);
    menu->SetItemCompoundModeToLeft(index);
    }
  color_mapping_menu->Delete();

  this->Superclass::PopulateContextMenuWithColorEntries(menu);
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::PopulateContextMenuWithAnnotationEntries(vtkKWMenu *menu)
{
  this->Superclass::PopulateContextMenuWithAnnotationEntries(menu);

  if (!menu)
    {
    return;
    }

  int tcl_major, tcl_minor, tcl_patch_level;
  Tcl_GetVersion(&tcl_major, &tcl_minor, &tcl_patch_level, NULL);
  int show_icons = (tcl_major > 8 || (tcl_major == 8 && tcl_minor >= 5));

  int index;

  if (this->SupportSideAnnotation)
    {
    index = menu->AddCheckButton(
      ks_("Annotation|Side Annotation"),
      this, "ToggleSideAnnotationVisibility");
    menu->SetItemSelectedState(index, this->GetSideAnnotationVisibility());
    if (show_icons)
      {
      menu->SetItemImageToPredefinedIcon(index, vtkKWIcon::IconSideAnnotation);
      menu->SetItemCompoundModeToLeft(index);
      }
    }

  // Remove Orientation Cube Annotation
  // Heuristic: as a hint, if we have no slice control, then this is
  // problably not an oriented stack, and side annotation don't
  // mean much.

  if (this->HasSliceControl)
    {
    menu->DeleteItem(menu->GetIndexOfItem(ks_("Annotation|Orientation Cube")));
    }
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::PopulateContextMenuWithInteractionEntries(vtkKWMenu *menu)
{
  this->Superclass::PopulateContextMenuWithInteractionEntries(menu);

  if (!menu)
    {
    return;
    }

  const char *group_name = "InteractionMode";

  int tcl_major, tcl_minor, tcl_patch_level;
  Tcl_GetVersion(&tcl_major, &tcl_minor, &tcl_patch_level, NULL);
  int show_icons = (tcl_major > 8 || (tcl_major == 8 && tcl_minor >= 5));

  int index;

  index = menu->AddRadioButton(
    ks_("Interaction Mode|Window/Level"), 
    this, "SetInteractionModeToWindowLevel");
  menu->SetItemSelectedValueAsInt(
    index, vtkKW2DRenderWidget::INTERACTION_MODE_WINDOWLEVEL);
  menu->SetItemGroupName(index, group_name);
  if (show_icons)
    {
    menu->SetItemImageToPredefinedIcon(index, vtkKWIcon::IconContrast);
    menu->SetItemCompoundModeToLeft(index);
    }
  
  index = menu->AddRadioButton(
    ks_("Interaction Mode|Pan"), 
    this, "SetInteractionModeToPan");
  menu->SetItemSelectedValueAsInt(
    index, vtkKW2DRenderWidget::INTERACTION_MODE_PAN);
  menu->SetItemGroupName(index, group_name);
  if (show_icons)
    {
    menu->SetItemImageToPredefinedIcon(index, vtkKWIcon::IconPanHand);
    menu->SetItemCompoundModeToLeft(index);
    }
  
  index = menu->AddRadioButton(
    ks_("Interaction Mode|Zoom"), 
    this, "SetInteractionModeToZoom");
  menu->SetItemSelectedValueAsInt(
    index, vtkKW2DRenderWidget::INTERACTION_MODE_ZOOM);
  menu->SetItemGroupName(index, group_name);
  if (show_icons)
    {
    menu->SetItemImageToPredefinedIcon(
      index, vtkKWIcon::IconNuvola16x16ActionsViewMag);
    menu->SetItemCompoundModeToLeft(index);
    }
  
  menu->SelectItemInGroupWithSelectedValueAsInt(
    "InteractionMode", this->InteractionMode);
}

//----------------------------------------------------------------------------
int vtkKW2DRenderWidget::GetSlice()
{
  return vtkMath::Round(this->SliceScale->GetValue());
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::SliceSelectedCallback(double value)
{
  this->SetSlice(vtkMath::Round(value));
}

//---------------------------------------------------------------------------
void vtkKW2DRenderWidget::ColorMappingCallback(
  int disp_ch, int use_op_mod)
{
  this->SetDisplayChannels(disp_ch);
  this->SetUseOpacityModulation(use_op_mod);
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::SetHasSliceControl(int arg)
{
  if (this->HasSliceControl == arg)
    {
    return;
    }

  this->HasSliceControl = arg;
  this->Modified();

  this->PackSliceScale();
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::UpdateSliceScale()
{
  if (this->SliceScale->GetOrientation() == 
      vtkKWOptions::OrientationVertical)
    {
    this->SliceScale->SetRange(this->GetSliceMax(), this->GetSliceMin());
    }
  else
    {
    this->SliceScale->SetRange(this->GetSliceMin(), this->GetSliceMax());
    }
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::IncrementSlice()
{
  this->SetSlice(this->GetSlice() + 1);
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::DecrementSlice()
{
  this->SetSlice(this->GetSlice() - 1);
}

//----------------------------------------------------------------------------
int vtkKW2DRenderWidget::GetSliceMin()
{
  if (!this->Input)
    {
    return 0;
    }
  this->Input->UpdateInformation();
  return this->Input->GetWholeExtent()[this->SliceOrientation * 2];
}

//----------------------------------------------------------------------------
int vtkKW2DRenderWidget::GetSliceMax()
{
  if (!this->Input)
    {
    return 0;
    }
  this->Input->UpdateInformation();
  return this->Input->GetWholeExtent()[this->SliceOrientation * 2 + 1];
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::GoToFirstSlice()
{
  this->SetSlice(this->GetSliceMin());
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::GoToLastSlice()
{
  this->SetSlice(this->GetSliceMax());
}

//----------------------------------------------------------------------------
int vtkKW2DRenderWidget::GoToDefaultSlice()
{
  this->SetSlice(
    static_cast<int>(
      ((double)this->GetSliceMin() + (double)this->GetSliceMax()) * 0.5));
  return this->GetSlice();
}

//----------------------------------------------------------------------------
int vtkKW2DRenderWidget::IsSliceInRange(int slice)
{
  int min_slice = this->GetSliceMin();
  int max_slice = this->GetSliceMax();
  if (min_slice > max_slice)
    {
    int temp = min_slice;
    min_slice = max_slice;
    max_slice = temp;
    }
  return (slice >= min_slice && slice <= max_slice);
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::GetSliceDisplayExtent(int slice, int extent[6])
{
  if (this->Input)
    {
    this->Input->GetWholeExtent(extent);
    extent[this->GetSliceOrientation() * 2] = 
      extent[this->GetSliceOrientation() * 2 + 1] = slice;
    }
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::GetSliceWorldExtent(int slice, double extent[6])
{
  if (this->Input)
    {
    int iextent[6];
    double *origin = this->Input->GetOrigin();
    double *spacing = this->Input->GetSpacing();
    this->GetSliceDisplayExtent(slice, iextent);
    for (int i = 0; i < 3; i++)
      {
      extent[i * 2] = origin[i] + (double)iextent[i * 2] * spacing[i];
      extent[i * 2 + 1] = origin[i] + (double)iextent[i * 2 + 1] * spacing[i];
      }
    }
}

//----------------------------------------------------------------------------
int vtkKW2DRenderWidget::GetClosestSliceToWorldPosition(double pos[3])
{
  if (this->Input)
    {
    double *origin = this->Input->GetOrigin();
    double *spacing = this->Input->GetSpacing();
    int o = this->GetSliceOrientation();
    int slice = static_cast<int>(((pos[o] - origin[o]) / spacing[o]) + 0.5);
    if (slice < this->GetSliceMin())
      {
      slice = this->GetSliceMin();
      }
    else if (slice > this->GetSliceMax())
      {
      slice = this->GetSliceMax();
      }
    return slice;
    }
  return -1;
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::ComputeVisiblePropBounds(int index, double bounds[6])
{
  if (this->Input)
    {
    this->Input->UpdateInformation();
    int *wExt = this->Input->GetWholeExtent();
    double *spc = this->Input->GetSpacing();
    double *org = this->Input->GetOrigin();
    bounds[0] = ((double)org[0] + (double)wExt[0] * (double)spc[0]);
    bounds[1] = ((double)org[0] + (double)wExt[1] * (double)spc[0]);
    bounds[2] = ((double)org[1] + (double)wExt[2] * (double)spc[1]);
    bounds[3] = ((double)org[1] + (double)wExt[3] * (double)spc[1]);
    bounds[4] = ((double)org[2] + (double)wExt[4] * (double)spc[2]);
    bounds[5] = ((double)org[2] + (double)wExt[5] * (double)spc[2]);
    }
  else
    {  
    this->Superclass::ComputeVisiblePropBounds(index, bounds);
    }
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::UpdateEnableState()
{
  this->Superclass::UpdateEnableState();

  this->PropagateEnableState(this->SliceScale);
}

//---------------------------------------------------------------------------
void vtkKW2DRenderWidget::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  
  os << indent << "SliceOrientation: " << this->SliceOrientation << endl;
  os << indent << "SliceType: " << this->SliceType << endl;
  os << indent << "InteractionMode: " << this->InteractionMode << endl;
  os << indent << "SupportSideAnnotation: " 
    << (this->SupportSideAnnotation ? "On" : "Off") << endl;
  os << indent << "SideAnnotation: " << this->SideAnnotation <<endl;
  os << indent << "ImageMapToRGBA: " << this->ImageMapToRGBA <<endl;
  os << indent << "HasSliceControl: " 
     << (this->HasSliceControl ? "On" : "Off") <<endl;
  os << indent << "ResetWindowLevelForSelectedSliceOnly: "
     << (this->ResetWindowLevelForSelectedSliceOnly ? "On" : "Off") << endl;
  os << indent << "SliceScale: " << this->SliceScale <<endl;
  os << indent << "CoordinateSystemMedical: "
               << this->CoordinateSystemMedical << endl;
}

//----------------------------------------------------------------------------
void vtkKW2DRenderWidget::SetCoordinateSystemMedical( int c )
{
  if (   c < vtkKW2DRenderWidget::COORDINATE_SYSTEM_MEDICAL_LPS
      || c > vtkKW2DRenderWidget::COORDINATE_SYSTEM_MEDICAL_RAS)
    {
    vtkErrorMacro("Error - coordinate system not supported ");
    return;
    }

  if (this->CoordinateSystemMedical == c)
    {
    return;
    }

  this->CoordinateSystemMedical = c;
  this->ResetCamera();
}

