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

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

#include "vtkCell.h"
#include "vtkCellArray.h"
#include "vtkDataObject.h"
#include "vtkIdList.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkPolyData.h"
#include "vtkContourToImageStencil.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkImageData.h"
#include "vtkImageStencilData.h"
#include "vtkStencilProjectionImageFilter.h"
#include "vtkMatrix4x4.h"
#include "vtkMatrixToLinearTransform.h"
#include "vtkTransformPolyDataFilter.h"
#include "vtkPoints.h"
#include "vtkCamera.h"
#include "vtkRenderer.h"
#include "vtkInteractorObserver.h"
#include "vtkCommand.h"
#include "vtkEventForwarderCommand.h"
#include <algorithm>

#ifdef VTK_VV_CONTOUR_SEGMENTATION_DEBUG
#include "vtkPNGWriter.h"
#endif

vtkCxxRevisionMacro(vtkContourSegmentationFilter, "$Revision: 1.18 $");
vtkStandardNewMacro(vtkContourSegmentationFilter);
vtkCxxSetObjectMacro(vtkContourSegmentationFilter, StencilAxes, vtkMatrix4x4);
vtkCxxSetObjectMacro(vtkContourSegmentationFilter, CompositeProjectionTransformMatrix, vtkMatrix4x4);
vtkCxxSetObjectMacro(vtkContourSegmentationFilter, Camera, vtkCamera);
vtkCxxSetObjectMacro(vtkContourSegmentationFilter, Renderer, vtkRenderer);

vtkContourSegmentationFilter::vtkContourSegmentationFilter()
{
  this->StencilAxes                          = NULL;
  this->NumberOfPixelsReplaced               = 0;
  this->ReplaceValue                         = 0;
  this->SegmentInside                        = 1;
  this->LastBuildTime                        = 0;
  this->ObtainOrientationFromContourPolyData = 1;
  this->SegmentationExtent[0] = this->SegmentationExtent[1] = this->SegmentationExtent[2] 
    = this->SegmentationExtent[3] = this->SegmentationExtent[4] = this->SegmentationExtent[5]
    = 0;
  this->VolumetricProjection = 0;
  this->CompositeProjectionTransformMatrix = NULL;
  this->Camera = NULL;
  this->Renderer = NULL;

  this->SetNumberOfInputPorts(3);
}

//----------------------------------------------------------------------------
vtkContourSegmentationFilter::~vtkContourSegmentationFilter()
{
  this->SetStencilAxes(NULL);
  this->SetCompositeProjectionTransformMatrix(NULL);
  this->SetCamera(NULL);
  this->SetRenderer(NULL);
}

//----------------------------------------------------------------------------
void vtkContourSegmentationFilter::SetContourPolyData(vtkPolyData *pd)
{
  this->SetInput(1, pd);
}

//----------------------------------------------------------------------------
vtkPolyData *vtkContourSegmentationFilter::GetContourPolyData()
{
  if (this->GetNumberOfInputConnections(1) < 1)
    {
    return NULL;
    }
  return vtkPolyData::SafeDownCast(this->GetExecutive()->GetInputData(1,0));
}

//----------------------------------------------------------------------------
void vtkContourSegmentationFilter::SetImage(vtkImageData *image)
{
  this->SetInput(2, image);
}

//----------------------------------------------------------------------------
vtkImageData *vtkContourSegmentationFilter::GetImage()
{
  if (this->GetNumberOfInputConnections(2) < 1)
    {
    if (this->GetNumberOfInputConnections(0) < 1)
      {
      return NULL;
      }
    else
      {
      return vtkImageData::SafeDownCast(this->GetExecutive()->GetInputData(0,0));
      }
    }
  return vtkImageData::SafeDownCast(this->GetExecutive()->GetInputData(2,0));
}

