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

  Program:   Visualization Toolkit
  Module:    vtkPVImageSliceMapper.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.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 "vtkPVImageSliceMapper.h"


#include "vtkObjectFactory.h"
#include "vtkInformation.h"
#include "vtkImageData.h"
#include "vtkCommand.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkScalarsToColors.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkExecutive.h"
#include "vtkDataArray.h"
#include "vtkScalarsToColors.h"

#ifdef VTKGL2
# include "vtkCellArray.h"
# include "vtkDataSetAttributes.h"
# include "vtkExtractVOI.h"
# include "vtkFloatArray.h"
# include "vtkNew.h"
# include "vtkOpenGLPolyDataMapper.h"
# include "vtkOpenGLTexture.h"
# include "vtkPoints.h"
# include "vtkPointData.h"
# include "vtkPolyData.h"
# include "vtkProperty.h"
# include "vtkTextureObject.h"
# include "vtkTrivialProducer.h"


// no-op  just here to shut up python wrapping
class vtkPainter : public vtkObject {};
//-----------------------------------------------------------------------------
void vtkPVImageSliceMapper::SetPainter(vtkPainter* )
{
}

#else

# include "vtkTexturePainter.h"

//-----------------------------------------------------------------------------
class vtkPVImageSliceMapper::vtkObserver : public vtkCommand
{
public:
  static vtkObserver* New()
    { return new vtkObserver; }

  virtual void Execute(vtkObject* caller, unsigned long event, void*)
    {
    vtkPainter* p = vtkPainter::SafeDownCast(caller);
    if (this->Target && p && event == vtkCommand::ProgressEvent)
      {
      this->Target->UpdateProgress(p->GetProgress());
      }
    }
  vtkObserver()
    {
    this->Target = 0;
    }
  vtkPVImageSliceMapper* Target;
};

#endif

vtkStandardNewMacro(vtkPVImageSliceMapper);
//----------------------------------------------------------------------------
vtkPVImageSliceMapper::vtkPVImageSliceMapper()
{
  this->Piece = 0;
  this->NumberOfPieces = 1;
  this->NumberOfSubPieces = 1;
  this->GhostLevel = 0;

  this->Slice = 0;
  this->SliceMode = XY_PLANE;
  this->UseXYPlane = 0;

#ifdef VTKGL2
  this->Texture = vtkOpenGLTexture::New();
  this->Texture->RepeatOff();
  vtkNew<vtkPolyData> polydata;
  vtkNew<vtkPoints> points;
  points->SetNumberOfPoints(4);
  polydata->SetPoints(points.Get());

  vtkNew<vtkCellArray> tris;
  tris->InsertNextCell(3);
  tris->InsertCellPoint(0);
  tris->InsertCellPoint(1);
  tris->InsertCellPoint(2);
  tris->InsertNextCell(3);
  tris->InsertCellPoint(0);
  tris->InsertCellPoint(2);
  tris->InsertCellPoint(3);
  polydata->SetPolys(tris.Get());

  vtkNew<vtkFloatArray> tcoords;
  tcoords->SetNumberOfComponents(2);
  tcoords->SetNumberOfTuples(4);
  tcoords->SetTuple2(0, 0.0, 0.0);
  tcoords->SetTuple2(1, 1.0, 0.0);
  tcoords->SetTuple2(2, 1.0, 1.0);
  tcoords->SetTuple2(3, 0.0, 1.0);
  polydata->GetPointData()->SetTCoords(tcoords.Get());

  vtkNew<vtkTrivialProducer> prod;
  prod->SetOutput(polydata.Get());
  vtkNew<vtkOpenGLPolyDataMapper> polyDataMapper;
  polyDataMapper->SetInputConnection(prod->GetOutputPort());
  this->PolyDataActor = vtkActor::New();
  this->PolyDataActor->SetMapper(polyDataMapper.Get());
  this->PolyDataActor->SetTexture(this->Texture);
  this->PolyDataActor->GetProperty()->SetAmbient(1.0);
  this->PolyDataActor->GetProperty()->SetDiffuse(0.0);

#else
  this->Observer = vtkObserver::New();
  this->Observer->Target = this;
  this->Painter = 0;

  this->PainterInformation = vtkInformation::New();
  vtkTexturePainter* painter = vtkTexturePainter::New();
  this->SetPainter(painter);
  painter->Delete();
#endif
}

