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

  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 "vtkKWVolumeWidget.h"

#include "vtk3DCursorAnnotation.h"
#include "vtkActor.h"
#include "vtkBoundingBoxAnnotation.h"
#include "vtkCamera.h"
#include "vtkCellArray.h"
#include "vtkColorTransferFunction.h"
#include "vtkEncodedGradientEstimator.h"
#include "vtkFiniteDifferenceGradientEstimator.h"
#include "vtkImageActor.h"
#include "vtkImageData.h"
#include "vtkImageResample.h"
#include "vtkImplicitPlaneWidget.h"
#include "vtkVolume.h"
#include "vtkLight.h"
#include "vtkLightCollection.h"
#include "vtkMath.h"
#include "vtkCollection.h"
#include "vtkObjectFactory.h"
#include "vtkPiecewiseFunction.h"
#include "vtkPlane.h"
#include "vtkPlaneCollection.h"
#include "vtkPointData.h"
#include "vtkPolyData.h"
#include "vtkPolyDataMapper.h"
#include "vtkPolyDataSource.h"
#include "vtkProperty.h"
#include "vtkProperty2D.h"
#include "vtkRecursiveSphereDirectionEncoder.h"
#include "vtkRenderWindow.h"
#include "vtkRenderer.h"
#include "vtkRendererCollection.h"
#include "vtkScalarBarActor.h"
#include "vtkSurfaceAnnotation.h"
#include "vtkTimerLog.h"
#include "vtkTransform.h"
#include "vtkVolumeProperty.h"

#include "vtkKW3DSplineCurvesWidget.h"
#include "vtkKW3DSplineSurfacesWidget.h"
#include "vtkKWApplication.h"
#include "vtkKWApplicationPro.h"
#include "vtkKWEVolumeMapper.h"
#include "vtkKWEvent.h"
#include "vtkKWEventMap.h"
#include "vtkKWFrame.h"
#include "vtkKWGenericRenderWindowInteractor.h"
#include "vtkKWHistogram.h"
#include "vtkKWHistogramSet.h"
#include "vtkKWIcon.h"
#include "vtkKWInteractorStyleVolumeView.h"
#include "vtkKWInternationalization.h"
#include "vtkKWMenu.h"
#include "vtkKWMessageDialog.h"
#include "vtkKWOrientationWidget.h"
#include "vtkKWProgressCommand.h"
#include "vtkKWScalarBarWidget.h"
#include "vtkKWScaleBarWidget.h"
#include "vtkKWVolumePropertyHelper.h"
#include "vtkKWTkUtilities.h"
#include "vtkKWWindow.h"

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

#ifdef KWCommonPro_USE_XML_RW
#include "XML/vtkXMLKWVolumeWidgetReader.h"
#include "XML/vtkXMLKWVolumeWidgetWriter.h"
#endif
vtkKWGetXMLReaderWriterObjectsImplementationMacro(vtkKWVolumeWidget, vtkXMLKWVolumeWidgetReader, vtkXMLKWVolumeWidgetWriter);

//---------------------------------------------------------------------------
vtkStandardNewMacro(vtkKWVolumeWidget);
vtkCxxRevisionMacro(vtkKWVolumeWidget, "$Revision: 1.361 $");

vtkSetObjectImplementationMacro(vtkKWVolumeWidget, HistogramSet, vtkKWHistogramSet);

//----------------------------------------------------------------------------
class vtkKWVolumeWidgetInternals
{
public:
  vtkKWVolumeWidgetInternals();

  int InRender;
  int InLevelOfDetailRender;
};

vtkKWVolumeWidgetInternals::vtkKWVolumeWidgetInternals()
{
  this->InRender = 0;
  this->InLevelOfDetailRender = 0;
}

//----------------------------------------------------------------------------
void KWVolumeWidget_IdleRender(ClientData arg)
{
  vtkKWVolumeWidget *me = (vtkKWVolumeWidget *)arg;
  me->IdleRenderCallback();
}

//----------------------------------------------------------------------------
vtkKWVolumeWidget::vtkKWVolumeWidget()
{
  this->Internals = new vtkKWVolumeWidgetInternals;

  this->Volume = vtkVolume::New();
  
  this->VolumeMapper = vtkKWEVolumeMapper::New();
  
  this->Volume->SetMapper(this->VolumeMapper);
  this->Volume->SetProperty(this->VolumeProperty);
  
  this->InteractiveUpdateRate = 8.0;

  vtkRenderWindowInteractor *interactor = this->GetRenderWindowInteractor();
  
  this->ScaleBarWidget = vtkKWScaleBarWidget::New();
  this->ScaleBarWidget->SetInteractor(interactor);
  this->ScaleBarWidget->SetParent(this);
  this->ScaleBarWidget->RepositionableOff();
  
  vtkScalarBarActor *scalarBar = vtkScalarBarActor::New();
  this->ScalarBarWidget = vtkKWScalarBarWidget::New();
  this->ScalarBarWidget->SetScalarBarActor(scalarBar);
  this->ScalarBarWidget->RepositionableOff();

  scalarBar->SetLookupTable(this->VolumeProperty->GetRGBTransferFunction(0));
  scalarBar->SetLabelFormat("%.5g");
  scalarBar->Delete();
  this->ScalarBarWidget->SetInteractor(interactor);
  scalarBar->GetPositionCoordinate()->SetValue(0.04,0.25);
  scalarBar->GetPosition2Coordinate()->SetValue(0.13,0.63);
  scalarBar->SetOrientation(1);

  this->PlaneWidget = vtkImplicitPlaneWidget::New();
  this->PlaneWidget->SetInteractor(interactor);
  this->PlaneWidget->GetPlaneProperty()->SetAmbient(1);
  this->PlaneWidget->GetPlaneProperty()->SetDiffuse(0);
  this->PlaneWidget->SetPlaceFactor(1.0);
  // Disable any potential advanced user feature
  this->PlaneWidget->OriginTranslationOff ();
  this->PlaneWidget->OutsideBoundsOff();
  this->PlaneWidget->SetDiagonalRatio(0.41);
  this->PlaneWidget->OutlineTranslationOff ();
  this->PlaneWidget->ScaleEnabledOff();
  this->PlaneWidget->DrawPlaneOff();
  // Slightly more that the diagonal in case of opaque volume

  this->Reformat = 0;
  this->ReformatThickness = 10.0;
  this->ReformatBoxVisibility = 1;
  this->ReformatManipulationStyle =
    vtkKWVolumeWidget::CAMERA_MANIPULATION_STYLE;

  this->ReformatNormal[0] = 0;
  this->ReformatNormal[1] = 0;
  this->ReformatNormal[2] = 1;
  this->ReformatUp[0] = 0;
  this->ReformatUp[1] = 1;
  this->ReformatUp[2] = 0;
  this->ReformatLocation[0] = 0;
  this->ReformatLocation[1] = 0;
  this->ReformatLocation[2] = 0;
  
  vtkPoints *pts = vtkPoints::New();
  this->ReformatData = vtkPolyData::New();
  this->ReformatData->SetPoints(pts);
  pts->Delete();
  vtkCellArray *ca = vtkCellArray::New();
  this->ReformatData->SetLines(ca);
  ca->Delete();
  this->ReformatPlane = vtkActor::New();
  this->ReformatPlane->GetProperty()->SetColor(1.0, 1.0, 0.0);
  this->ReformatPlane->GetProperty()->SetAmbient(1.0);
  this->ReformatPlane->GetProperty()->SetDiffuse(0.0);
  this->ReformatPlane->GetProperty()->SetSpecular(0.0);
  vtkPolyDataMapper *mapper = vtkPolyDataMapper::New();
  mapper->SetInput(this->ReformatData);
  this->ReformatPlane->SetMapper(mapper);
  mapper->Delete();
  
  this->ReformatTransform = vtkTransform::New();
  
  this->LastPosition[0] = 0;
  this->LastPosition[1] = 0;
  
  this->ZSampling = 2;
  
  this->UseCursor3D = 0;
  
  this->InteractorStyle = vtkKWInteractorStyleVolumeView::New();
  
  this->CurrentLight = NULL;
  this->RenderTimer = vtkTimerLog::New();
  this->TimerToken = NULL;
  this->SingleUpdateRate = 0.003;

  this->SurfaceAnnotation = vtkSurfaceAnnotation::New();
  this->SurfaceAnnotation->SetRenderWidget(this);

  this->BoundingBoxAnnotation = vtkBoundingBoxAnnotation::New();
  this->BoundingBoxAnnotation->VisibilityOff();
  
  this->Cursor3DAnnotation = vtk3DCursorAnnotation::New();
  this->Cursor3DAnnotation->VisibilityOff();
  this->Cursor3DAnnotation->SetRenderWidget(this);
  
  this->HistogramSet = 0;
  
  this->InteractionMode = vtkKWVolumeWidget::INTERACTION_MODE_ALL;

  // Set the Zoom Factor to 85%
  this->ResetCameraZoomRatio = 0.85;

  this->SetRendererBackgroundColorRegKey(
    "VolumeWidgetRendererBackgroundColor");
  this->SetRendererBackgroundColor2RegKey(
    "VolumeWidgetRendererBackgroundColor2");
  this->SetRendererGradientBackgroundRegKey(
    "VolumeWidgetRendererGradientBackground");
}