//----------------------------------------------------------------------------
//
int vtkContourSegmentationFilter::RequestData(
  vtkInformation*  request,
  vtkInformationVector** inputVector,
  vtkInformationVector*  outputVector )
{
  vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
  vtkInformation *pdInfo = inputVector[1]->GetInformationObject(0);

  vtkImageData *input = vtkImageData::SafeDownCast(
    inInfo->Get(vtkDataObject::DATA_OBJECT()));
  vtkPolyData *contourPd = vtkPolyData::SafeDownCast(
    pdInfo->Get(vtkDataObject::DATA_OBJECT()));

  if (!input || !contourPd)
    {
    return 1; // no inputs
    }

  // let superclass allocate data
  this->Superclass::RequestData(request, inputVector, outputVector);

  vtkStencilProjectionImageFilter *projectionFilter =
     vtkStencilProjectionImageFilter::New();
  
  vtkSmartPointer< vtkPolyData > contourPolyData = 
    vtkSmartPointer< vtkPolyData >::New();

  // In the case of volumetric projection, get the relevant projection parameters
  if (this->VolumetricProjection)
    {
    if (!this->Renderer)
      {
      vtkErrorMacro( << "Set the renderer in the case of volumetric projection.");
      return 1;
      }

    this->SetCamera( this->Renderer->GetActiveCamera() );
    this->SetCompositeProjectionTransformMatrix( 
        this->Renderer->GetActiveCamera()->GetCompositeProjectionTransformMatrix(
          this->Renderer->GetTiledAspectRatio(),0,1) );

    if (!this->StencilAxes)
      {
      this->StencilAxes = vtkMatrix4x4::New();
      }

    // Compute the direction cosines of the contour plane (assumed to be the
    // focal plane).
    vtkMatrix4x4::Transpose( 
        this->Renderer->GetActiveCamera()->GetViewTransformMatrix(),
        this->StencilAxes );

    double pWorld[4], fp[4], origin[3];
    
    this->Renderer->GetActiveCamera()->GetFocalPoint(fp);
    vtkInteractorObserver::ComputeWorldToDisplay(this->Renderer, 
                                       fp[0], fp[1], fp[2], fp);

    this->GetImage()->GetOrigin(origin);
   
    // What point does the origin of the display co-ordinates map to in world
    // co-ordinates with respect to the world co-ordinate origin ?
    vtkInteractorObserver::ComputeDisplayToWorld(this->Renderer, 
        0.0, 0.0, fp[2], pWorld);
    this->StencilAxes->SetElement(0, 3, pWorld[0] - origin[0]);
    this->StencilAxes->SetElement(1, 3, pWorld[1] - origin[1]);
    this->StencilAxes->SetElement(2, 3, pWorld[2] - origin[2]);

    // Blank out the other side that was a side effect of transposing.
    this->StencilAxes->SetElement(3, 0, 0);
    this->StencilAxes->SetElement(3, 1, 0);
    this->StencilAxes->SetElement(3, 2, 0);

    vtkContourSegmentationFilter::TransformPolyData( this->StencilAxes,
                               contourPd, contourPolyData );    
    }
  else
    {
    contourPolyData->ShallowCopy( contourPd );
    }

  // Allocate image data along the XY plane of the same size as the contour
  // extents
  vtkSmartPointer< vtkImageData > contourRegionImageData =
                  this->AllocateContourRegionImageData( input, contourPolyData );
  if (!contourRegionImageData)
    {
    return 1; // Contour not oriented properly
    }

  // Here we will attempt to re-orient the contour polydata, (poly) along the
  // axial plane and store the result in pd.
  vtkPolyData *pd = vtkPolyData::New();
  if (this->ReorientPolyline(contourPolyData, pd, input) == 1)
    {
    if (pd)
      {
      pd->Delete();
      }
    // ReorientPolyline failed
    return 1;
    }

  // Rasterize the contour, (pd) into an image (contourRegionImageData).
  vtkSmartPointer< vtkImageStencilData > contourStencil =
                this->RasterizePolyline( pd, contourRegionImageData );

  if (!contourStencil)
    {
    if (pd) {   pd->Delete();   }
    // Rasterization failed
    return 1;
    }

#ifdef VTK_VV_CONTOUR_SEGMENTATION_DEBUG
  vtkContourSegmentationFilter::WriteRasterizedContourToPNGFile(
      contourStencil, "Stencil.png" );
#endif

  // Project the stencil onto the imagedata.
  projectionFilter->SetInput(this->GetImage());
  projectionFilter->SetStencil(contourStencil);
  projectionFilter->SetReplaceValue(this->ReplaceValue);
  projectionFilter->SetSegmentInside(this->SegmentInside);
  projectionFilter->SetSegmentationExtent( this->SegmentationExtent );
  projectionFilter->SetVolumetricProjection( this->VolumetricProjection );
  projectionFilter->SetCamera( this->Camera );
  projectionFilter->SetCompositeProjectionTransformMatrix( 
                this->CompositeProjectionTransformMatrix );


  // Set Projection stenicl's direction cosines
  if (this->ObtainOrientationFromContourPolyData)
    {
    vtkMatrix4x4 *matrix = vtkMatrix4x4::New();
    if (this->ContourOrientation==0)
      {
      // Direction cosines for a sagittal plane
      const double m[16] = {0,0,1,0,1,0,0,0,0,1,0,0,0,0,0,1};
      matrix->DeepCopy(m);
      }
    else if (this->ContourOrientation==1)
      {
      // Direction cosines for coronal plane
      const double m[16] = {1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1};
      matrix->DeepCopy(m);
      }
    else if (this->ContourOrientation==2)
      {
      // Direction cosines for axial plane
      matrix->Identity();
      }
    projectionFilter->SetStencilAxes(matrix);
    matrix->Delete();
    }
  else
    {
    // User specified direction cosines.
    projectionFilter->SetStencilAxes( this->GetStencilAxes() );
    }

  
  // The bulk of the time segmenting is taken up by the projection filter. 
  // Here, we will forward progress events from the projectionFilter 
  // to this class, as if this class were invoking it.
  vtkEventForwarderCommand * event_forwarder 
                      = vtkEventForwarderCommand::New();
  event_forwarder->SetTarget(this);
  projectionFilter->AddObserver( 
      vtkCommand::StartEvent,    event_forwarder );
  projectionFilter->AddObserver( 
      vtkCommand::ProgressEvent, event_forwarder );
  projectionFilter->AddObserver( 
      vtkCommand::EndEvent,      event_forwarder );

  
  // Here is where segmentation spends the bulk of its time..
  projectionFilter->Update();

  projectionFilter->RemoveObserver( event_forwarder );
  event_forwarder->Delete();
  
  this->NumberOfPixelsReplaced = projectionFilter->GetNumberOfPixelsReplaced();
  projectionFilter->Delete();

  if (pd)
    {
    pd->Delete();
    }

  return 1;
}