//----------------------------------------------------------------------------
vtkPVImageSliceMapper::~vtkPVImageSliceMapper()
{
#ifdef VTKGL2
  this->Texture->Delete();
  this->Texture = NULL;
  this->PolyDataActor->Delete();
  this->PolyDataActor = NULL;
#else
  this->SetPainter(NULL);

  this->Observer->Target = 0;
  this->Observer->Delete();
  this->PainterInformation->Delete();
#endif
}

#ifdef VTKGL2

//----------------------------------------------------------------------------
static int vtkGetDataDimension(int inextents[6])
{
  int dim[3];
  dim[0] = inextents[1] - inextents[0] + 1;
  dim[1] = inextents[3] - inextents[2] + 1;
  dim[2] = inextents[5] - inextents[4] + 1;
  int dimensionality = 0;
  dimensionality += (dim[0]>1? 1 : 0);
  dimensionality += (dim[1]>1? 1 : 0);
  dimensionality += (dim[2]>1? 1 : 0);
  return dimensionality;
}

static const int XY_PLANE_QPOINTS_INDICES[] =
{0, 2, 4, 1, 2, 4, 1, 3, 4, 0, 3, 4};
static const int YZ_PLANE_QPOINTS_INDICES[] =
{0, 2, 4, 0, 3, 4, 0, 3, 5, 0, 2, 5};
static const int XZ_PLANE_QPOINTS_INDICES[] =
{0, 2, 4, 1, 2, 4, 1, 2, 5, 0, 2, 5};

static const int *XY_PLANE_QPOINTS_INDICES_ORTHO =
XY_PLANE_QPOINTS_INDICES;
static const int YZ_PLANE_QPOINTS_INDICES_ORTHO[] =
{2, 4, 0, 3, 4, 0, 3, 5, 0, 2, 5, 0};
static const int XZ_PLANE_QPOINTS_INDICES_ORTHO[] =
{ 4, 0, 2, 4, 1, 2, 5, 1, 2, 5, 0, 2 };


int vtkPVImageSliceMapper::SetupScalars(vtkImageData* input)
{
  // Based on the scalar mode, scalar array, scalar id,
  // we need to tell the vtkTexture to use the appropriate scalars.
  int cellFlag = 0;
  vtkDataArray* scalars = vtkAbstractMapper::GetScalars(input,
    this->ScalarMode,
    *this->ArrayName ? VTK_GET_ARRAY_BY_NAME : VTK_GET_ARRAY_BY_ID,
    this->ArrayId,
    this->ArrayName,
    cellFlag);

  if (!scalars)
    {
    vtkWarningMacro("Failed to locate selected scalars. Will use image "
      "scalars by default.");
    // If not scalar array specified, simply use the point data (the cell
    // data) scalars.
    this->Texture->SetInputArrayToProcess(0, 0, 0,
      vtkDataObject::FIELD_ASSOCIATION_POINTS_THEN_CELLS,
      vtkDataSetAttributes::SCALARS);
    cellFlag = 0;
    }
  else
    {
    // Pass the scalar array choice to the texture.
    this->Texture->SetInputArrayToProcess(0, 0, 0,
      (cellFlag? vtkDataObject::FIELD_ASSOCIATION_CELLS:
       vtkDataObject::FIELD_ASSOCIATION_POINTS),
      scalars->GetName());
    }
  return cellFlag;
}