//----------------------------------------------------------------------------
vtkKWVolumeWidget::~vtkKWVolumeWidget()
{
  delete this->Internals;

  if ( this->Volume )
    {
    this->Volume->Delete();
    this->Volume = NULL;
    }

  if ( this->VolumeMapper )
    {
    this->VolumeMapper->Delete();
    this->VolumeMapper = NULL;
    }

  if (this->ScaleBarWidget)
    {
    this->ScaleBarWidget->SetInteractor(NULL);
    this->ScaleBarWidget->Delete();
    this->ScaleBarWidget = NULL;
    }

  if (this->ScalarBarWidget)
    {
    this->ScalarBarWidget->Delete();
    this->ScalarBarWidget = NULL;
    }

  if (this->PlaneWidget)
    {
    this->PlaneWidget->Delete();
    this->PlaneWidget = NULL;
    }

  if (this->ReformatData)
    {
    this->ReformatData->Delete();
    this->ReformatData = NULL;
    }

  if (this->ReformatPlane)
    {
    this->ReformatPlane->Delete();
    this->ReformatPlane = NULL;
    }

  if (this->ReformatTransform)
    {
    this->ReformatTransform->Delete();
    this->ReformatTransform = NULL;
    }

  if (this->InteractorStyle)
    {
    this->InteractorStyle->Delete();
    this->InteractorStyle = NULL;
    }

  if (this->RenderTimer)
    {
    this->RenderTimer->Delete();
    this->RenderTimer = NULL;
    }
  
  if (this->SurfaceAnnotation)
    {
    this->SurfaceAnnotation->Delete();
    this->SurfaceAnnotation = NULL;
    }

  if (this->BoundingBoxAnnotation)
    {
    this->BoundingBoxAnnotation->Delete();
    this->BoundingBoxAnnotation = NULL;
    }

  if (this->Cursor3DAnnotation)
    {
    this->Cursor3DAnnotation->Delete();
    this->Cursor3DAnnotation = NULL;
    }

  if (this->TimerToken)
    {
    Tcl_DeleteTimerHandler( this->TimerToken );
    this->TimerToken = NULL;
    }
  
  this->SetHistogramSet(0);
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::CreateDefaultRenderers()
{
  this->Superclass::CreateDefaultRenderers();

  vtkRenderer *ren = this->GetRenderer();
  if (ren)
    {
    ren->TwoSidedLightingOn();

    vtkLight *light1 = vtkLight::New();
    light1->SetLightTypeToCameraLight();
    light1->SetPosition(0, 0, 1);
    ren->AddLight(light1);
    light1->Delete();  
    ((vtkLight*)ren->GetLights()->GetItemAsObject(0))
      ->SetLightTypeToCameraLight();
    }
}

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

  this->Superclass::ConfigureEventMap();

  // Configure the volume-widget specific settings

  switch (this->InteractionMode)
    {
    case vtkKWVolumeWidget::INTERACTION_MODE_PAN:
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::LeftButton,vtkKWEventMap::NoModifier, "Pan");
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::LeftButton,vtkKWEventMap::ShiftModifier, "Rotate");
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::LeftButton,vtkKWEventMap::ControlModifier, "Zoom");

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

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

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

    case vtkKWVolumeWidget::INTERACTION_MODE_ROTATE:
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::LeftButton,vtkKWEventMap::NoModifier, "Rotate");
      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 vtkKWVolumeWidget::INTERACTION_MODE_ALL:
    default:
      this->EventMap->AddMouseEvent(
        vtkKWEventMap::LeftButton,vtkKWEventMap::NoModifier, "Rotate");
      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,"Rotate");
      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,"Rotate");
      break;
    }
  
  /*
  this->EventMap->AddMouseEvent(
    vtkKWEventMap::LeftButton, vtkKWEventMap::ShiftModifier, "Roll");
  this->EventMap->AddMouseEvent(
    vtkKWEventMap::MiddleButton, vtkKWEventMap::ShiftModifier, "FlyIn");
  this->EventMap->AddMouseEvent(
    vtkKWEventMap::RightButton, vtkKWEventMap::ShiftModifier, "FlyOut");
  */

  const char *context = "3D view";

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

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

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

  // Cursor 3D

  if (this->Cursor3DAnnotation)
    {
    this->Cursor3DAnnotation->SetInput(this->Input);
    this->Cursor3DAnnotation->Modified();
    }

  // 3D Widgets

  if (this->PlaneWidget)
    {
    this->PlaneWidget->SetInput(this->Input);
    this->PlaneWidget->Modified();
    }

  // We need an input to do the rest

  vtkCollection *mappers = vtkCollection::New();
  this->GetAllVolumeMappers(mappers);

  int i;
  for (i = 0; i < mappers->GetNumberOfItems(); i++)
    {
    vtkVolumeMapper *vmapper = vtkVolumeMapper::SafeDownCast(
      mappers->GetItemAsObject(i));
    if (vmapper)
      {
      vmapper->SetInput(this->Input);
      }
    }
  mappers->Delete();
  mappers = NULL;

  if (!this->Input)
    {
    this->GetRenderer()->RemoveViewProp(this->Volume);
    return 1;
    }

  this->Input->Update();
  this->VolumeMapper->Modified();
  
  this->GetRenderer()->AddViewProp(this->Volume);
  
  return 1;
}

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

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

  // If we have more than 2 dependent components, do not display any 
  // scalar bar (would make no sense)
  
  int nb_components = 
    this->Input ? this->Input->GetNumberOfScalarComponents() : 0;

  if (!this->GetIndependentComponents() && nb_components > 2)
    {
    this->SetScalarBarVisibility(0);
    }

  // Apply default presets

  this->ApplyDefaultPreset();

  return 1;
}

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

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

  // We have to recreate the pipeline since LOD volumes depend
  // on the Input dimension, which is likely to have changed
  // Note that if the scalar structure has changed too, 
  // UpdateAccordingToInput() will call both InputScalarStructureHasChanged()
  // and InputBoundsHaveChanged(), so ultimately ConnectInternalPipeline()
  // will be called twice, which is OK, we can live with that at the moment.

  this->ConnectInternalPipeline();

  // We need an input to do the rest

  if (!this->Input)
    {
    return 1;
    }

  double bounds[6];
  this->Input->GetBounds(bounds);

  // Update reformatthickness

  double diagonal = sqrt(
    ((double)bounds[1] - (double)bounds[0]) * 
    ((double)bounds[1] - (double)bounds[0]) +
    ((double)bounds[3] - (double)bounds[2]) * 
    ((double)bounds[3] - (double)bounds[2]) +
    ((double)bounds[5] - (double)bounds[4]) * 
    ((double)bounds[5] - (double)bounds[4]));
  
  this->SetReformatThickness(diagonal * 0.10);

  // Update the extents to the whole extent

  int i;
  vtkCollection *mappers = vtkCollection::New();
  this->GetAllVolumeMappers(mappers);

  for (i = 0; i < mappers->GetNumberOfItems(); i++)
    {
    vtkVolumeMapper *vmapper = vtkVolumeMapper::SafeDownCast(
      mappers->GetItemAsObject(i));
    if (vmapper)
      {
      vmapper->GetInput()->UpdateInformation();
      vmapper->GetInput()->SetUpdateExtent(
        vmapper->GetInput()->GetWholeExtent());
      vmapper->GetInput()->Update();
      }
    }

  // Set the cropping planes to the bounds
  
  for (i = 0; i < mappers->GetNumberOfItems(); i++)
    {
    vtkVolumeMapper *vmapper = vtkVolumeMapper::SafeDownCast(
      mappers->GetItemAsObject(i));
    if (vmapper)
      {
      vmapper->SetCroppingRegionPlanes(
        bounds[0], bounds[1], bounds[2], bounds[3], bounds[4], bounds[5]);
      }
    }

  mappers->Delete();
  mappers = NULL;

  // Place the bounding box

  this->BoundingBoxAnnotation->SetInputBounds(
    bounds[0], bounds[1], bounds[2], bounds[3], bounds[4], bounds[5]);

  // Reset the cursor 3D

  this->SetCursor3DPosition(
    ((double)bounds[0] + (double)bounds[1]) * 0.5,
    ((double)bounds[2] + (double)bounds[3]) * 0.5,
    ((double)bounds[4] + (double)bounds[5]) * 0.5);

  // Place the plane widget

  this->PlaneWidget->PlaceWidget(
    bounds[0], bounds[1], bounds[2], bounds[3], bounds[4], bounds[5]);
  // Set it to Axial by default
  this->PlaneWidget->SetNormal(0,0,1);
  this->PlaneWidget->SetOrigin(
    ((double)bounds[0] + (double)bounds[1]) * 0.5,
    ((double)bounds[2] + (double)bounds[3]) * 0.5,
    ((double)bounds[4] + (double)bounds[5]) * 0.5);

  // Recompute the reformat plane

  if (this->Reformat)
    {
    double *center = this->Input->GetCenter();
    this->ReformatLocation[0] = center[0];
    this->ReformatLocation[1] = center[1];
    this->ReformatLocation[2] = center[2];
    this->ReformatNormal[0] = 0;
    this->ReformatNormal[1] = 0;
    this->ReformatNormal[2] = 1;
    this->RecomputeReformatPlane();
    }

  // Place the surface widget
  
  if( this->GetSplineSurfaces3D() )
    {
    this->GetSplineSurfaces3D()->PlaceWidget( bounds );
    }

  // Set up the sample distance

  this->UpdateAccordingToInputSpacing();

  // Reset the camera
  
  this->ResetCamera();

  return 1;
}