//----------------------------------------------------------------------------
int vtkContourSegmentationFilter::FillInputPortInformation(int port, 
                                                  vtkInformation *info)
{
  if (port == 0)
    {
    info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageData");
    return 1;
    }
  else if (port == 1)
    {
    info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData");
    return 1;
    }
  else if (port == 2)
    {
    info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageData");
    return 1;
    }
  return 0;
}

//----------------------------------------------------------------------------
// Reorient the polyline into an equivalent one that lies along the XY plane
//
// This function does the following.
//
// 1. It takes an input polydata, that is assumed to consist of a closed
//    polyline oriented along one of the axis aligned planes. It re-orients
//    it into an equivalent one that lies along the XY plane with the same
//    meta-data (spacing, origin) as the original polydata. In other words
//    the (x,y,z) values of the contour points are swapped appropriately.
// 2. If the ObtainSliceFromContourPolyData is off, the polydata is also
//    points along that axis are shifted appropriately so that the contour, if
//    drawn would lie on that slice. Remember that the contour points are
//    specified in physical co-ordinates and the 'slice number' is specified
//    in "extent co-ordinates". It is the responsibility of the calling
//    function to ensure that the slice lies within the extents along that axis
//
//  (Meta-information such as spacing etc is obtained from the image that the
//   contour lies on).
//
int vtkContourSegmentationFilter::ReorientPolyline( const vtkPolyData *poly,
                           vtkPolyData *polyOut, const vtkImageData *)
{
  vtkPolyData *polyIn   = const_cast< vtkPolyData * >(poly);
  int numberOfContourPoints = polyIn->GetNumberOfPoints();
  if (numberOfContourPoints < 3) // Less than 3 points.. sanity check failed
    {
    return 1;
    }

  // Contour is already along the axial plane.. just copy the old contour
  if (this->ContourOrientation == 2 || !this->ObtainOrientationFromContourPolyData)
    {
    polyOut->DeepCopy( polyIn );
    return 0;
    }

  double x[3];

  vtkPoints *points   = vtkPoints::New();
  vtkCellArray *lines = vtkCellArray::New();
  points->SetNumberOfPoints(numberOfContourPoints);

  // lines = numberOfpoints + 1 for closed loops (assumption that our polyline
  // is closed)
  vtkIdType *lineIndices = new vtkIdType[numberOfContourPoints + 1];
  vtkPoints *pointsIn = const_cast< vtkPoints * >(polyIn->GetPoints());

  if (this->ContourOrientation == 0)
    {
    // Contour is perpendicular to X.. (Drawn on sagittal plane)
    // Transform, so that Along X, you have inreasing Y
    //                    Along Y there is inreasing Z
    for (vtkIdType i=0; i<numberOfContourPoints; i++)
      {
      pointsIn->GetPoint( i, x );
      points->InsertPoint(i, x[1], x[2], x[0]);
      lineIndices[i] = i;
      }
    }

  else if (this->ContourOrientation == 1)
    {
    // Contour is perpendicular to Y.. (Drawn on coronal plane)
    // Transform, so that Along X there is inreasing X
    //                    Along Y there is increasing Z
    for (vtkIdType i=0; i<numberOfContourPoints; i++)
      {
      pointsIn->GetPoint( i, x );
      points->InsertPoint(i, x[0], x[2], x[1]);
      lineIndices[i] = i;
      }
    }

  lineIndices[numberOfContourPoints] = 0; // to close the loop
  lines->InsertNextCell( numberOfContourPoints + 1, lineIndices );
  delete [] lineIndices;
  polyOut->SetPoints(points);
  polyOut->SetLines( lines );
  points->Delete();
  lines->Delete();
  return 0;
}