//----------------------------------------------------------------------------
void vtkPVImageSliceMapper::RenderInternal(vtkRenderer *renderer,
                                           vtkActor *actor)
{
  vtkImageData* input = vtkImageData::SafeDownCast(this->GetInput());
  if (this->UpdateTime < input->GetMTime() || this->UpdateTime < this->MTime)
    {
    this->UpdateTime.Modified();
    int sliceDescription = 0;
    int inextent[6];
    int outextent[6];
    // we deliberately use whole extent here. So on processes where the slice is
    // not available, the vtkExtractVOI filter will simply yield an empty
    // output.
    int *wext = this->GetInputInformation(0, 0)->Get(
      vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT());
    memcpy(inextent, wext, 6*sizeof(int));
    memcpy(outextent, inextent, sizeof(int)*6);
    int numdims = ::vtkGetDataDimension(inextent);
    int dims[3];
    dims[0] = inextent[1] - inextent[0] + 1;
    dims[1] = inextent[3] - inextent[2] + 1;
    dims[2] = inextent[5] - inextent[4] + 1;

    // Based on the scalar mode, scalar array, scalar id,
    // we need to tell the vtkTexture to use the appropriate scalars.
    int cellFlag = this->SetupScalars(input);

    // Determine the VOI to extract:
    // * If the input image is 3D, then we respect the slice number and slice
    // direction the user recommended.
    // * If the input image is 2D, we simply show the input image slice.
    // * If the input image is 1D, we raise an error.
    if (numdims==3)
      {
      int slice = this->Slice;
      // clamp the slice number at min val.
      slice = (slice < 0)? 0 : slice;

      // if cell centered, then dimensions reduces by 1.
      int curdim = cellFlag? (dims[this->SliceMode]-1) :
        dims[this->SliceMode];

      // clamp the slice number at max val.
      slice = (slice >= curdim)? curdim-1: slice;

      if (this->SliceMode == XY_PLANE) // XY plane
        {
        outextent[4] = outextent[5] = outextent[4]+slice;
        sliceDescription = VTK_XY_PLANE;
        }
      else if (this->SliceMode == YZ_PLANE) // YZ plane
        {
        outextent[0] = outextent[1] = outextent[0] + slice;
        sliceDescription = VTK_YZ_PLANE;
        }
      else if (this->SliceMode == XZ_PLANE) // XZ plane
        {
        outextent[2] = outextent[3] = outextent[2] + slice;
        sliceDescription = VTK_XZ_PLANE;
        }
      }
    else if (numdims==2)
      {
      if (inextent[4] == inextent[5]) //XY plane
        {
        //nothing to change.
        sliceDescription = VTK_XY_PLANE;
        }
      else if (inextent[0] == inextent[1]) /// YZ plane
        {
        sliceDescription = VTK_YZ_PLANE;
        }
      else if (inextent[2] == inextent[3]) // XZ plane
        {
        sliceDescription = VTK_XZ_PLANE;
        }
      }
    else
      {
      vtkErrorMacro("Incorrect dimensionality.");
      return;
      }

    vtkNew<vtkImageData> clone;
    clone->ShallowCopy(input);

    vtkNew<vtkExtractVOI> extractVOI;
    extractVOI->SetVOI(outextent);
    extractVOI->SetInputData(clone.Get());
    extractVOI->Update();

    int evoi[6];
    extractVOI->GetOutput()->GetExtent(evoi);
    if (evoi[1] < evoi[0] && evoi[3] < evoi[2] && evoi[5] < evoi[4])
      {
      // if vtkExtractVOI did not produce a valid output, that means there's no
      // image slice to display.
      this->Texture->SetInputData(0);
      return;
      }

    // TODO: Here we would have change the input scalars if the user asked us to.
    // The LUT can be simply passed to the vtkTexture. It can handle scalar
    // mapping.
    this->Texture->SetInputConnection(extractVOI->GetOutputPort());
    double outputbounds[6];

    // TODO: vtkExtractVOI is not passing correct origin. Until that's fixed, I
    // will just use the input origin/spacing to compute the bounds.
    clone->SetExtent(evoi);
    clone->GetBounds(outputbounds);

    this->Texture->SetLookupTable(this->LookupTable);
    this->Texture->SetMapColorScalarsThroughLookupTable(
      this->ColorMode == VTK_COLOR_MODE_MAP_SCALARS ? 1 : 0);

    if (cellFlag)
      {
      // Structured bounds are point bounds. Shrink them to reflect cell
      // center bounds.
      // i.e move min bounds up by spacing/2 in that direction
      //     and move max bounds down by spacing/2 in that direction.
      double spacing[3];
      input->GetSpacing(spacing); // since spacing doesn't change, we can use
                                  // input spacing directly.
      for (int dir=0; dir < 3; dir++)
        {
        double& min = outputbounds[2*dir];
        double& max = outputbounds[2*dir+1];
        if (min+spacing[dir] <= max)
          {
          min += spacing[dir]/2.0;
          max -= spacing[dir]/2.0;
          }
        else
          {
          min = max = (min + spacing[dir]/2.0);
          }
        }
      }

    const int *indices = NULL;
    switch (sliceDescription)
      {
    case VTK_XY_PLANE:
      indices = XY_PLANE_QPOINTS_INDICES;
      if (this->UseXYPlane)
        {
        indices = XY_PLANE_QPOINTS_INDICES_ORTHO;
        outputbounds[4]=0;
        }
      break;

    case VTK_YZ_PLANE:
      indices = YZ_PLANE_QPOINTS_INDICES;
      if (this->UseXYPlane)
        {
        indices = YZ_PLANE_QPOINTS_INDICES_ORTHO;
        outputbounds[0]=0;
        }
      break;

    case VTK_XZ_PLANE:
      indices = XZ_PLANE_QPOINTS_INDICES;
      if (this->UseXYPlane)
        {
        indices = XZ_PLANE_QPOINTS_INDICES_ORTHO;
        outputbounds[2]=0;
        }
      break;
      }

    vtkPolyData *poly = vtkPolyDataMapper::SafeDownCast(
      this->PolyDataActor->GetMapper())->GetInput();
    vtkPoints *polyPoints = poly->GetPoints();

    for (int i = 0; i < 4; i++)
      {
      polyPoints->SetPoint(i, outputbounds[indices[i*3]],
        outputbounds[indices[3*i+1]], outputbounds[indices[3*i+2]]);
      }
      polyPoints->Modified();
    }

  if (!this->Texture->GetInput())
    {
    return;
    }

  // copy information to the delegate
  this->PolyDataActor->vtkProp3D::ShallowCopy(actor);
  vtkInformation *info = actor->GetPropertyKeys();
  this->PolyDataActor->SetPropertyKeys(info);
  this->PolyDataActor->SetProperty(actor->GetProperty());

  // Render
  this->Texture->Render(renderer);
  this->PolyDataActor->GetMapper()->Render(renderer, this->PolyDataActor);
  this->Texture->PostRender(renderer);
}