//----------------------------------------------------------------------------
int vtkKWVolumeWidget::InputHasChanged(int mask)
{
  // Have a look at the documentation for this method (see .h).

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

  // Need to make sure we are using the overlay renderer

  if (this->Input)
    {
    if (this->ScaleBarWidget)
      {
      this->ScaleBarWidget->SetDefaultRenderer(this->GetOverlayRenderer());
      }
    if (this->ScalarBarWidget)
      {
      this->ScalarBarWidget->SetDefaultRenderer(this->GetOverlayRenderer());
      this->ScalarBarWidget->GetScalarBarActor()->SetLookupTable(
        this->VolumeProperty->GetRGBTransferFunction(
          this->GetScalarBarComponent()));
      }
    }

  // Get rid of the surface

  if (this->SurfaceAnnotation)
    {
    this->SurfaceAnnotation->Remove();
    }

  return 1;
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::UpdateAccordingToUnits()
{
  this->ScaleBarWidget->SetDistanceUnits(this->DistanceUnits);
} 

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::UpdateAccordingToInputSpacing()
{
  if (!this->Input)
    {
    return;
    }

  double *spacing = this->Input->GetSpacing();
  double avgSpacing = 
    ((double)spacing[0] + (double)spacing[1] + (double)spacing[2] ) / 3.0;
  double newSpacing = avgSpacing / (double)this->ZSampling;

  // Should need to do anything here - mapper should take care of it all
  // TODO - remove this whole method one we move the volume property scalar
  // opacity distance adjustment is moved into the volume mapper

  // Set up the scalar opacity unit distance

  if (this->VolumeProperty)
    {
    for (int i = 0; i < VTK_MAX_VRCOMP; i++)
      {
      this->VolumeProperty->SetScalarOpacityUnitDistance(i, avgSpacing);
      }
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::GetAllVolumeMappers(vtkCollection *mappers)
{
  if (!mappers)
    {
    return;
    }

  if (this->VolumeMapper)
    {
    mappers->AddItem(this->VolumeMapper);
    }
}

//---------------------------------------------------------------------------
void vtkKWVolumeWidget::SetWindowLevel(double window, double level)
{
  if (this->Window == window && this->Level == level)
    {
    return;
    }

  this->Window = window;
  this->Level = level;
  this->Modified();

  this->UpdateColorMapping();

  // At this point the W/L is only used in the color map of the
  // planewidget, so re-render only if the widget is visible, to avoid
  // the volume to be re-rendered too often. In many cases, the application
  // will set the W/L on all its renderwidgets. For 2D ones, this is
  // a fast operation, and you should expect switching from one W/L preset
  // to another one to be as fast as possible. Re-rendering the volume
  // breaks this interactivity.

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

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

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

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

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

  this->Superclass::CreateWidget();

  vtkKWApplicationPro *app_pro = 
    vtkKWApplicationPro::SafeDownCast(this->GetApplication());
  if (app_pro)
    {
    this->VolumeMapper->SetRequestedRenderMode(
      app_pro->GetUseGPURendering() ? vtkKWEVolumeMapper::DefaultRenderMode
      : vtkKWEVolumeMapper::RayCastAndTextureRenderMode);
    // : vtkKWEVolumeMapper::RayCastRenderMode);
    }

  this->ScaleBarWidget->SetApplication(this->GetApplication());
  
  this->UpdateAccordingToUnits();
  
  this->InteractorStyle->SetEventMap(this->EventMap);

  vtkRenderWindowInteractor *interactor = this->GetRenderWindowInteractor();
  if (interactor)
    {
    interactor->SetInteractorStyle(this->InteractorStyle);
    }
  
  // Make sure the color theme is set for the plane widget 

  this->SetPlaneWidgetColor(this->GetPlaneWidgetColor());

  this->SetOrientationMarkerVisibility(1);
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::Close()
{
  this->Superclass::Close();

  // Get rid of the surface

  if (this->SurfaceAnnotation)
    {
    this->SurfaceAnnotation->Remove();
    }

  // Break the loop to the overlay renderer

  if (this->ScaleBarWidget)
    {
    this->ScaleBarWidget->SetDefaultRenderer(NULL);
    }
  if (this->ScalarBarWidget)
    {
    this->ScalarBarWidget->SetDefaultRenderer(NULL);
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetZSampling(int s)
{
  if (this->ZSampling == s)
    {
    return;
    }

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

  this->UpdateAccordingToInputSpacing();
  
  this->Render();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::AddVolumeMapperProgress(
  vtkVolumeMapper *mapper, const char *message)
{
  if (mapper && 
      !mapper->HasObserver(vtkCommand::VolumeMapperRenderStartEvent))
    {
    // Instead of listening to the mapper directly, we will forward the
    // mapper events ourselves, so that the vtkKWProgressCommand can
    // query our RenderMode

    vtkKWProgressCommand *cb = vtkKWProgressCommand::New();
    cb->SetWindow(vtkKWWindowBase::SafeDownCast(this->GetParentTopLevel()));
    cb->SetStartMessage(message);
    cb->SetStartEvent(vtkCommand::VolumeMapperRenderStartEvent);
    cb->SetEndEvent(vtkCommand::VolumeMapperRenderEndEvent);
    cb->SetProgressEvent(vtkCommand::VolumeMapperRenderProgressEvent);
    cb->SetRetrieveProgressMethodToCallData();
    this->AddObserver(cb->GetStartEvent(), cb);
    this->AddObserver(cb->GetEndEvent(), cb);
    this->AddObserver(cb->GetProgressEvent(), cb);
    cb->Delete();

    this->AddCallbackCommandObserver(
      mapper, vtkCommand::VolumeMapperRenderStartEvent);
    this->AddCallbackCommandObserver(
      mapper, vtkCommand::VolumeMapperRenderEndEvent);
    this->AddCallbackCommandObserver(
      mapper, vtkCommand::VolumeMapperRenderProgressEvent);
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::RemoveVolumeMapperProgress(
  vtkVolumeMapper *mapper)
{
  if (mapper && 
      mapper->HasObserver(vtkCommand::VolumeMapperRenderStartEvent))
    {
    this->RemoveObservers(vtkCommand::VolumeMapperRenderStartEvent);
    this->RemoveObservers(vtkCommand::VolumeMapperRenderEndEvent);
    this->RemoveObservers(vtkCommand::VolumeMapperRenderProgressEvent);

    this->RemoveCallbackCommandObserver(
      mapper, vtkCommand::VolumeMapperRenderStartEvent);
    this->RemoveCallbackCommandObserver(
      mapper, vtkCommand::VolumeMapperRenderEndEvent);
    this->RemoveCallbackCommandObserver(
      mapper, vtkCommand::VolumeMapperRenderProgressEvent);
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::AddVolumeMapperGradientProgress(
  vtkVolumeMapper *mapper, const char *message)
{
  if (mapper && 
      !mapper->HasObserver(vtkCommand::VolumeMapperComputeGradientsStartEvent))
    {
    vtkKWProgressCommand *cb = vtkKWProgressCommand::New();
    cb->SetWindow(vtkKWWindowBase::SafeDownCast(this->GetParentTopLevel()));
    cb->SetStartMessage(message);
    cb->SetStartEvent(vtkCommand::VolumeMapperComputeGradientsStartEvent);
    cb->SetEndEvent(vtkCommand::VolumeMapperComputeGradientsEndEvent);
    cb->SetProgressEvent(vtkCommand::VolumeMapperComputeGradientsProgressEvent);
    cb->SetRetrieveProgressMethodToCallData();
    mapper->AddObserver(cb->GetStartEvent(), cb);
    mapper->AddObserver(cb->GetEndEvent(), cb);
    mapper->AddObserver(cb->GetProgressEvent(), cb);
    cb->Delete();
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::RemoveVolumeMapperGradientProgress(
  vtkVolumeMapper *mapper)
{
  if (mapper && 
      mapper->HasObserver(vtkCommand::VolumeMapperComputeGradientsStartEvent))
    {
    mapper->RemoveObservers(
      vtkCommand::VolumeMapperComputeGradientsStartEvent);
    mapper->RemoveObservers(
      vtkCommand::VolumeMapperComputeGradientsEndEvent);
    mapper->RemoveObservers(
      vtkCommand::VolumeMapperComputeGradientsProgressEvent);
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::LevelOfDetailRender()
{
  if ( !this->VolumeMapper ||
       !this->VolumeMapper->GetInput() ||
       !this->Volume ||
       !this->Volume->GetVisibility() )
    {
    if (this->RenderWindow)
      {
      this->RenderWindow->Render();
      }
    return;
    }
  
  if (this->Internals->InLevelOfDetailRender)
    {
    return;
    }
  this->Internals->InLevelOfDetailRender = 1;

  this->RenderWindow->Render();

  this->Internals->InLevelOfDetailRender = 0;
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::ResetCamera()
{
  int i, nb_renderers = this->GetNumberOfRenderers();
  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, viewAngle, *vup;

      // Check Y and Z for the Y axis on the window

      width = (bounds[3] - bounds[2]) / aspect[1];

      if (((bounds[5] - bounds[4]) / aspect[1]) > width)
        {
        width = (bounds[5] - bounds[4]) / aspect[1];
        }

      // Check X and Y for the X axis on the window

      if (((bounds[1] - bounds[0]) / aspect[0]) > width)
        {
        width = (bounds[1] - bounds[0]) / aspect[0];
        }

      if (((bounds[3] - bounds[2]) / aspect[0]) > width)
        {
        width = (bounds[3] - bounds[2]) / aspect[0];
        }

      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->ResetCameraZoomRatio);
      }
    }

  this->ResetCameraClippingRange();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::Render()
{
  if (this->CollapsingRenders)
    {
    this->CollapsingRendersCount++;
    return;
    }

  if (!this->RenderState)
    {
    return;
    }

  if (this->Internals->InRender)
    {
    return;
    }
  this->Internals->InRender = 1;

  double vec[3];
  double args[10];

  vtkCamera* cam = this->GetRenderer()->GetActiveCamera();
  cam->GetPosition(vec);
  args[0 + 0] = vec[0];
  args[0 + 1] = vec[1];
  args[0 + 2] = vec[2];

  cam->GetFocalPoint(vec);
  args[3 + 0] = vec[0];
  args[3 + 1] = vec[1];
  args[3 + 2] = vec[2];

  cam->GetViewUp(vec);
  args[6 + 0] = vec[0];
  args[6 + 1] = vec[1];
  args[6 + 2] = vec[2];

  args[9] = cam->GetParallelScale();
  this->InvokeEvent(vtkKWEvent::RenderEvent, args );
  
  if (this->CurrentLight &&
      this->CurrentLight->GetLightType() != VTK_LIGHT_TYPE_CAMERA_LIGHT)
    {
    this->CurrentLight->SetPosition(cam->GetPosition());
    this->CurrentLight->SetFocalPoint(cam->GetFocalPoint());
    }

  if ( this->RenderMode == vtkKWRenderWidget::InteractiveRender )
    {
    if ( this->TimerToken )
      {
      Tcl_DeleteTimerHandler( this->TimerToken );
      this->TimerToken = NULL;
      }
    
    this->RenderWindow->
      SetDesiredUpdateRate( 
        this->InteractiveUpdateRate / (
          this->GetRenderer()->GetViewProps()->GetNumberOfItems() *
          this->RenderWindow->GetRenderers()->GetNumberOfItems()) );      
    this->LevelOfDetailRender();
    
    }
  else if ( this->RenderMode == vtkKWRenderWidget::StillRender )
    {
    
    // If this is a still render do it as an timer callback
    // Start the timer here. Then, if we don't already have a timer call
    // going, start one. The timer callback will render only if it has been
    // at least some length of time since the last time a render request was
    // made (we keep track of this with the timerlog - we re-start it each 
    // time a render request is made. The timer will keep rescheduling itself
    // until it can do its render successfully.
    this->RenderTimer->StartTimer();
    if ( !this->TimerToken )
      {
      this->TimerToken = Tcl_CreateTimerHandler(100, 
                                                KWVolumeWidget_IdleRender, 
                                                (ClientData)this);
      }
    }
  else if ( this->RenderMode == vtkKWRenderWidget::SingleRender )
    {
    if ( this->TimerToken )
      {
      Tcl_DeleteTimerHandler( this->TimerToken );
      this->TimerToken = NULL;
      }

    // if we are printing pick a good update rate
    if (this->Printing)
      {
      // default to longest update rate
      this->RenderWindow->SetDesiredUpdateRate(0.000001);
      }
    else
      {
      this->RenderWindow->SetDesiredUpdateRate(this->SingleUpdateRate);
      }
    this->LevelOfDetailRender();
    }
  
  this->Internals->InRender = 0;
}

//----------------------------------------------------------------------------
int vtkKWVolumeWidget::CreateCanonicalView(vtkImageData *image, 
                                           vtkVolumeProperty *prop,
                                           int blend_mode)
{
  vtkRenderWindow *renwin = this->GetRenderWindow();
  
  // This method is actually fairly tricky. What we are doing is trying
  // to use whatever mapper to create a thumbnail while we may be potentially
  // rendering or using the same window. To avoid too many problem, let's
  // just not do it if we are rendering.

  if (!this->GetInput() ||              // no input. Hello?
      !this->VolumeMapper ||                 // no mapper. Ouch.
      !renwin ||                             // no renderwindow. Really?
      renwin->CheckInRenderStatus() ||       // we are already rendering
      renwin->CheckAbortStatus() ||          // we should abort anyway
      renwin->GetEventPending() ||           // some events are pending
      (this->RenderMode == 
       vtkKWRenderWidget::InteractiveRender) ||// we are interactive rendering
      (this->VolumeMapper->GetRequestedRenderMode() == 
       vtkKWEVolumeMapper::RayCastAndTextureRenderMode && 
       !this->IsCreated()) || // we are not created but use raycast
      (this->VolumeMapper->GetRequestedRenderMode() == 
       vtkKWEVolumeMapper::DefaultRenderMode && 
       !this->IsMapped()) // we are not mapped and use GPU rendering
    )
    {
    return 0;
    }

  image->SetScalarTypeToUnsignedChar();
  image->SetNumberOfScalarComponents(3);
  image->AllocateScalars();
  
  int res = 1;
  double direction[3] = {0,-1,0};
  double up[3] = {0,0,1};
  vtkVolume *vol = vtkVolume::New();
  vol->SetProperty(prop);
  vol->SetMapper(this->VolumeMapper);
  int vmapper_blend_mode = vtkVolumeMapper::COMPOSITE_BLEND;
  if (blend_mode == vtkKWVolumeWidget::BLEND_MODE_MIP)
    {
    vmapper_blend_mode = vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND;
    }
  this->VolumeMapper->CreateCanonicalView( this->GetRenderer(),
					   this->Volume, vol, image, 
					   vmapper_blend_mode, direction, up);
  vol->SetProperty(NULL);
  vol->SetMapper(NULL);
  vol->Delete();

  return res;
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::IdleRenderCallback()
{
  int rescheduleDelay=100;
  int needToRender = 0;
  int abortFlag;
  double elapsedTime;
    
  this->RenderTimer->StopTimer();
  
  elapsedTime = this->RenderTimer->GetElapsedTime();
  abortFlag = this->ShouldIAbort();
  
  // Has enough time passed? Is there anything pending that will
  // abort this render?
  if ( elapsedTime > 0.1 && abortFlag == 0 )
    {
    if (!this->Volume->GetVisibility())
      {
      this->RenderWindow->Render();
      this->TimerToken = NULL;
      return;
      }
    
    // TODO: clean up this to use real still render rate (adjusted for number of props)
    // also consider multi-pass
    this->RenderWindow->SetDesiredUpdateRate(0.000001);
    this->LevelOfDetailRender();
    }
  else
    {
    if ( abortFlag == 1 )
      {
      needToRender = 1;
      rescheduleDelay = 1000;
      } 
    else if ( elapsedTime <= 0.1 )
      {  
      needToRender = 1;
      rescheduleDelay = 100;
      }
    }

  // If we still need to render, reschedule this callback
  if ( needToRender )
    {
    this->TimerToken = Tcl_CreateTimerHandler(rescheduleDelay, 
                                              KWVolumeWidget_IdleRender, 
                                              (ClientData)this);
    }
  else
    {
    this->TimerToken = NULL;
    }
}

//----------------------------------------------------------------------------
int vtkKWVolumeWidget::ShouldIAbort()
{
  return (vtkKWTkUtilities::CheckForPendingInteractionEvents(
            this->GetRenderWindow()));
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::RegisterIntermixIntersectingGeometry()
{
  this->UseIntermixIntersectingGeometry++;
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::UnRegisterIntermixIntersectingGeometry()
{
  if ( this->UseIntermixIntersectingGeometry <= 0 )
    {
    this->UseIntermixIntersectingGeometry = 0;
    vtkErrorMacro("Reference count for intermix intersecting geometry cannot "
                  "be less than zero.");
    }
  else
    {
    this->UseIntermixIntersectingGeometry--;
    }
}

//---------------------------------------------------------------------------
void vtkKWVolumeWidget::SetBlendModeToMIP()
{ 
  this->SetBlendMode(vtkKWVolumeWidget::BLEND_MODE_MIP); 
}

//---------------------------------------------------------------------------
void vtkKWVolumeWidget::SetBlendModeToComposite()
{ 
  this->SetBlendMode(vtkKWVolumeWidget::BLEND_MODE_COMPOSITE); 
}

//---------------------------------------------------------------------------
void vtkKWVolumeWidget::SetBlendMode(int arg)
{
  int mode = vtkVolumeMapper::COMPOSITE_BLEND;
  switch (arg)
    {
    case vtkKWVolumeWidget::BLEND_MODE_MIP:
      mode = vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND;
      break;

    default:
    case vtkKWVolumeWidget::BLEND_MODE_COMPOSITE:
      mode = vtkVolumeMapper::COMPOSITE_BLEND;
      break;
    }
  
  vtkCollection *mappers = vtkCollection::New();
  this->GetAllVolumeMappers(mappers);
  int need_render = 0;
  for (int i = 0; i < mappers->GetNumberOfItems(); i++)
    {
    vtkVolumeMapper *vmapper = vtkVolumeMapper::SafeDownCast(
      mappers->GetItemAsObject(i));
    if (vmapper && vmapper->GetBlendMode() != mode)
      {
      vmapper->SetBlendMode(mode);
      need_render++;
      }
    }

  mappers->Delete();
  mappers = NULL;
  
  if (need_render)
    {
    this->Render();
    }
}

//----------------------------------------------------------------------------
int vtkKWVolumeWidget::GetBlendMode()
{
  vtkCollection *mappers = vtkCollection::New();
  this->GetAllVolumeMappers(mappers);
  vtkVolumeMapper *vmapper = vtkVolumeMapper::SafeDownCast(
    mappers->GetItemAsObject(0));
  int mode = vtkKWVolumeWidget::BLEND_MODE_COMPOSITE;
  if (vmapper)
    {
    switch (vmapper->GetBlendMode())
      {
      case vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND:
        mode = vtkKWVolumeWidget::BLEND_MODE_MIP;
        break;
        
      default:
      case vtkVolumeMapper::COMPOSITE_BLEND:
        mode = vtkKWVolumeWidget::BLEND_MODE_COMPOSITE;
        break;
      }
    }

  mappers->Delete();
  mappers = NULL;

  return mode;
}

//----------------------------------------------------------------------------
int vtkKWVolumeWidget::GetVolumeVisibility()
{
  return this->Volume && this->Volume->GetVisibility() ? 1 : 0;
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetVolumeVisibility(int v)
{
  if (this->GetVolumeVisibility() == v)
    {
    return;
    }

  if (this->Volume)
    {
    this->Volume->SetVisibility(v);
    this->Render();
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::ToggleVolumeVisibility()
{
  this->SetVolumeVisibility(!this->GetVolumeVisibility());
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetCropping(int state)
{
  vtkCollection *mappers = vtkCollection::New();
  this->GetAllVolumeMappers(mappers);
  int need_render = 0;
  for (int i = 0; i < mappers->GetNumberOfItems(); i++)
    {
    vtkVolumeMapper *vmapper = vtkVolumeMapper::SafeDownCast(
      mappers->GetItemAsObject(i));
    if (vmapper && vmapper->GetCropping() != state)
      {
      vmapper->SetCropping(state);
      need_render++;
      }
    }

  mappers->Delete();
  mappers = NULL;

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

//----------------------------------------------------------------------------
int vtkKWVolumeWidget::GetCropping()
{
  vtkCollection *mappers = vtkCollection::New();
  this->GetAllVolumeMappers(mappers);
  vtkVolumeMapper *vmapper = vtkVolumeMapper::SafeDownCast(
    mappers->GetItemAsObject(0));
  int res = vmapper ? vmapper->GetCropping() : 0;

  mappers->Delete();
  mappers = NULL;

  return res;
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetCroppingPlanes(double p[6])
{
  vtkCollection *mappers = vtkCollection::New();
  this->GetAllVolumeMappers(mappers);
  int need_render = 0;
  for (int i = 0; i < mappers->GetNumberOfItems(); i++)
    {
    vtkVolumeMapper *vmapper = vtkVolumeMapper::SafeDownCast(
      mappers->GetItemAsObject(i));
    if (vmapper)
      {
      double *current = vmapper->GetCroppingRegionPlanes();
      if (!current ||
          current[0] != p[0] || current[1] != p[1] ||
          current[2] != p[2] || current[3] != p[3] ||
          current[4] != p[4] || current[5] != p[5])
        {
        vmapper->SetCroppingRegionPlanes(p);
        need_render++;
        }
      }
    }

  mappers->Delete();
  mappers = NULL;

  if (need_render && this->GetCropping())
    {
    this->Render();
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetCroppingPlanes(
  double p0, double p1, double p2, double p3, double p4, double p5)
{
  double p[6];
  p[0] = p0;
  p[1] = p1;
  p[2] = p2;
  p[3] = p3;
  p[4] = p4;
  p[5] = p5;
  this->SetCroppingPlanes(p);
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::ResetCroppingPlanes()
{
  if (this->Input)
    {
    this->SetCroppingPlanes(this->Input->GetBounds());
    }
}

//----------------------------------------------------------------------------
double* vtkKWVolumeWidget::GetCroppingPlanes()
{
  vtkCollection *mappers = vtkCollection::New();
  this->GetAllVolumeMappers(mappers);
  vtkVolumeMapper *vmapper = vtkVolumeMapper::SafeDownCast(
    mappers->GetItemAsObject(0));
  double* res = vmapper ? vmapper->GetCroppingRegionPlanes() : NULL;

  mappers->Delete();
  mappers = NULL;

  return res;
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetCroppingRegionFlags(int flags)
{
  vtkCollection *mappers = vtkCollection::New();
  this->GetAllVolumeMappers(mappers);
  int need_render = 0;
  for (int i = 0; i < mappers->GetNumberOfItems(); i++)
    {
    vtkVolumeMapper *vmapper = vtkVolumeMapper::SafeDownCast(
      mappers->GetItemAsObject(i));
    if (vmapper && vmapper->GetCroppingRegionFlags() != flags)
      {
      vmapper->SetCroppingRegionFlags(flags);
      need_render++;
      }
    }

  mappers->Delete();
  mappers = NULL;

  if (need_render && this->GetCropping())
    {
    this->Render();
    }
}

//----------------------------------------------------------------------------
int vtkKWVolumeWidget::GetCroppingRegionFlags()
{
  vtkCollection *mappers = vtkCollection::New();
  this->GetAllVolumeMappers(mappers);
  vtkVolumeMapper *vmapper = vtkVolumeMapper::SafeDownCast(
    mappers->GetItemAsObject(0));
  int res = vmapper ? vmapper->GetCroppingRegionFlags() : 0;

  mappers->Delete();
  mappers = NULL;

  return res;
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetReformatThickness(double arg)
{
  if (this->ReformatThickness == arg)
    {
    return;
    }

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

  if (this->Reformat)
    {
    this->RecomputeReformatPlane();
    this->Render();
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetReformatNormal(double x, double y, double z)
{
  if (this->ReformatNormal[0] == x &&
      this->ReformatNormal[1] == y &&
      this->ReformatNormal[2] == z)
    {
    return;
    }

  this->ReformatNormal[0] = x;
  this->ReformatNormal[1] = y;
  this->ReformatNormal[2] = z;

  this->Modified();

  if (this->Reformat)
    {
    this->RecomputeReformatPlane();
    this->Render();
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetReformatUp(double x, double y, double z)
{
  if (this->ReformatUp[0] == x &&
      this->ReformatUp[1] == y &&
      this->ReformatUp[2] == z)
    {
    return;
    }

  this->ReformatUp[0] = x;
  this->ReformatUp[1] = y;
  this->ReformatUp[2] = z;

  this->Modified();

  if (this->Reformat)
    {
    this->RecomputeReformatPlane();
    this->Render();
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetReformatLocation(double x, double y, double z)
{
  if (this->ReformatLocation[0] == x &&
      this->ReformatLocation[1] == y &&
      this->ReformatLocation[2] == z)
    {
    return;
    }

  this->ReformatLocation[0] = x;
  this->ReformatLocation[1] = y;
  this->ReformatLocation[2] = z;

  this->Modified();

  if (this->Reformat)
    {
    this->RecomputeReformatPlane();
    this->Render();
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::RecomputeReformatPlane()
{
  double args[6];
  int cc;
  for ( cc = 0; cc < 3; cc ++ )
    {
    args[cc] = this->ReformatNormal[cc];
    args[cc+3] = this->ReformatUp[cc];
    }
  this->InvokeEvent(vtkKWEvent::VolumeReformatPlaneChangedEvent, args);

  this->PerformRecomputeReformatPlane(args);
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::PerformRecomputeReformatPlane(double* args)
{
  int cc;
  for ( cc = 0; cc < 3; cc ++ )
    {
    this->ReformatNormal[cc] = args[cc];
    this->ReformatUp[cc] = args[cc+3];
    }

  int i, j, k;

  // compute the basis
  double extra[3];
  double basis2[3];

  vtkMath::Cross(this->ReformatNormal, this->ReformatUp, basis2);
  vtkMath::Normalize(basis2);

  // We are making the assumption that the volume matrix
  // is an identity matrix
  double *bounds = this->Input->GetBounds();

  double loc[3];
  double diag =
    1.3 * sqrt(((double)bounds[1] - (double)bounds[0])*
               ((double)bounds[1] - (double)bounds[0]) +
               ((double)bounds[3] - (double)bounds[2]) *
               ((double)bounds[3] - (double)bounds[2]) +
               ((double)bounds[5] - (double)bounds[4]) *
               ((double)bounds[5] - (double)bounds[4]));

  for (i = 0; i < 3; i++)
    {
    extra[i] = 0.4 * (double)this->ReformatUp[i] * (double)diag;
    basis2[i] = 0.4 * (double)basis2[i] * (double)diag;
    loc[i] = (double)this->ReformatLocation[i] +
      (double)this->ReformatNormal[i] * (double)this->ReformatThickness * 0.5;
    }

  // computes ABCD, abc are just the reformat normal
  double d = 0;
  for (i = 0; i < 3; i++)
    {
    d = d - this->ReformatNormal[i]*loc[i];
    }

  vtkPoints *pts = this->ReformatData->GetPoints();
  vtkCellArray *lines = this->ReformatData->GetLines();
  lines->Reset();
  pts->InsertPoint(0,
                   loc[0] + extra[0] + basis2[0],
                   loc[1] + extra[1] + basis2[1],
                   loc[2] + extra[2] + basis2[2]);
  pts->InsertPoint(1,
                   loc[0] - extra[0] + basis2[0],
                   loc[1] - extra[1] + basis2[1],
                   loc[2] - extra[2] + basis2[2]);
  pts->InsertPoint(2,
                   loc[0] - extra[0] - basis2[0],
                   loc[1] - extra[1] - basis2[1],
                   loc[2] - extra[2] - basis2[2]);
  pts->InsertPoint(3,
                   loc[0] + extra[0] - basis2[0],
                   loc[1] + extra[1] - basis2[1],
                   loc[2] + extra[2] - basis2[2]);

  pts->InsertPoint(4,
                   loc[0] + extra[0] + basis2[0] -
                   this->ReformatNormal[0]*this->ReformatThickness,
                   loc[1] + extra[1] + basis2[1] -
                   this->ReformatNormal[1]*this->ReformatThickness,
                   loc[2] + extra[2] + basis2[2] -
                   this->ReformatNormal[2]*this->ReformatThickness);
  pts->InsertPoint(5,
                   loc[0] - extra[0] + basis2[0] -
                   this->ReformatNormal[0]*this->ReformatThickness,
                   loc[1] - extra[1] + basis2[1] -
                   this->ReformatNormal[1]*this->ReformatThickness,
                   loc[2] - extra[2] + basis2[2] -
                   this->ReformatNormal[2]*this->ReformatThickness);
  pts->InsertPoint(6,
                   loc[0] - extra[0] - basis2[0] -
                   this->ReformatNormal[0]*this->ReformatThickness,
                   loc[1] - extra[1] - basis2[1] -
                   this->ReformatNormal[1]*this->ReformatThickness,
                   loc[2] - extra[2] - basis2[2] -
                   this->ReformatNormal[2]*this->ReformatThickness);
  pts->InsertPoint(7,
                   loc[0] + extra[0] - basis2[0] -
                   this->ReformatNormal[0]*this->ReformatThickness,
                   loc[1] + extra[1] - basis2[1] -
                   this->ReformatNormal[1]*this->ReformatThickness,
                   loc[2] + extra[2] - basis2[2] -
                   this->ReformatNormal[2]*this->ReformatThickness);

  // set the polygons
  lines->InsertNextCell(2);
  lines->InsertCellPoint(0);
  lines->InsertCellPoint(1);
  lines->InsertNextCell(2);
  lines->InsertCellPoint(1);
  lines->InsertCellPoint(2);
  lines->InsertNextCell(2);
  lines->InsertCellPoint(2);
  lines->InsertCellPoint(3);
  lines->InsertNextCell(2);
  lines->InsertCellPoint(3);
  lines->InsertCellPoint(0);

  lines->InsertNextCell(2);
  lines->InsertCellPoint(4);
  lines->InsertCellPoint(5);
  lines->InsertNextCell(2);
  lines->InsertCellPoint(5);
  lines->InsertCellPoint(6);
  lines->InsertNextCell(2);
  lines->InsertCellPoint(6);
  lines->InsertCellPoint(7);
  lines->InsertNextCell(2);
  lines->InsertCellPoint(7);
  lines->InsertCellPoint(4);

  lines->InsertNextCell(2);
  lines->InsertCellPoint(0);
  lines->InsertCellPoint(4);
  lines->InsertNextCell(2);
  lines->InsertCellPoint(1);
  lines->InsertCellPoint(5);
  lines->InsertNextCell(2);
  lines->InsertCellPoint(2);
  lines->InsertCellPoint(6);
  lines->InsertNextCell(2);
  lines->InsertCellPoint(3);
  lines->InsertCellPoint(7);
  
  int idx = 8;
  
  // These are the eight bounding box corners
  double corners[8][3];
  for ( k = 0; k < 2; k++ )
    {
    for ( j = 0; j < 2; j++ )
      {
      for ( i = 0; i < 2; i++ )
        {
        corners[k*4+j*2+i][0] = bounds[i];
        corners[k*4+j*2+i][1] = bounds[2+j];
        corners[k*4+j*2+i][2] = bounds[4+k];
        }
      }
    }
  
  // These are all the edges around the 
  // bounding box that we'll need to clip
  // with the thick reformat planes
  int edges[6][4][2] = 
  {{{0,1},{1,3},{3,2},{2,0}},
   {{4,5},{5,7},{7,6},{6,4}},
   {{1,5},{5,7},{7,3},{3,1}},
   {{0,4},{4,6},{6,2},{2,0}},
   {{0,1},{1,5},{5,4},{4,0}},
   {{2,3},{3,7},{7,6},{6,2}}};
  
  double p[2][3];
  double points[8][3];
  int pIdx;
  double dist, t;
  double dir[3];
  double plane[2][4];
  
  // These are the two reformat planes
  plane[0][0] = -this->ReformatNormal[0];
  plane[0][1] = -this->ReformatNormal[1];
  plane[0][2] = -this->ReformatNormal[2];
  plane[0][3] = 
    -(plane[0][0] * loc[0] +
      plane[0][1] * loc[1] +
      plane[0][2] * loc[2] );

  plane[1][0] = this->ReformatNormal[0];
  plane[1][1] = this->ReformatNormal[1];
  plane[1][2] = this->ReformatNormal[2];
  plane[1][3] = 
    -(plane[1][0] * 
      (loc[0]-this->ReformatNormal[0]*this->ReformatThickness) +
      plane[1][1] * 
      (loc[1]-this->ReformatNormal[1]*this->ReformatThickness) +
      plane[1][2] * 
      (loc[2]-this->ReformatNormal[2]*this->ReformatThickness) );
  
  // For each face
  for ( j = 0; j < 6; j++ )
    {
    pIdx = 0;
    // For each of the four edges on the face
    for ( i = 0; i < 4; i++ )
      {
      int invalid = 0;
      
      p[0][0] = corners[edges[j][i][0]][0];
      p[0][1] = corners[edges[j][i][0]][1];
      p[0][2] = corners[edges[j][i][0]][2];

      p[1][0] = corners[edges[j][i][1]][0];
      p[1][1] = corners[edges[j][i][1]][1];
      p[1][2] = corners[edges[j][i][1]][2];

      dir[0] = p[1][0] - p[0][0];
      dir[1] = p[1][1] - p[0][1];
      dir[2] = p[1][2] - p[0][2];
      
      int ii, jj;
      // For each of the two end points
      for ( jj = 0; jj < 2; jj++ )
        {
        // For each of the two planes
        for ( ii = 0; ii < 2; ii++ )
          {
          // Compute the distance of the end point
          // to the plane
          dist = 
            p[jj][0]*plane[ii][0] + 
            p[jj][1]*plane[ii][1] + 
            p[jj][2]*plane[ii][2] + 
                     plane[ii][3];
          
          // If it is negative, we need to clip
          // the line
          if ( dist < 0 )
            {
            // Compute where the line intersect
            // this plane
            t = -(p[0][0]*plane[ii][0] + 
                  p[0][1]*plane[ii][1] + 
                  p[0][2]*plane[ii][2] +
                          plane[ii][3]) /
              (dir[0]*plane[ii][0] +
               dir[1]*plane[ii][1] + 
               dir[2]*plane[ii][2]);
            
            // If the line does not intersect on the
            // edge, this edge is entirely clipped 
            // and we'll mark it as invalid and we
            // will not add it to the polydata
            if ( t < 0 || t > 1 )
              {
              invalid = 1;
              }
            // Otherwise, modify the point
            else
              {
              p[jj][0] = p[0][0] + t*dir[0];
              p[jj][1] = p[0][1] + t*dir[1];
              p[jj][2] = p[0][2] + t*dir[2];
              }
            }
          }
        }
      
      // If we are not invalid, add the two points in
      if ( !invalid )
        {
        points[pIdx][0] = p[0][0];
        points[pIdx][1] = p[0][1];
        points[pIdx][2] = p[0][2];
        points[pIdx+1][0] = p[1][0];
        points[pIdx+1][1] = p[1][1];
        points[pIdx+1][2] = p[1][2];
        pIdx+=2;
        }
      }
    
    // As long as we have some points
    // add them in
    if ( pIdx > 0 )
      {
      lines->InsertNextCell(pIdx+1);
      for ( i = 0; i < pIdx; i++ )
        {
        pts->InsertPoint( idx, 
                          points[i][0], 
                          points[i][1], 
                          points[i][2] );
        lines->InsertCellPoint(idx);
        idx++;
        }
      lines->InsertCellPoint(idx-pIdx);
      }
    }
  
  this->ReformatData->Modified();

  vtkCollection *mappers = vtkCollection::New();
  this->GetAllVolumeMappers(mappers);

  for (i = 0; i < mappers->GetNumberOfItems(); i++)
    {
    vtkVolumeMapper *vmapper = vtkVolumeMapper::SafeDownCast(
      mappers->GetItemAsObject(i));
    if (vmapper && vmapper->GetClippingPlanes())
      { 
      vmapper->GetClippingPlanes()->RemoveAllItems();
      }
    }

  if (this->Reformat)
    {
    vtkPlane *plane1 = vtkPlane::New();
    vtkPlane *plane2 = vtkPlane::New();
    plane1->SetNormal( -this->ReformatNormal[0],
                       -this->ReformatNormal[1],
                       -this->ReformatNormal[2] );
    plane1->SetOrigin( loc[0], loc[1], loc[2] );
    plane2->SetNormal( this->ReformatNormal[0],
                       this->ReformatNormal[1],
                       this->ReformatNormal[2] );
    plane2->SetOrigin( loc[0] -
                       this->ReformatNormal[0]*this->ReformatThickness,
                       loc[1] -
                       this->ReformatNormal[1]*this->ReformatThickness,
                       loc[2] -
                       this->ReformatNormal[2]*this->ReformatThickness);

    for (i = 0; i < mappers->GetNumberOfItems(); i++)
      {
      vtkVolumeMapper *vmapper = vtkVolumeMapper::SafeDownCast(
        mappers->GetItemAsObject(i));
      if (vmapper)
        { 
        vmapper->AddClippingPlane( plane1 );
        vmapper->AddClippingPlane( plane2 );
        }
      }

    plane1->Delete();
    plane2->Delete();
    }

  mappers->Delete();
  mappers = NULL;
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetReformat(int arg)
{
  if (this->Reformat == arg)
    {
    return;
    }

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

  if (this->Reformat)
    {
    this->SetReformatManipulationStyle(this->ReformatManipulationStyle);
    
    double *center = this->Input->GetCenter();
    this->ReformatLocation[0] = center[0];
    this->ReformatLocation[1] = center[1];
    this->ReformatLocation[2] = center[2];
    this->ReformatNormal[0] = 0;
    this->ReformatNormal[1] = 0;
    this->ReformatNormal[2] = 1;

    if (this->ReformatBoxVisibility)
      {
      this->AddViewProp(this->ReformatPlane);
      }
    
    this->RecomputeReformatPlane();
    this->ResetCameraClippingRange();
    this->Render();
    }
  else
    {
    vtkCollection *mappers = vtkCollection::New();
    this->GetAllVolumeMappers(mappers);
    for (int i = 0; i < mappers->GetNumberOfItems(); i++)
      {
      vtkVolumeMapper *vmapper = vtkVolumeMapper::SafeDownCast(
        mappers->GetItemAsObject(i));
      if (vmapper)
        {
        vmapper->GetClippingPlanes()->RemoveAllItems();
        }
      }
    
    mappers->Delete();
    mappers = NULL;
    
    if (this->ReformatBoxVisibility)
      {
      this->RemoveViewProp(this->ReformatPlane);
      }
    
    this->ResetCameraClippingRange();
    this->SetReformatManipulationStyle(
      vtkKWVolumeWidget::CAMERA_MANIPULATION_STYLE);
    this->Render();
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetReformatManipulationStyle(int arg)
{
  if (this->ReformatManipulationStyle == arg)
    {
    return;
    }

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

  this->AddInteractionBindings();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::AddInteractionBindings()
{
  // Setup the usual bindings

  this->Superclass::AddInteractionBindings();

  // If we are disabled, don't do anything

  if (!this->GetEnabled())
    {
    return;
    }

  // Now if in reformat manipulation style, override some of the bindings

  if (this->Reformat && 
      this->ReformatManipulationStyle == 
      vtkKWVolumeWidget::REFORMAT_MANIPULATION_STYLE)
    {
    if (this->VTKWidget->IsCreated())
      {
      const char *wname = this->VTKWidget->GetWidgetName();
      const char *tname = this->GetTclName();

      // Setup the bindings

      this->VTKWidget->SetBinding(
        "<Any-ButtonPress>", this, "ReformatButtonPress %x %y");
    
      this->VTKWidget->SetBinding(
        "<Any-ButtonRelease>", this, "ReformatButtonRelease %x %y");
    
      this->VTKWidget->SetBinding(
        "<B1-Motion>", this, "ReformatButton1Motion %x %y");
    
      this->VTKWidget->SetBinding(
        "<B2-Motion>", this, "ReformatButton2Motion %x %y");
    
      this->VTKWidget->SetBinding(
        "<B3-Motion>", this, "ReformatButton3Motion %x %y");
    
      this->VTKWidget->SetBinding(
        "<Shift-B1-Motion>", this, "ReformatButton2Motion %x %y");
      }
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::ReformatButtonPress(int x, int y)
{
  this->LastPosition[0] = x;
  this->LastPosition[1] = y;
  this->SetRenderModeToInteractive();
  this->InvokeEvent(vtkCommand::StartInteractionEvent, NULL);
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::ReformatButtonRelease()
{
  this->InvokeEvent(vtkCommand::EndInteractionEvent, NULL);
  this->SetRenderModeToStill();
  this->Render();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::ReformatButton1Motion(int x, int y)
{
  vtkCamera *cam = this->GetRenderer()->GetActiveCamera();
  if (!cam)
    {
    return;
    }

  // rotate the plane
  double *vup = cam->GetViewUp();
  double *vpn = cam->GetViewPlaneNormal();
  double vr[3];
  
  vtkMath::Cross(vup,vpn,vr);

  this->ReformatTransform->Identity();
  this->ReformatTransform->RotateWXYZ(y - this->LastPosition[1],
                                      vr[0], vr[1], vr[2]);
  this->ReformatTransform->RotateWXYZ(x - this->LastPosition[0],
                                      vup[0], vup[1], vup[2]);
  // push the normal and up through the matrix
  this->ReformatTransform->MultiplyPoint(this->ReformatNormal,
                                         this->ReformatNormal);
  this->ReformatTransform->MultiplyPoint(this->ReformatUp,
                                         this->ReformatUp);

  // recompute the plane
  this->RecomputeReformatPlane();
  this->LastPosition[0] = x;
  this->LastPosition[1] = y;
  this->ResetCameraClippingRange();
  this->Render();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::ReformatButton2Motion(int x, int y)
{
  vtkCamera *cam = this->GetRenderer()->GetActiveCamera();
  if (!cam)
    {
    return;
    }

  // pan the plane
  double *vup = cam->GetViewUp();
  double *vpn = cam->GetViewPlaneNormal();
  double vr[3];
  
  vtkMath::Cross(vup,vpn,vr);
  
  double scale = cam->GetParallelScale();
  int *size = this->RenderWindow->GetSize();
  
  // adjust the location
  int i;
  double *newLoc = this->ReformatLocation;
  for (i = 0; i < 3; i++)
    {
    newLoc[i] = this->ReformatLocation[i] + 
      vr[i]*(x - this->LastPosition[0])*scale/size[0] + 
      vup[i]*(this->LastPosition[1]-y)*scale/size[1];
    }
  
  double *bounds = this->Input->GetBounds();
  newLoc[0] = (newLoc[0] < bounds[0])?(bounds[0]):(newLoc[0]);
  newLoc[0] = (newLoc[0] > bounds[1])?(bounds[1]):(newLoc[0]);
  newLoc[1] = (newLoc[1] < bounds[2])?(bounds[2]):(newLoc[1]);
  newLoc[1] = (newLoc[1] > bounds[3])?(bounds[3]):(newLoc[1]);
  newLoc[2] = (newLoc[2] < bounds[4])?(bounds[4]):(newLoc[2]);
  newLoc[2] = (newLoc[2] > bounds[5])?(bounds[5]):(newLoc[2]);
  
  // recompute the plane
  this->RecomputeReformatPlane();
  this->LastPosition[0] = x;
  this->LastPosition[1] = y;
  this->ResetCameraClippingRange();
  this->Render();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::ReformatButton3Motion(int x, int y)
{
  vtkCamera *cam = this->GetRenderer()->GetActiveCamera();
  if (!cam)
    {
    return;
    }

  // move the plane in and out
  double *vpn = cam->GetViewPlaneNormal();
  double diag = this->Input->GetLength();
  
  // adjust the location
  int i;
  double *newLoc = this->ReformatLocation;
  for (i = 0; i < 3; i++)
    {
    newLoc[i] = this->ReformatLocation[i] + 
      vpn[i]*(this->LastPosition[1]-y)*diag*0.01;
    }

  double *bounds = this->Input->GetBounds();
  newLoc[0] = (newLoc[0] < bounds[0])?(bounds[0]):(newLoc[0]);
  newLoc[0] = (newLoc[0] > bounds[1])?(bounds[1]):(newLoc[0]);
  newLoc[1] = (newLoc[1] < bounds[2])?(bounds[2]):(newLoc[1]);
  newLoc[1] = (newLoc[1] > bounds[3])?(bounds[3]):(newLoc[1]);
  newLoc[2] = (newLoc[2] < bounds[4])?(bounds[4]):(newLoc[2]);
  newLoc[2] = (newLoc[2] > bounds[5])?(bounds[5]):(newLoc[2]);
  
  // recompute the plane
  this->RecomputeReformatPlane();
  this->LastPosition[0] = x;
  this->LastPosition[1] = y;
  this->ResetCameraClippingRange();
  this->Render();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetAnnotationsVisibility(int v)
{
  this->Superclass::SetAnnotationsVisibility(v);

  this->SetBoundingBoxVisibility(v);
  this->SetScaleBarVisibility(v);
  this->SetScalarBarVisibility(v);
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetReformatBoxVisibility(int arg)
{
  if (this->GetReformatBoxVisibility() == arg)
    {
    return;
    }
  
  this->ReformatBoxVisibility = arg;
  this->Modified();

  if (this->ReformatBoxVisibility)
    {
    this->ReformatPlane->VisibilityOn();
    if (!this->HasViewProp(this->ReformatPlane))
      {
      this->AddViewProp(this->ReformatPlane);
      }
    }
  else
    {
    this->ReformatPlane->VisibilityOff();
    if (this->HasViewProp(this->ReformatPlane))
      {
      this->RemoveViewProp(this->ReformatPlane);
      }
    }
  if (this->Reformat)
    {
    this->Render();
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::ApplyDefaultPreset()
{
  this->UpdateHistogramSet();

  vtkKWVolumePropertyHelper::ApplyPresetAndConvertNormalizedRange(
    vtkKWVolumePropertyHelper::Preset3,
    this->VolumeProperty,
    this->Input,
    this->GetIndependentComponents(),
    this->HistogramSet
    );
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::UpdateAccordingTo3DCursorAnnotation()
{
  int vis = this->Cursor3DAnnotation->GetVisibility();

  if (vis)
    {
    if (!this->HasViewProp(this->Cursor3DAnnotation))
      {
      this->AddViewProp(this->Cursor3DAnnotation);
      }
    }
  else
    {
    if (this->HasViewProp(this->Cursor3DAnnotation))
      {
      this->RemoveViewProp(this->Cursor3DAnnotation);
      }
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetCursor3DVisibility(int state)
{
  if (this->GetCursor3DVisibility() == state)
    {
    return;
    }

  this->Cursor3DAnnotation->SetVisibility(state);
  this->UpdateAccordingTo3DCursorAnnotation();
  this->Render();
}

//----------------------------------------------------------------------------
int vtkKWVolumeWidget::GetCursor3DVisibility()
{
  return (this->Cursor3DAnnotation && 
          this->Cursor3DAnnotation->GetVisibility());
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetCursor3DPosition(double x, double y, double z)
{
  double *pos = this->GetCursor3DPosition();
  if (!pos || (pos[0] == x && pos[1] == y && pos[2] == z))
    {
    return;
    }

  this->Cursor3DAnnotation->SetCursorPosition(x, y, z);
  this->UpdateAccordingTo3DCursorAnnotation();

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

//----------------------------------------------------------------------------
double* vtkKWVolumeWidget::GetCursor3DPosition()
{
  return this->Cursor3DAnnotation->GetCursorPosition();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetCursor3DType(int type)
{
  if (this->GetCursor3DType() == type)
    {
    return;
    }

  this->Cursor3DAnnotation->SetCursorType(type);
  this->UpdateAccordingTo3DCursorAnnotation();

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

//----------------------------------------------------------------------------
int vtkKWVolumeWidget::GetCursor3DType()
{
  return this->Cursor3DAnnotation->GetCursorType();
}

//----------------------------------------------------------------------------
double* vtkKWVolumeWidget::GetCursor3DXColor()
{
  return this->Cursor3DAnnotation->GetCursorXAxisColor();
}

//----------------------------------------------------------------------------
double* vtkKWVolumeWidget::GetCursor3DYColor()
{
  return this->Cursor3DAnnotation->GetCursorYAxisColor();
}

//----------------------------------------------------------------------------
double* vtkKWVolumeWidget::GetCursor3DZColor()
{
  return this->Cursor3DAnnotation->GetCursorZAxisColor();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetCursor3DXColor(double r, double g, double b)
{
  double *color = this->GetCursor3DXColor();
  if (!color || (color[0] == r && color[1] == g && color[2] == b))
    {
    return;
    }

  this->Cursor3DAnnotation->SetCursorXAxisColor(r, g, b);
  this->UpdateAccordingTo3DCursorAnnotation();

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

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetCursor3DYColor(double r, double g, double b)
{
  double *color = this->GetCursor3DYColor();
  if (!color || (color[0] == r && color[1] == g && color[2] == b))
    {
    return;
    }

  this->Cursor3DAnnotation->SetCursorYAxisColor(r, g, b);
  this->UpdateAccordingTo3DCursorAnnotation();

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

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetCursor3DZColor(double r, double g, double b)
{
  double *color = this->GetCursor3DZColor();
  if (!color || (color[0] == r && color[1] == g && color[2] == b))
    {
    return;
    }

  this->Cursor3DAnnotation->SetCursorZAxisColor(r, g, b);
  this->UpdateAccordingTo3DCursorAnnotation();

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

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::StartUsingCursor3D()
{
  if (!this->UseCursor3D)
    {
    this->RegisterIntermixIntersectingGeometry();
    this->UseCursor3D = 1;
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::StopUsingCursor3D()
{
  if (this->UseCursor3D)
    {
    this->UnRegisterIntermixIntersectingGeometry();
    this->UseCursor3D = 0;
    }
}

//----------------------------------------------------------------------------
int vtkKWVolumeWidget::GetProjectionType()
{
  vtkCamera *cam = this->GetRenderer()->GetActiveCamera();
  if (cam && cam->GetParallelProjection())
    {
    return vtkKWVolumeWidget::PARALLEL_PROJECTION;
    }
  return vtkKWVolumeWidget::PERSPECTIVE_PROJECTION;
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetProjectionType(int type)
{
  if (this->GetProjectionType() == type)
    {
    return;
    }

  vtkCamera *cam = this->GetRenderer()->GetActiveCamera();
  switch (type)
    {
    case vtkKWVolumeWidget::PARALLEL_PROJECTION:
      if (cam)
        {
        cam->ParallelProjectionOn();
        }
      break;

    case vtkKWVolumeWidget::PERSPECTIVE_PROJECTION:
      if (this->GetScaleBarVisibility())
        {
        vtkKWMessageDialog::PopupMessage(
          this->GetApplication(), this->GetParentTopLevel(), 
          ks_("Volume Widget|Dialog|Scale Bar Warning"),
          k_("The scale bar widget does not work with perspective "
             "projection. It will be turned off in the corresponding window."),
          vtkKWMessageDialog::WarningIcon);
        int vis = 0;
        this->InvokeEvent(vtkKWEvent::VolumeScaleBarVisibilityChangedEvent,
                          &vis);
        this->SetScaleBarVisibility(0);
        }
      if (cam)
        {
        cam->ParallelProjectionOff();
        }
      break;
    }

  this->ResetCamera();
  this->Render();

  this->InvokeEvent(vtkKWEvent::ProjectionTypeChangedEvent, &type);
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetScaleBarVisibility(int state)
{
  if (this->GetScaleBarVisibility() == state)
    {
    return;
    }

  this->ScaleBarWidget->SetEnabled(state);
  this->Render();
}

//----------------------------------------------------------------------------
int vtkKWVolumeWidget::GetScaleBarVisibility()
{
  return this->ScaleBarWidget->GetEnabled();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::ToggleScaleBarVisibility()
{
  this->SetScaleBarVisibility(!this->GetScaleBarVisibility());
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetScaleBarColor(double r, double g, double b)
{
  double *color = this->GetScaleBarColor();
  if (!color || (color[0] == r && color[1] == g && color[2] == b))
    {
    return;
    }

  this->ScaleBarWidget->SetColor(r, g, b);

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

//----------------------------------------------------------------------------
double* vtkKWVolumeWidget::GetScaleBarColor()
{
  return this->ScaleBarWidget->GetColor();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetScalarBarVisibility(int state)
{
  if (this->GetScalarBarVisibility() == state)
    {
    return;
    }

  this->ScalarBarWidget->SetEnabled(state);
  this->Render();
}

//----------------------------------------------------------------------------
int vtkKWVolumeWidget::GetScalarBarVisibility()
{
  return this->ScalarBarWidget->GetEnabled();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::ToggleScalarBarVisibility()
{
  this->SetScalarBarVisibility(!this->GetScalarBarVisibility());
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetScalarBarComponent(int comp)
{
  if (comp == this->GetScalarBarComponent())
    {
    return;
    }

  vtkScalarBarActor *actor = this->ScalarBarWidget->GetScalarBarActor();
  if (this->VolumeProperty && actor)
    {
    actor->SetLookupTable(this->VolumeProperty->GetRGBTransferFunction(comp));
    if (this->GetScalarBarVisibility())
      {
      this->Render();
      }
    }
}

//----------------------------------------------------------------------------
int vtkKWVolumeWidget::GetScalarBarComponent()
{
  // Search inside the volume property to find which component we
  // are visualizing right now

  vtkScalarBarActor *actor = this->ScalarBarWidget->GetScalarBarActor();
  if (this->VolumeProperty && actor && actor->GetLookupTable())
    {
    for (int i = 0; i < VTK_MAX_VRCOMP; i++)
      {
      if (actor->GetLookupTable() == 
          this->VolumeProperty->GetRGBTransferFunction(i))
        {
        return i;
        }
      }
    }

  return 0;
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetPlaneWidgetVisibility(int state)
{
  if (this->GetPlaneWidgetVisibility() == state)
    {
    return;
    }

  // Make sure to actually the right CurrentRenderer and not the one
  // picked by the last mouse event
  this->PlaneWidget->SetCurrentRenderer(this->GetRenderer());
  this->PlaneWidget->SetEnabled(state);
  this->Render();
}

//----------------------------------------------------------------------------
int vtkKWVolumeWidget::GetPlaneWidgetVisibility()
{
  return this->PlaneWidget->GetEnabled();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetPlaneWidgetColor(double r, double g, double b)
{
  int need_to_render = 0;
  double *color;

  color = this->PlaneWidget->GetPlaneProperty()->GetColor();
  if (color && (color[0] != r || color[1] != g || color[2] != b))
    {
    this->PlaneWidget->GetPlaneProperty()->SetColor(r, g, b);
    need_to_render++;
    }

  double h, h2, s, v, v2;
  vtkMath::RGBToHSV(r, g, b, &h, &s, &v);

  h2 = h * 1.15;
  v2 = v * 1.3;
  vtkMath::HSVToRGB(
    (h2>1.0 ? (h2 - 1.0) : h2), s, (v2>1.0 ? 1.0 : v2), &r, &g, &b);

  color = this->PlaneWidget->GetSelectedPlaneProperty()->GetColor();
  if (color && (color[0] != r || color[1] != g || color[2] != b))
    {
    this->PlaneWidget->GetSelectedPlaneProperty()->SetColor(r, g, b);
    need_to_render++;
    }

  //color = this->PlaneWidget->GetCursorProperty()->GetColor();
  //if (color && (color[0] != r || color[1] != g || color[2] != b))
  //  {
  //  this->PlaneWidget->GetCursorProperty()->SetColor(r, g, b);
  //  need_to_render++;
  //  }

  h2 = h * 0.85;
  v2 = v * 0.7;
  vtkMath::HSVToRGB(
    (h2>1.0 ? (h2 - 1.0) : h2), s, (v2>1.0 ? 1.0 : v2), &r, &g, &b);

  //color = this->PlaneWidget->GetMarginProperty()->GetColor();
  //if (color && (color[0] != r || color[1] != g || color[2] != b))
  //  {
  //  this->PlaneWidget->GetMarginProperty()->SetColor(r, g, b);
  //  need_to_render++;
  //  }

  if (this->GetPlaneWidgetVisibility() && need_to_render)
    {
    this->Render();
    }
}

//----------------------------------------------------------------------------
double* vtkKWVolumeWidget::GetPlaneWidgetColor()
{
  return this->PlaneWidget->GetPlaneProperty()->GetColor();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetPlaneWidgetScalarsVisibility(int state)
{
  if (this->GetPlaneWidgetScalarsVisibility() == state)
    {
    return;
    }
  
  this->PlaneWidget->SetDrawPlane(state);
  this->Modified();
  
  this->Render();
}

//----------------------------------------------------------------------------
int vtkKWVolumeWidget::GetPlaneWidgetScalarsVisibility()
{
  return this->PlaneWidget->GetDrawPlane();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::UpdateProbe()
{
  this->PlaneWidget->UpdatePlacement();
  this->InvokeEvent(vtkKWEvent::ObliqueProbeMovementEvent, NULL);
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::ResetPlaneWidget()
{
  if (!this->Input)
    {
    return;
    }

  double *bounds = this->Input->GetBounds();
  this->PlaneWidget->PlaceWidget(
    bounds[0], bounds[1], bounds[2], bounds[3], bounds[4], bounds[5]);
  // Set it to Axial by default
  this->PlaneWidget->SetNormal(0,0,1);
  this->PlaneWidget->SetOrigin(
    ((double)bounds[0] + (double)bounds[1]) * 0.5,
    ((double)bounds[2] + (double)bounds[3]) * 0.5,
    ((double)bounds[4] + (double)bounds[5]) * 0.5);

  this->UpdateProbe();

  this->Render();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetStandardCameraView(int type)
{
  vtkCamera *c = this->GetRenderer()->GetActiveCamera();
  double v[3];
  
  c->GetFocalPoint(v);
  
  switch(type)
    {
    case vtkKWVolumeWidget::STANDARD_VIEW_PLUS_X:
      c->SetPosition(v[0]+1, v[1], v[2]);
      c->SetViewUp(0, 0, 1);
      break;

    case vtkKWVolumeWidget::STANDARD_VIEW_PLUS_Y:
      c->SetPosition(v[0], v[1]+1, v[2]);
      c->SetViewUp(0, 0, 1);
      break;

    case vtkKWVolumeWidget::STANDARD_VIEW_PLUS_Z:
      c->SetPosition(v[0], v[1], v[2]+1);
      c->SetViewUp(0, 1, 0);
      break;

    case vtkKWVolumeWidget::STANDARD_VIEW_MINUS_X:
      c->SetPosition(v[0]-1, v[1], v[2]);
      c->SetViewUp(0, 0, 1);
      break;

    case vtkKWVolumeWidget::STANDARD_VIEW_MINUS_Y:
      c->SetPosition(v[0], v[1]-1, v[2]);
      c->SetViewUp(0, 0, 1);
      break;

    case vtkKWVolumeWidget::STANDARD_VIEW_MINUS_Z:
      c->SetPosition(v[0], v[1], v[2]-1);
      c->SetViewUp(0, 1, 0);
      break;
    }

  this->ResetCamera();
  this->Render();
}

//----------------------------------------------------------------------------
const char* 
vtkKWVolumeWidget::GetStandardCameraViewAsMedicalString(int type)
{
  switch(type)
    {
    case vtkKWVolumeWidget::STANDARD_VIEW_PLUS_X:
      return ks_("Standard Views|Button|Left|L");

    case vtkKWVolumeWidget::STANDARD_VIEW_MINUS_X:
      return ks_("Standard Views|Button|Right|R");

    case vtkKWVolumeWidget::STANDARD_VIEW_PLUS_Y:
      return ks_("Standard Views|Button|Posterior|P");

    case vtkKWVolumeWidget::STANDARD_VIEW_MINUS_Y:
      return ks_("Standard Views|Button|Anterior|A");

    case vtkKWVolumeWidget::STANDARD_VIEW_PLUS_Z:
      return ks_("Standard Views|Button|Superior|S");

    case vtkKWVolumeWidget::STANDARD_VIEW_MINUS_Z:
      return ks_("Standard Views|Button|Inferior|I");
    }

  return NULL;
}

//----------------------------------------------------------------------------
const char* 
vtkKWVolumeWidget::GetStandardCameraViewAsMedicalHelpString(int type)
{
  switch(type)
    {
    case vtkKWVolumeWidget::STANDARD_VIEW_PLUS_X:
      return k_("View the left side of the volume");

    case vtkKWVolumeWidget::STANDARD_VIEW_MINUS_X:
      return k_("View the right side of the volume");

    case vtkKWVolumeWidget::STANDARD_VIEW_PLUS_Y:
      return k_("View the posterior side of the volume");

    case vtkKWVolumeWidget::STANDARD_VIEW_MINUS_Y:
      return k_("View the anterior side of the volume");

    case vtkKWVolumeWidget::STANDARD_VIEW_PLUS_Z:
      return k_("View the superior side of the volume");

    case vtkKWVolumeWidget::STANDARD_VIEW_MINUS_Z:
      return k_("View the inferior side of the volume");
    }

  return NULL;
}

//----------------------------------------------------------------------------
const char* 
vtkKWVolumeWidget::GetStandardCameraViewAsDefaultString(int type)
{
  switch(type)
    {
    case vtkKWVolumeWidget::STANDARD_VIEW_PLUS_X:
      return ks_("Standard Views|Button|+X");

    case vtkKWVolumeWidget::STANDARD_VIEW_MINUS_X:
      return ks_("Standard Views|Button|-X");

    case vtkKWVolumeWidget::STANDARD_VIEW_PLUS_Y:
      return ks_("Standard Views|Button|+Y");

    case vtkKWVolumeWidget::STANDARD_VIEW_MINUS_Y:
      return ks_("Standard Views|Button|-Y");

    case vtkKWVolumeWidget::STANDARD_VIEW_PLUS_Z:
      return ks_("Standard Views|Button|+Z");

    case vtkKWVolumeWidget::STANDARD_VIEW_MINUS_Z:
      return ks_("Standard Views|Button|-Z");
    }

  return NULL;
}

//----------------------------------------------------------------------------
const char* 
vtkKWVolumeWidget::GetStandardCameraViewAsDefaultHelpString(int type)
{
  switch(type)
    {
    case vtkKWVolumeWidget::STANDARD_VIEW_PLUS_X:
      return k_("View the +X side of the volume");

    case vtkKWVolumeWidget::STANDARD_VIEW_MINUS_X:
      return k_("View the -X side of the volume");

    case vtkKWVolumeWidget::STANDARD_VIEW_PLUS_Y:
      return k_("View the +Y side of the volume");

    case vtkKWVolumeWidget::STANDARD_VIEW_MINUS_Y:
      return k_("View the -Y side of the volume");

    case vtkKWVolumeWidget::STANDARD_VIEW_PLUS_Z:
      return k_("View the +Z side of the volume");

    case vtkKWVolumeWidget::STANDARD_VIEW_MINUS_Z:
      return k_("View the -Z side of the volume");
    }

  return NULL;
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::UpdateRenderer(int x, int y)
{
  vtkLightCollection *lights = this->GetRenderer()->GetLights();
  lights->InitTraversal();
  this->CurrentLight = lights->GetNextItem();
  
  this->LastPosition[0] = x;
  this->LastPosition[1] = y;
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::EnterCallback(int x, int y)
{
  this->UpdateRenderer(x, y);
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetPerspectiveViewAngle(double val)
{
  if (this->GetPerspectiveViewAngle() != val)
    {
    vtkCamera *cam = this->GetRenderer()->GetActiveCamera();
    if (cam)
      {
      cam->SetViewAngle(val);
      }
    this->Render();
    }
}

//----------------------------------------------------------------------------
double vtkKWVolumeWidget::GetPerspectiveViewAngle()
{
  vtkCamera *cam = this->GetRenderer()->GetActiveCamera();
  if (cam)
    {
    return cam->GetViewAngle();
    }
  return 0.0;
}

//----------------------------------------------------------------------------
int vtkKWVolumeWidget::GetNumberOfLights()
{
  return this->GetRenderer()->GetLights()->GetNumberOfItems();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetLightVisibility(int idx, int vis)
{
  if (this->GetLightVisibility(idx) == vis)
    {
    return;
    }

  vtkLight *light =
    vtkLight::SafeDownCast(this->GetRenderer()->GetLights()->GetItemAsObject(idx));
  if (!light)
    {
    return;
    }
  
  light->SetSwitch(vis);
  this->Render();
}

//----------------------------------------------------------------------------
int vtkKWVolumeWidget::GetLightVisibility(int idx)
{
  vtkLight *light =
    vtkLight::SafeDownCast(this->GetRenderer()->GetLights()->GetItemAsObject(idx));
  if (!light)
    {
    return 0;
    }
  
  return light->GetSwitch();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetLightPosition(int idx, double x, double y, double z)
{
  vtkLight *light =
    vtkLight::SafeDownCast(this->GetRenderer()->GetLights()->GetItemAsObject(idx));
  if (!light)
    {
    return;
    }
  
  light->SetPosition(x, y, z);

  if (this->GetLightVisibility(idx))
    {
    this->Render();
    }
}

//----------------------------------------------------------------------------
double* vtkKWVolumeWidget::GetLightPosition(int idx)
{
  vtkLight *light =
    vtkLight::SafeDownCast(this->GetRenderer()->GetLights()->GetItemAsObject(idx));
  if (!light)
    {
    return 0;
    }
  
  return light->GetPosition();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetLightColor(int idx, double r, double g, double b)
{
  double *color = this->GetLightColor(idx);
  if (!color || (color[0] == r && color[1] == g && color[2] == b))
    {
    return;
    }

  vtkLight *light =
    vtkLight::SafeDownCast(this->GetRenderer()->GetLights()->GetItemAsObject(idx));
  if ( ! light )
    {
    return;
    }
  
  light->SetColor(r, g, b);

  if (this->GetLightVisibility(idx))
    {
    this->Render();
    }
}

//----------------------------------------------------------------------------
double* vtkKWVolumeWidget::GetLightColor(int idx)
{
  vtkLight *light =
    vtkLight::SafeDownCast(this->GetRenderer()->GetLights()->GetItemAsObject(idx));
  if (!light)
    {
    return 0;
    }
  
  return light->GetColor();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetLightIntensity(int idx, double val)
{
  vtkLight *light =
    vtkLight::SafeDownCast(this->GetRenderer()->GetLights()->GetItemAsObject(idx));
  if ( ! light )
    {
    return;
    }
  
  light->SetIntensity(val);

  if (this->GetLightVisibility(idx))
    {
    this->Render();
    }
}

//----------------------------------------------------------------------------
double vtkKWVolumeWidget::GetLightIntensity(int idx)
{
  vtkLight *light =
    vtkLight::SafeDownCast(this->GetRenderer()->GetLights()->GetItemAsObject(idx));
  if (!light)
    {
    return 0;
    }
  
  return light->GetIntensity();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::MouseButtonPressCallback(
  int num, int x, int y, int ctrl, int shift, int alt, int repeat)
{
  this->Superclass::MouseButtonPressCallback(
    num, x, y, ctrl, shift, alt, repeat);
  this->SetRenderModeToInteractive();
  this->InvokeEvent(vtkCommand::StartInteractionEvent, NULL);
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::MouseButtonReleaseCallback(
  int num, int x, int y, int ctrl, int shift, int alt)
{
  this->Superclass::MouseButtonReleaseCallback(
    num, x, y, ctrl, shift, alt);
  this->InvokeEvent(vtkCommand::EndInteractionEvent, NULL);
  this->SetRenderModeToStill();
  this->Render();
}

//----------------------------------------------------------------------------
int vtkKWVolumeWidget::GetBoundingBoxVisibility()
{
  return (this->BoundingBoxAnnotation &&
          this->HasViewProp(this->BoundingBoxAnnotation) && 
          this->BoundingBoxAnnotation->GetVisibility());
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetBoundingBoxVisibility(int v)
{
  if (this->GetBoundingBoxVisibility() == v)
    {
    return;
    }

  if (v)
    {
    this->BoundingBoxAnnotation->VisibilityOn();
    if (!this->HasViewProp(this->BoundingBoxAnnotation))
      {
      this->AddViewProp(this->BoundingBoxAnnotation);
      }
    this->RegisterIntermixIntersectingGeometry();
    }
  else
    {
    this->BoundingBoxAnnotation->VisibilityOff();
    if (this->HasViewProp(this->BoundingBoxAnnotation))
      {
      this->RemoveViewProp(this->BoundingBoxAnnotation);
      }
    this->UnRegisterIntermixIntersectingGeometry();
    }

  this->Render();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::ToggleBoundingBoxVisibility()
{
  this->SetBoundingBoxVisibility(!this->GetBoundingBoxVisibility());
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetBoundingBoxColor(double r, double g, double b)
{
  double *color = this->GetBoundingBoxColor();
  if (!color || (color[0] == r && color[1] == g && color[2] == b))
    {
    return;
    }

  this->BoundingBoxAnnotation->GetProperty()->SetColor(r, g, b);

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

//----------------------------------------------------------------------------
double* vtkKWVolumeWidget::GetBoundingBoxColor()
{
  return this->BoundingBoxAnnotation->GetProperty()->GetColor();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetSplineCurves3D( vtkKW3DSplineCurvesWidget * curves )
{
  this->Superclass::SetSplineCurves3D( curves );
  vtkRenderWindowInteractor *interactor = this->GetRenderWindowInteractor();
  if( this->SplineCurves3D && interactor)
    {
    this->SplineCurves3D->SetInteractor(interactor);
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetSplineSurfaces3D( vtkKW3DSplineSurfacesWidget * surfaces )
{
  this->Superclass::SetSplineSurfaces3D( surfaces );
  vtkRenderWindowInteractor *interactor = this->GetRenderWindowInteractor();
  if(this->SplineCurves3D  && interactor)
    {
    this->SplineSurfaces3D->SetInteractor(interactor);
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::UpdateHistogramSet()
{
  // We need data here and the histogram set

  if (!this->HistogramSet)
    {
    return;
    }

  // Add/update histograms for each component of the image data scalars
  // Skip RGB for dependent RGB(A) images

  vtkImageData *input = this->GetInput();
  vtkDataArray *array = input ? input->GetPointData()->GetScalars() : NULL;
  if (array)
    {
    int nb_components = array->GetNumberOfComponents();
    int ind_components = this->GetIndependentComponents();

    this->HistogramSet->AddHistograms(
      array, NULL, (!ind_components && nb_components >= 3 ? 0x7 : 0x0));
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::UpdateHistogramSetForGradients()
{
  // We need data here

  vtkImageData *input = this->GetInput();
  if (!input || !this->HistogramSet)
    {
    return;
    }

  vtkDataArray *input_scalars = input->GetPointData()->GetScalars();
  if (!input_scalars)
    {
    return;
    }
  
  // Add/update histograms for the gradient magnitudes
  
  // We used to get the gradients from the ray cast mapper for the
  // histogram - we no longer want to do that. Also there was 
  // horrible code here that actually made yet another copy of the
  // magnitudes - we definitely don't want to do that
  // TODO decide on what we are doing with gradient magnitudes
  return;
  
  this->InvokeEvent(vtkKWEvent::HistogramChangedEvent, NULL);
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetPrinting(int arg)
{
  if (arg == this->GetPrinting())
    {
    return;
    }

  this->Superclass::SetPrinting(arg);
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetInteractionMode(int type)
{
  if (type < vtkKWVolumeWidget::INTERACTION_MODE_PAN ||
      type > vtkKWVolumeWidget::INTERACTION_MODE_ALL)
    {
    vtkErrorMacro("Error - invalid slice type " << type);
    return;
    }
  
  if (this->InteractionMode == type)
    {
    return;
    }

  this->InteractionMode = type;

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

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::PopulateContextMenuWithCameraEntries(vtkKWMenu *menu)
{
  this->Superclass::PopulateContextMenuWithCameraEntries(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;

  // Projection Mode

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

  cascade_index = projection_menu->AddRadioButton(
    "Parallel", this, "SetProjectionTypeToParallel");
  projection_menu->SetItemSelectedValueAsInt(
    cascade_index, vtkKWVolumeWidget::PARALLEL_PROJECTION);
  if (show_icons)
    {
    projection_menu->SetItemImageToPredefinedIcon(
      cascade_index, vtkKWIcon::IconParallelProjection);
    projection_menu->SetItemCompoundModeToLeft(cascade_index);
    }

  cascade_index = projection_menu->AddRadioButton(
    "Perspective", this, "SetProjectionTypeToPerspective");
  projection_menu->SetItemSelectedValueAsInt(
    cascade_index, vtkKWVolumeWidget::PERSPECTIVE_PROJECTION);
  if (show_icons)
    {
    projection_menu->SetItemImageToPredefinedIcon(
      cascade_index, vtkKWIcon::IconPerspectiveProjection);
    projection_menu->SetItemCompoundModeToLeft(cascade_index);
    }

  projection_menu->SelectItemWithSelectedValueAsInt(this->GetProjectionType());
  
  index = menu->AddCascade(k_("Projection Type"), projection_menu);
  if (show_icons)
    {
    if (this->GetProjectionType() == vtkKWVolumeWidget::PERSPECTIVE_PROJECTION)
      {
      menu->SetItemImageToPredefinedIcon(
        index, vtkKWIcon::IconPerspectiveProjection);
      }
    else
      {
      menu->SetItemImageToPredefinedIcon(
        index, vtkKWIcon::IconParallelProjection);
      }
    menu->SetItemCompoundModeToLeft(index);
    }
  projection_menu->Delete();

  // Slice Orientation

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

  int standard_views[] = 
    { 
    vtkKWVolumeWidget::STANDARD_VIEW_PLUS_X,
    vtkKWVolumeWidget::STANDARD_VIEW_MINUS_X,
    vtkKWVolumeWidget::STANDARD_VIEW_PLUS_Y,
    vtkKWVolumeWidget::STANDARD_VIEW_MINUS_Y,
    vtkKWVolumeWidget::STANDARD_VIEW_PLUS_Z,
    vtkKWVolumeWidget::STANDARD_VIEW_MINUS_Z
    };

  // This is not very elegant, we should have had a variable in the superclass
  // that says if we are dealing with medical data or not. In the meantime, 
  // use the OrientationWidget, hoping it's up to date.

  int is_medical = 
    this->GetOrientationWidget() && 
    this->GetOrientationWidget()->GetAnnotationType() == 
    vtkKWOrientationWidget::ANNOTATION_TYPE_MEDICAL;

  int i;
  for (i = 0; i < sizeof(standard_views) / sizeof(standard_views[0]); i++)
    {
    const char *cmd_name, *cmd_help;
    if (is_medical)
      {
      cmd_name = 
        vtkKWVolumeWidget::GetStandardCameraViewAsMedicalString(
          standard_views[i]);
      cmd_help = 
        vtkKWVolumeWidget::GetStandardCameraViewAsMedicalHelpString(
          standard_views[i]);
      }
    else
      {
      cmd_name = 
        vtkKWVolumeWidget::GetStandardCameraViewAsDefaultString(
          standard_views[i]);
      cmd_help = 
        vtkKWVolumeWidget::GetStandardCameraViewAsDefaultHelpString(
          standard_views[i]);
      }

    vtksys_ios::ostringstream cmd;
    cmd << "SetStandardCameraView " << standard_views[i];

    // Add a new entry

    cascade_index = standard_views_menu->AddCommand(
      cmd_name, this, cmd.str().c_str());
    standard_views_menu->SetItemHelpString(cascade_index, cmd_help);

    if (i == 2 || i == 4)
      {
      standard_views_menu->SetItemColumnBreak(cascade_index, 1);
      }
    }
  
  index = menu->AddCascade(k_("Standard View"), standard_views_menu);
  if (show_icons)
    {
    menu->SetItemImageToPredefinedIcon(
      index, vtkKWIcon::IconStandardView);
    menu->SetItemCompoundModeToLeft(index);
    }
  standard_views_menu->Delete();
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::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;

  // Scale bar

  index = menu->AddCheckButton(
    ks_("Annotation|Scale Bar"), 
    this, "ToggleScaleBarVisibility");
  menu->SetItemSelectedState(index, this->GetScaleBarVisibility());
  if (show_icons)
    {
    menu->SetItemImageToPredefinedIcon(
      index, vtkKWIcon::IconScaleBarAnnotation);
    menu->SetItemCompoundModeToLeft(index);
    }

  // Scalar bar

  index = menu->AddCheckButton(
    ks_("Annotation|Color Bar"), 
    this, "ToggleScalarBarVisibility");
  menu->SetItemSelectedState(index, this->GetScalarBarVisibility());
  if (show_icons)
    {
    menu->SetItemImageToPredefinedIcon(
      index, vtkKWIcon::IconColorBarAnnotation);
    menu->SetItemCompoundModeToLeft(index);
    }

  // Bounding box

  index = menu->AddCheckButton(
    ks_("Annotation|Bounding Box"), 
    this, "ToggleBoundingBoxVisibility");
  menu->SetItemSelectedState(index, this->GetBoundingBoxVisibility());
  if (show_icons)
    {
    menu->SetItemImageToPredefinedIcon(
      index, vtkKWIcon::IconBoundingBox);
    menu->SetItemCompoundModeToLeft(index);
    }

  /*
  // Nah, why would you allow that
  // Volume itself

  index = menu->AddCheckButton(
    ks_("Annotation|Volume"), 
    this, "ToggleVolumeVisibility");
  menu->SetItemSelectedState(index, this->GetVolumeVisibility());
  */
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::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|Rotate"), 
    this, "SetInteractionModeToRotate");
  menu->SetItemSelectedValueAsInt(
    index, vtkKWVolumeWidget::INTERACTION_MODE_ROTATE);
  menu->SetItemGroupName(index, group_name);
  if (show_icons)
    {
    menu->SetItemImageToPredefinedIcon(
      index, vtkKWIcon::IconCrystalProject16x16ActionsRotate);
    menu->SetItemCompoundModeToLeft(index);
    }
  
  index = menu->AddRadioButton(
    ks_("Interaction Mode|Pan"), 
    this, "SetInteractionModeToPan");
  menu->SetItemSelectedValueAsInt(
    index, vtkKWVolumeWidget::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, vtkKWVolumeWidget::INTERACTION_MODE_ZOOM);
  menu->SetItemGroupName(index, group_name);
  if (show_icons)
    {
    menu->SetItemImageToPredefinedIcon(
      index, vtkKWIcon::IconNuvola16x16ActionsViewMag);
    menu->SetItemCompoundModeToLeft(index);
    }
  
  menu->SelectItemInGroupWithSelectedValueAsInt(
    "InteractionMode", this->InteractionMode);
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::AddCallbackCommandObservers()
{
  this->Superclass::AddCallbackCommandObservers();

  // To update the probe

  this->AddCallbackCommandObserver(
    this->PlaneWidget, vtkCommand::InteractionEvent);

  // Abort logic

  this->AddCallbackCommandObserver(
    this->RenderWindow, vtkCommand::AbortCheckEvent);

  // Progress

  if (this->VolumeMapper)
    {
    this->AddVolumeMapperProgress(
      this->VolumeMapper,
      ks_("Progress|Generating Image"));

    this->AddVolumeMapperGradientProgress(
      this->VolumeMapper,
      ks_("Progress|Generating Full Resolution Gradients"));
    }

  // Use GPU Rendering

  vtkKWApplicationPro *app_pro = 
    vtkKWApplicationPro::SafeDownCast(this->GetApplication());
  if (app_pro)
    {
    this->AddCallbackCommandObserver(
      app_pro, vtkKWApplicationPro::UseGPURenderingChangedEvent);
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::RemoveCallbackCommandObservers()
{
  this->Superclass::RemoveCallbackCommandObservers();

  // To update the probe

  this->RemoveCallbackCommandObserver(
    this->PlaneWidget, vtkCommand::InteractionEvent);

  // Abort logic

  this->RemoveCallbackCommandObserver(
    this->RenderWindow, vtkCommand::AbortCheckEvent);

  // Progress

  if (this->VolumeMapper)
    {
    this->RemoveVolumeMapperProgress(this->VolumeMapper);
    this->RemoveVolumeMapperGradientProgress(this->VolumeMapper);
    }

  // Use GPU Rendering

  vtkKWApplicationPro *app_pro = 
    vtkKWApplicationPro::SafeDownCast(this->GetApplication());
  if (app_pro)
    {
  this->RemoveCallbackCommandObserver(
    app_pro, vtkKWApplicationPro::UseGPURenderingChangedEvent);
    }
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::ProcessCallbackCommandEvents(vtkObject *caller, 
                                                     unsigned long event,
                                                     void *calldata)
{
  int *iarg = reinterpret_cast<int*>(calldata);

  vtkKWApplicationPro *app_pro = 
    vtkKWApplicationPro::SafeDownCast(caller);

  switch (event)
    {
    case vtkCommand::InteractionEvent:
      if (caller == this->PlaneWidget)
        {
        this->UpdateProbe();
        }
      break;

    case vtkCommand::AbortCheckEvent:
      if (!this->GetPrinting() && this->ShouldIAbort() == 2)
        {
        this->GetRenderWindow()->SetAbortRender(1);    
        }
      break;

    case vtkKWApplicationPro::UseGPURenderingChangedEvent:
      if (this->VolumeMapper)
        {
        this->VolumeMapper->SetRequestedRenderMode(
          *iarg ? vtkKWEVolumeMapper::DefaultRenderMode
          : vtkKWEVolumeMapper::RayCastAndTextureRenderMode);
        }

    case vtkCommand::VolumeMapperRenderStartEvent:
    case vtkCommand::VolumeMapperRenderEndEvent:
    case vtkCommand::VolumeMapperRenderProgressEvent:
      this->InvokeEvent(event, calldata);
      break;
    }

  this->Superclass::ProcessCallbackCommandEvents(caller, event, calldata);
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::SetVolumeProperty(vtkVolumeProperty *arg)
{
  if (this->Volume && this->VolumeProperty != arg)
    {
    this->Volume->SetProperty(arg);
    }  

  this->Superclass::SetVolumeProperty(arg);
}

//----------------------------------------------------------------------------
void vtkKWVolumeWidget::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  
  os << indent << "VolumeMapper: " << this->VolumeMapper << endl;

  os << indent << "ReformatBoxVisibility: " 
     << this->ReformatBoxVisibility << endl;
  os << indent << "Volume: " << this->Volume << endl;
  os << indent << "Reformat: " << this->Reformat << endl;
  os << indent << "ReformatManipulationStyle: "
     << this->ReformatManipulationStyle << endl;
  os << indent << "ReformatThickness: " << this->ReformatThickness << endl;
  os << indent << "ReformatNormal: (" 
     << this->ReformatNormal[0] << ", " 
     << this->ReformatNormal[1] << ", " 
     << this->ReformatNormal[2] << ")" << endl;
  os << indent << "ReformatUp: (" 
     << this->ReformatUp[0] << ", " 
     << this->ReformatUp[1] << ", " 
     << this->ReformatUp[2] << ")" << endl;
  os << indent << "ReformatLocation: (" 
     << this->ReformatLocation[0] << ", " 
     << this->ReformatLocation[1] << ", " 
     << this->ReformatLocation[2] << ")" << endl;
  os << indent << "Input: ";
  os << indent << "InteractorStyle: " << this->InteractorStyle << endl;
  os << indent << "BoundingBoxAnnotation: " 
     << this->BoundingBoxAnnotation <<endl;
  os << indent << "Cursor3DAnnotation: " << this->Cursor3DAnnotation <<endl;
  os << indent << "ScalarBarWidget: " << this->ScalarBarWidget <<endl;
  os << indent << "ScaleBarWidget: " << this->ScaleBarWidget <<endl;
  os << indent << "ZSampling: " << this->ZSampling <<endl;
  os << indent << "SingleUpdateRate: " << this->SingleUpdateRate << endl;
  os << indent << "SurfaceAnnotation: " << this->SurfaceAnnotation <<endl;
  os << indent << "PlaneWidget: " << this->PlaneWidget << endl;
  os << indent << "BoundingBoxAnnotation: " 
     << this->BoundingBoxAnnotation << endl;
  os << indent << "HistogramSet: " << this->HistogramSet << endl;
  os << indent << "InteractionMode: " << this->InteractionMode << endl;
  os << indent << "ResetCameraZoomRatio: " << this->ResetCameraZoomRatio << endl;
}