//----------------------------------------------------------------------------
// The goal of the function is to take a polyline that lies on the XY plane
// and return an ImageStencilData (smart pointer) that contains the extents
// of the rasterized output.
//
vtkSmartPointer< vtkImageStencilData >
vtkContourSegmentationFilter::RasterizePolyline(
                  vtkPolyData *poly,  vtkImageData *contourRegionImageData)
{
  vtkContourToImageStencil *contourToImageStencil = vtkContourToImageStencil::New();
  contourToImageStencil->SetInput( poly );
  contourToImageStencil->SetSpacing( contourRegionImageData->GetSpacing() );
  contourToImageStencil->SetOrigin( contourRegionImageData->GetOrigin() );
  contourRegionImageData->ReleaseDataFlagOn();
  contourToImageStencil->Update();
  vtkSmartPointer< vtkImageStencilData > contourStencil = contourToImageStencil->GetOutput();
  contourToImageStencil->Delete();
  return contourStencil;
}

//----------------------------------------------------------------------------
// The polydata argument must contain one closed cell, that must be a
// vtkPolyLine. It can lie on one of the axis aligned planes.
//
// This function does the following.
//
// 0. A bunch of sanity checks.
// 1. It first determines the plane that the contour lies on. For now it is
//    assumed that it lies on one of the axis aligned planes.
// 2. Re-orient the polydata into an equivalent one that lies along the XY
//    plane. This is done by the function ReorientPolyline()
// 3. Allocate image data of the same size as in the rectangular bounding
//    box of the contour lying along the XY plane. The resulting image data is
//    one slice thick and lies on the XY plane and has the same extents as
//    the contour.
//
vtkSmartPointer< vtkImageData >
vtkContourSegmentationFilter::AllocateContourRegionImageData(
        vtkImageData *inputImageData, const vtkPolyData *contourPolyData)
{
  // If there is no imagedata or if data is multicomponent, fail.
  // Note that the area/perimeter is still valid.. the SegmentationFailed
  // flag is the flag of the "Statistics" within the contour ie the min/max
  // etc..
  if( !inputImageData )
    {
    return NULL;
    }

  // Copy the polygon.. We need to do this, because we will re-orient the
  // polydata to the axial plane.. This is done because its more efficient to
  // store stencils in the axial plane. (A stencil stores the xmin,xmax tuple
  // for each scan line, the scan line being along the x axis).
  //
  // We will appropriately extract the slice of the data that lies along the
  // contour and re-orient that along the axial plane as well.

  vtkPolyData *polyIn = const_cast< vtkPolyData * >(contourPolyData);
  const vtkIdType ncells = polyIn->GetNumberOfCells();
  if ( ncells < 1 )
    { // Sanity check failed
    return NULL;
    }

  // The ContourOrientation ivar indicates the axis aligned plane the contour
  // is on. Infer contour orientation from contour bounds. This happens only
  // if it is perpendicular to one of the axes. If not, please specify the
  // direction cosines via SetStencilAxes.
  double contourBounds[6];
  polyIn->GetCell(0)->GetBounds(contourBounds);
  const double tolerance = 0.001;
  if ( fabs(contourBounds[0] - contourBounds[1]) < tolerance )
    {
    this->ContourOrientation = 0;
    }
  else if ( fabs(contourBounds[2] - contourBounds[3]) < tolerance )
    {
    this->ContourOrientation = 1;
    }
  else if ( fabs(contourBounds[5] - contourBounds[4]) < tolerance )
    {
    this->ContourOrientation = 2;
    }
  else
    {
    // Not supported
    this->ContourOrientation = 3;
    return NULL;
    }

  // Get bounding box region of the contour
  double spacing[3], origin[3];
  int extent[6], contourRegion[6], contourXYRegion[6];
  inputImageData->GetSpacing( spacing );
  inputImageData->GetOrigin(  origin  );
  inputImageData->GetExtent( extent );
  inputImageData->GetExtent( contourRegion );

  // Compute the bounding box region of the contour and clip the contour
  // region bounds with the image extents
  for (int i=0; i < 3; i++ )
    {
    if (i == this->ContourOrientation)
      {
      // If the contour lies on a plane that is in between two planes, (a
      // partial volume region), assume that the contour lies on the
      // closest of the two planes.
      contourRegion[2*i] = (int)(((contourBounds[2*i] - origin[i])/spacing[i]) + 0.5); // floor
      contourRegion[2*i+1] = (int)(((contourBounds[2*i+1] - origin[i])/spacing[i]) + 0.5);
      }
    else
      {
      // In case the contour lies in a partial volume region consider the region
      // that encloses the contour.
      contourRegion[2*i] = (int)((contourBounds[2*i] - origin[i])/spacing[i]); // floor
      contourRegion[2*i+1] = (int)(ceil((contourBounds[2*i+1] - origin[i])/spacing[i]));
      }
    //contourRegion[2*i] = (contourRegion[2*i] < extent[2*i])
    //                              ? extent[2*i] : contourRegion[2*i];
    //contourRegion[2*i+1] = (contourRegion[2*i+1] > extent[2*i+1])
    //                              ? extent[2*i+1] : contourRegion[2*i+1];
    }

  //std::cout << "ContourRegion: [" 
  //  << contourRegion[0] << "," << contourRegion[1] << ","
  //  << contourRegion[2] << "," << contourRegion[3] << ","
  //  << contourRegion[4] << "," << contourRegion[5] << "]" << std::endl;
  //std::cout << "ImageExtent: [" 
  //  << extent[0] << "," << extent[1] << ","
  //  << extent[2] << "," << extent[3] << ","
  //  << extent[4] << "," << extent[5] << "]" << std::endl;

  // Re-map the data in the contour bounding box into a 2D image.
  // A vtkImageData with 1 slice thickness.
  vtkSmartPointer< vtkImageData > contourRegionImageData =
                      vtkSmartPointer< vtkImageData >::New();

  // Calculate metadata for this image.
  double contourRegionImageDataSpacing[3], contourRegionImageDataOrigin[3];
  if ( this->ContourOrientation == 0 )
    {
    // Contour is perpendicular to X.
    contourXYRegion[0] = contourRegion[2];
    contourXYRegion[1] = contourRegion[3];
    contourXYRegion[2] = contourRegion[4];
    contourXYRegion[3] = contourRegion[5];
    contourXYRegion[4] = contourRegion[0];
    contourXYRegion[5] = contourRegion[1];
    contourRegionImageDataSpacing[0] = spacing[1];
    contourRegionImageDataSpacing[1] = spacing[2];
    contourRegionImageDataSpacing[2] = spacing[0];
    contourRegionImageDataOrigin[0] = origin[1];
    contourRegionImageDataOrigin[1] = origin[2];
    contourRegionImageDataOrigin[2] = origin[0];
    contourRegionImageData->SetSpacing( contourRegionImageDataSpacing );
    contourRegionImageData->SetOrigin( contourRegionImageDataOrigin );
    }
  else if ( this->ContourOrientation == 1 )
    {
    // Contour is perpendicular to Y.
    contourXYRegion[0] = contourRegion[0];
    contourXYRegion[1] = contourRegion[1];
    contourXYRegion[2] = contourRegion[4];
    contourXYRegion[3] = contourRegion[5];
    contourXYRegion[4] = contourRegion[2];
    contourXYRegion[5] = contourRegion[3];
    contourRegionImageDataSpacing[0] = spacing[0];
    contourRegionImageDataSpacing[1] = spacing[2];
    contourRegionImageDataSpacing[2] = spacing[1];
    contourRegionImageDataOrigin[0] = origin[0];
    contourRegionImageDataOrigin[1] = origin[2];
    contourRegionImageDataOrigin[2] = origin[1];
    contourRegionImageData->SetSpacing( contourRegionImageDataSpacing );
    contourRegionImageData->SetOrigin( contourRegionImageDataOrigin );
    }
  else if ( this->ContourOrientation == 2 )
    {
    // Same as contourRegion
    contourXYRegion[0] = contourRegion[0];
    contourXYRegion[1] = contourRegion[1];
    contourXYRegion[2] = contourRegion[2];
    contourXYRegion[3] = contourRegion[3];
    contourXYRegion[4] = contourRegion[4];
    contourXYRegion[5] = contourRegion[5];
    contourRegionImageData->SetSpacing( spacing );
    contourRegionImageData->SetOrigin( origin );
    }

  contourRegionImageData->SetExtent( contourXYRegion );
  contourRegionImageData->SetScalarTypeToDouble();
  contourRegionImageData->SetNumberOfScalarComponents(
            inputImageData->GetNumberOfScalarComponents());
  contourRegionImageData->AllocateScalars();

  return contourRegionImageData;
}