#else

//-----------------------------------------------------------------------------
void vtkPVImageSliceMapper::SetPainter(vtkPainter* p)
{
  if (this->Painter)
    {
    this->Painter->RemoveObservers(vtkCommand::ProgressEvent, this->Observer);
    this->Painter->SetInformation(0);
    }
  vtkSetObjectBodyMacro(Painter, vtkPainter, p);
   if (this->Painter)
    {
    this->Painter->AddObserver(vtkCommand::ProgressEvent, this->Observer);
    this->Painter->SetInformation(this->PainterInformation);
    }
}

//----------------------------------------------------------------------------
void vtkPVImageSliceMapper::UpdatePainterInformation()
{
  vtkInformation* info = this->PainterInformation;
  info->Set(vtkPainter::STATIC_DATA(), this->Static);

  // tell which array to color with.
  if (this->ScalarMode == VTK_SCALAR_MODE_USE_FIELD_DATA)
    {
    vtkErrorMacro("Field data coloring is not supported.");
    this->ScalarMode = VTK_SCALAR_MODE_DEFAULT;
    }

  if (this->ArrayAccessMode == VTK_GET_ARRAY_BY_ID)
    {
    info->Remove(vtkTexturePainter::SCALAR_ARRAY_NAME());
    info->Set(vtkTexturePainter::SCALAR_ARRAY_INDEX(), this->ArrayId);
    }
  else
    {
    info->Remove(vtkTexturePainter::SCALAR_ARRAY_INDEX());
    info->Set(vtkTexturePainter::SCALAR_ARRAY_NAME(), this->ArrayName);
    }
  info->Set(vtkTexturePainter::SCALAR_MODE(), this->ScalarMode);
  info->Set(vtkTexturePainter::LOOKUP_TABLE(), this->LookupTable);
  info->Set(vtkTexturePainter::USE_XY_PLANE(), this->UseXYPlane);

  // tell is we should map unsiged chars thorough LUT.
  info->Set(vtkTexturePainter::MAP_SCALARS(),
    (this->ColorMode == VTK_COLOR_MODE_MAP_SCALARS)? 1 : 0);

  // tell information about the slice.
  info->Set(vtkTexturePainter::SLICE(), this->Slice);
  switch(this->SliceMode)
    {
  case YZ_PLANE:
    info->Set(vtkTexturePainter::SLICE_MODE(), vtkTexturePainter::YZ_PLANE);
    break;

  case XZ_PLANE:
    info->Set(vtkTexturePainter::SLICE_MODE(), vtkTexturePainter::XZ_PLANE);
    break;

  case XY_PLANE:
    info->Set(vtkTexturePainter::SLICE_MODE(), vtkTexturePainter::XY_PLANE);
    break;
    }
}

#endif


//----------------------------------------------------------------------------
void vtkPVImageSliceMapper::ReleaseGraphicsResources (vtkWindow *win)
{
#ifdef VTKGL2
  this->Texture->ReleaseGraphicsResources(win);
  this->PolyDataActor->ReleaseGraphicsResources(win);
#else
  this->Painter->ReleaseGraphicsResources(win);
#endif
  this->Superclass::ReleaseGraphicsResources(win);
}

//----------------------------------------------------------------------------
void vtkPVImageSliceMapper::Render(vtkRenderer* ren, vtkActor* act)
{
  if (this->Static)
    {
    this->RenderPiece(ren, act);
    }
  vtkImageData* input = this->GetInput();
  if (!input)
    {
    vtkErrorMacro("Mapper has no vtkImageData input.");
    return;
    }

  int nPieces = this->NumberOfSubPieces* this->NumberOfPieces;
  for (int cc=0; cc < this->NumberOfSubPieces; cc++)
    {
    int currentPiece = this->NumberOfSubPieces * this->Piece + cc;
    vtkStreamingDemandDrivenPipeline::SetUpdateExtent(this->GetInputInformation(),
      currentPiece, nPieces, this->GhostLevel);
    this->RenderPiece(ren, act);
    }

}

//----------------------------------------------------------------------------
void vtkPVImageSliceMapper::SetInputData(vtkImageData* input)
{
  this->SetInputDataInternal(0, input);
}

//----------------------------------------------------------------------------
vtkImageData* vtkPVImageSliceMapper::GetInput()
{
  return vtkImageData::SafeDownCast(this->GetExecutive()->GetInputData(0, 0));
}

//----------------------------------------------------------------------------
void vtkPVImageSliceMapper::Update(int port)
{
  if (!this->Static)
    {
    int currentPiece, nPieces = this->NumberOfPieces;
    vtkImageData* input = this->GetInput();

    // If the estimated pipeline memory usage is larger than
    // the memory limit, break the current piece into sub-pieces.
    if (input)
      {
      this->GetInputAlgorithm()->UpdateInformation();
      currentPiece = this->NumberOfSubPieces * this->Piece;
      vtkStreamingDemandDrivenPipeline::SetUpdateExtent(
        this->GetInputInformation(),
        currentPiece, this->NumberOfSubPieces*nPieces, this->GhostLevel);
      }

    this->Superclass::Update(port);
    }

#ifndef VTKGL2
  // Set the whole extent on the painter because it needs it internally
  // and it has no access to the pipeline information.
  vtkTexturePainter* ptr = vtkTexturePainter::SafeDownCast(this->GetPainter());

  int *wext = this->GetInputInformation(0, 0)->Get(
    vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT());
  if (wext)
    {
    ptr->SetWholeExtent(wext);
    }
#endif
}