//----------------------------------------------------------------------------
// output polydata = Inv(m) * input polydata
void vtkContourSegmentationFilter::TransformPolyData(
         const vtkMatrix4x4 *m, const vtkPolyData *in, vtkPolyData *out )
{
  vtkPolyData *pd = const_cast< vtkPolyData * >(in);
  vtkMatrixToLinearTransform *linearTransform = vtkMatrixToLinearTransform::New();
  vtkMatrix4x4::Invert( const_cast< vtkMatrix4x4 * >(m), linearTransform->GetMatrix() );
  vtkTransformPolyDataFilter *transformPolyData = vtkTransformPolyDataFilter::New();
  transformPolyData->SetInput( pd );
  transformPolyData->SetTransform( linearTransform );
  transformPolyData->Update();
  out->ShallowCopy(transformPolyData->GetOutput());
  linearTransform->Delete();
  transformPolyData->Delete();
}

//----------------------------------------------------------------------------
void vtkContourSegmentationFilter::SetSegmentationExtentToImageExtent()
{
  if (!this->GetImage())
    {
    vtkErrorMacro( << "Cannot obtain extents from image. Image not set." );
    }

  this->GetImage()->GetExtent( this->SegmentationExtent );
}

#ifdef VTK_VV_CONTOUR_SEGMENTATION_DEBUG
//----------------------------------------------------------------------------
void vtkContourSegmentationFilter::WriteRasterizedContourToPNGFile(
          vtkImageStencilData * stencilData, const char *filename )
{
  int extent[6];
  stencilData->GetExtent( extent );
  extent[5] = extent[4]; // Otherwise we cannot write it out as a PNG!
  vtkImageData *image = vtkImageData::New();
  image->SetExtent( extent );
  image->SetScalarTypeToUnsignedChar();
  image->SetNumberOfScalarComponents(1);
  image->AllocateScalars();

  // Fill image with zeroes

  for (int y=extent[2]; y <= extent[3]; y++)
    {
    unsigned char *ptr = (unsigned char *)(image->GetScalarPointer(
                              extent[0], y, extent[4] ));
    for (int x=extent[0]; x <= extent[1]; x++)
      {
      *ptr = 0;
      ++ptr;
      }
    }

  vtkIdType increments[3];
  image->GetIncrements( increments );

  int iter = 0;
  for (int y=extent[2]; y <= extent[3]; y++, iter = 0)
    {
    int r1,r2;
    int moreSubExtents = 1;
    while( moreSubExtents )
      {
      moreSubExtents = stencilData->GetNextExtent(
        r1, r2, extent[0], extent[1], y, extent[4], iter);

      // sanity check
      if (r1 <= r2 )
        {
        //std::cout << "       " << y << " (" << r1 << " " << r2 << ")" << std::endl;
        unsigned char *beginExtent =
          (unsigned char *)(image->GetScalarPointer( r1, y, extent[4] ));
        unsigned char *endExtent   =
          (unsigned char *)(image->GetScalarPointer( r2, y, extent[4] ));
        while (beginExtent <= endExtent)
          {
          *beginExtent = (unsigned char)(255);
          beginExtent += increments[0];
          }
        }
      } // end for each extent tuple
    } // end for each scan line

  vtkPNGWriter *writer = vtkPNGWriter::New();
  writer->SetInput( image );
  writer->SetFileName(filename);
  writer->Write();
}
#endif

//----------------------------------------------------------------------------
// This method computes the Region of input necessary to generate outRegion.
// The extents of the image and the stencil are allowed to be differnt.
// The stencil for instance typically has extents that are the width and
// height of the contour (in the plane the contour lies in).
//
int vtkContourSegmentationFilter::RequestUpdateExtent(
  vtkInformation * vtkNotUsed(request),
  vtkInformationVector **inputVector,
  vtkInformationVector *)
{
  vtkInformation *inputInfo = inputVector[0]->GetInformationObject(0);
  vtkInformation *imageInfo = inputVector[2]->GetInformationObject(0);
  vtkImageData *input = vtkImageData::SafeDownCast(
    inputInfo->Get(vtkDataObject::DATA_OBJECT())); // Image on which contour is drawn
  vtkImageData *image = this->GetImage();       // Image to segment

  if (!image || !input)
    {
    return 0;
    }

  int inputExtent[6], imageExtent[6];
  image->GetExtent(imageExtent);
  imageInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), imageExtent, 6);
  input->GetExtent(inputExtent);
  inputInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), inputExtent, 6);

  return 1;
}

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

  os << indent << "ReplaceValue:  " << this->ReplaceValue  << endl;
  os << indent << "SegmentInside: " << this->SegmentInside << endl;
  os << indent << "NumberOfPixelsReplaced: " <<
                  this->NumberOfPixelsReplaced << endl;
  os << indent << "ObtainOrientationFromContourPolyData: " <<
                  this->ObtainOrientationFromContourPolyData << endl;
  os << indent << "StencilAxes: " << this->StencilAxes << "\n";
  if (this->StencilAxes)
    {
    this->StencilAxes->PrintSelf(os,indent.GetNextIndent());
    }
  os << indent << "SegmentationExtent:     "
     << this->SegmentationExtent[0] << ", "
     << this->SegmentationExtent[1] << ", " << this->SegmentationExtent[2] << ", " 
     << this->SegmentationExtent[3] << ", " << this->SegmentationExtent[4] << ", " 
     << this->SegmentationExtent[5] << endl;
}