//----------------------------------------------------------------------------
double* vtkPVImageSliceMapper::GetBounds()
{
  static double bounds[6] = {-1.0, 1.0, -1.0, 1.0, -1.0, 1.0};
  vtkImageData* input = this->GetInput();
  if (!input)
    {
    return bounds;
    }

  this->Update();
  input->GetBounds(this->Bounds);
  if (this->UseXYPlane)
    {
    // When using XY plane, the image will be in XY plane placed at the origin,
    // hence we adjust the bounds.
    if (this->Bounds[0] == this->Bounds[1])
      {
      this->Bounds[0] = this->Bounds[2];
      this->Bounds[1] = this->Bounds[3];
      this->Bounds[2] = this->Bounds[4];
      this->Bounds[3] = this->Bounds[5];
      }
    else if (this->Bounds[2] == this->Bounds[3])
      {
      this->Bounds[0] = this->Bounds[4];
      this->Bounds[1] = this->Bounds[5];
      this->Bounds[2] = this->Bounds[0];
      this->Bounds[3] = this->Bounds[1];
      }
    else if (this->Bounds[5] == this->Bounds[5])
      {
      // nothing to do.
      }
    // We check for SliceMode only if the input is not already 2D, since slice
    // mode is applicable only for 3D images.
    else if (this->SliceMode == YZ_PLANE)
      {
      this->Bounds[0] = this->Bounds[2];
      this->Bounds[1] = this->Bounds[3];
      this->Bounds[2] = this->Bounds[4];
      this->Bounds[3] = this->Bounds[5];
      }
    else if (this->SliceMode == XZ_PLANE)
      {
      this->Bounds[0] = this->Bounds[4];
      this->Bounds[1] = this->Bounds[5];
      this->Bounds[2] = this->Bounds[0];
      this->Bounds[3] = this->Bounds[1];
      }

    this->Bounds[4] = this->Bounds[5] = 0.0;
    }

  return this->Bounds;
}

//----------------------------------------------------------------------------
void vtkPVImageSliceMapper::ShallowCopy(vtkAbstractMapper* mapper)
{
  vtkPVImageSliceMapper* idmapper = vtkPVImageSliceMapper::SafeDownCast(mapper);
  if (idmapper)
    {
    this->SetInputData(idmapper->GetInput());
    this->SetGhostLevel(idmapper->GetGhostLevel());
    this->SetNumberOfPieces(idmapper->GetNumberOfPieces());
    this->SetNumberOfSubPieces(idmapper->GetNumberOfSubPieces());
    }

  this->Superclass::ShallowCopy(mapper);
}

//----------------------------------------------------------------------------
int vtkPVImageSliceMapper::FillInputPortInformation(
  int vtkNotUsed(port), vtkInformation* info)
{
  info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageData");
  return 1;
}


//----------------------------------------------------------------------------
void vtkPVImageSliceMapper::RenderPiece(vtkRenderer* ren, vtkActor* actor)
{
  vtkImageData* input = this->GetInput();
  //
  // make sure that we've been properly initialized
  //
  if (ren->GetRenderWindow()->CheckAbortStatus())
    {
    return;
    }
  if ( input == NULL )
    {
    vtkErrorMacro(<< "No input!");
    return;
    }
  else
    {
    this->InvokeEvent(vtkCommand::StartEvent,NULL);
    if (!this->Static)
      {
      this->Update();
      }
    this->InvokeEvent(vtkCommand::EndEvent,NULL);

    vtkIdType numPts = input->GetNumberOfPoints();
    if (numPts == 0)
      {
      vtkDebugMacro(<< "No points!");
      return;
      }
    }
  // make sure our window is current
  ren->GetRenderWindow()->MakeCurrent();
  this->TimeToDraw = 0.0;
#ifdef VTKGL2
  this->RenderInternal(ren, actor);
#else
  if (this->Painter)
    {
    // Update Painter information if obsolete.
    if (this->PainterInformationUpdateTime < this->GetMTime())
      {
      this->UpdatePainterInformation();
      this->PainterInformationUpdateTime.Modified();
      }
    // Pass polydata if changed.
    if (this->Painter->GetInput() != input)
      {
      this->Painter->SetInput(input);
      }
    this->Painter->Render(ren, actor, 0xff,this->ForceCompileOnly==1);
    this->TimeToDraw = this->Painter->GetTimeToDraw();
    }
#endif

  // If the timer is not accurate enough, set it to a small
  // time so that it is not zero
  if ( this->TimeToDraw == 0.0 )
    {
    this->TimeToDraw = 0.0001;
    }

  this->UpdateProgress(1.0);
}

//----------------------------------------------------------------------------
void vtkPVImageSliceMapper::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);

  os << indent << "Piece : " << this->Piece << endl;
  os << indent << "NumberOfPieces : " << this->NumberOfPieces << endl;
  os << indent << "GhostLevel: " << this->GhostLevel << endl;
  os << indent << "Number of sub pieces: " << this->NumberOfSubPieces << endl;
}
