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

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

#include "vtkTriangleFilter.h"
#include "vtkCell.h"
#include "vtkCellArray.h"
#include "vtkDataObject.h"
#include "vtkIdList.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkImageData.h"
#include "vtkImageIterator.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkImageStencilData.h"
#include "vtkImageStencil.h"
#include "vtkContourToImageStencil.h"

vtkCxxRevisionMacro(vtkContourStatistics, "$Revision: 1.20 $");
vtkStandardNewMacro(vtkContourStatistics);

vtkCxxSetObjectMacro( vtkContourStatistics, ImageData, vtkImageData );

vtkContourStatistics::vtkContourStatistics()
{
  this->Area                    = 0.0;
  this->Perimeter               = 0.0;
  this->Minimum                 = 0.0;
  this->Maximum                 = 0.0;
  this->StandardDeviation       = 0.0;
  this->LastAreaBuildTime       = 0;
  this->StatisticsBuildTime     = 0;
  this->ContourOrientation      = 0;
  this->NumberOfPixelsInContour = 0;
  this->StandardDeviation       = 0.0;
  this->Minimum                 = 0.0;
  this->Maximum                 = 0.0;
  this->Mean                    = 0.0;
  this->StatisticsComputeFailed = 1;
  this->ImageData               = NULL;
  this->Slice                   = 0;
  this->ObtainSliceFromContourPolyData = 1;
  this->StatisticsComputeFailedHow     = NULL;

  this->SetNumberOfOutputPorts(0);
}

vtkContourStatistics::~vtkContourStatistics()
{
  if (this->ImageData)
    {
    this->ImageData->Delete();
    }
  if (this->StatisticsComputeFailedHow)
    {
    delete [] this->StatisticsComputeFailedHow;
    }
    
}

//----------------------------------------------------------------------------
unsigned long vtkContourStatistics::GetMTime()
{
  unsigned long t1, t2;
  
  t1 = this->Superclass::GetMTime();
  if (this->ImageData)
    {
    t2 = this->ImageData->GetMTime();
    if (t2 > t1)
      {
      t1 = t2;
      }
    }
  return t1;
}
  
//----------------------------------------------------------------------------
// This method measures the area and perimeter of the contour. If the 
// ImageData has been set, we also measure the min/max/mean/stddev statistics
// within the contour.
// 
// Currently, the input is a polydata which is assumed to have at least 1 cell
// of type vtkPolyLine. (the polyline that represents the contour).
//
// This function does the following. 
// 1. Compute the area. This is done by creating triangle strips from the 
//    polydata using vtkTriangleFilter and summing the areas. This portion
//    a copy of vtkMassProperties discarding the several irrelevant computations
// 2. Compute perimeter by calling ComputePerimeter()
// 3. Compute Min/Max/Std/Mean stats by calling ComputeMinMaxStatistics()
// 
int vtkContourStatistics::RequestData(
  vtkInformation* vtkNotUsed( request ),
  vtkInformationVector** inputVector,
  vtkInformationVector* vtkNotUsed( outputVector ))
{
  vtkInformation *inInfo = 
    inputVector[0]->GetInformationObject(0);

  vtkPolyData *input = vtkPolyData::SafeDownCast(  
    inInfo->Get(vtkDataObject::DATA_OBJECT()));
  if (!input)
    {
    return 1;
    }

  // Don't recompute area or perimeter if contour polydata hasn't changed
  unsigned long polydataMTime = input->GetMTime();
  //if ( polydataMTime > this->LastAreaBuildTime )
    {
    // Sanity check
    vtkIdType numCells = input->GetNumberOfCells();
    vtkIdType numPts   = input->GetNumberOfPoints();
    if (numCells < 1 || numPts < 3)
      {
      // Bogus polydata. Set area/perimeter to 0. 
      this->Area      = 0.0;
      this->Perimeter = 0.0;
      vtkWarningMacro(<<"No data to measure.. setting area and perimeter to 0"); 
      return 1;
      }
    
    this->Area = this->ComputeArea( input );
    this->Perimeter = this->ComputePerimeter( input );
    }

  // Area build time is the max of (class's MTime,
  // contour polydataMTime, InputImageData).
  unsigned long t = this->GetMTime();
  unsigned long imageDataMTime = 0;
  if (this->ImageData)
    {
    imageDataMTime = this->ImageData->GetMTime();
    }
  t = (t < polydataMTime)  ? polydataMTime  : t;
  t = (t < imageDataMTime) ? imageDataMTime : t;
  this->LastAreaBuildTime = t;
   
  // 3. Compute min, max, standard deviation
  // If there is no image.. min max, std are whatever was last computed.
  this->ComputeMinMaxStatistics( input );

  this->StatisticsBuildTime = t;
  return 1;
}

//----------------------------------------------------------------------------
// Compute the area. Done by creating triangle strips from the input data and
// summing the areas of the triangle strips. The function is a const function
// although hasn't been declared as one cause VTK largely isn't const correct.
// 
double vtkContourStatistics::ComputeArea( const vtkPolyData *poly ) 
{
  // We will pass the input through a vtkTriangleFilter. And sum the areas
  // of the triangle strips.

  // Generate triangle strips
  vtkPolyData *pd = vtkPolyData::New();
  pd->DeepCopy( const_cast< vtkPolyData * >(poly) );
  pd->SetPolys( pd->GetLines() );
  pd->SetLines( NULL );
  
  vtkTriangleFilter *triangleFilter = vtkTriangleFilter::New();
  triangleFilter->SetInput(pd);
  triangleFilter->Update();
 
  vtkPolyData *trianglePolys = triangleFilter->GetOutput();
  
  vtkIdType cellId, numCells, numPts, numIds;
  double p[3];
  
  // Sum the areas of strips
  numCells = trianglePolys->GetNumberOfCells();
  numPts   = trianglePolys->GetNumberOfPoints();
  
  vtkIdList *ptIds = vtkIdList::New();
  ptIds->Allocate(VTK_CELL_SIZE);
  
  // Traverse all cells, obtaining node coordinates.
  //
  double   area,totalarea;
  double   a,b,c,s;
  double    x[3],y[3],z[3];
  double    i[3],j[3],k[3];
  double    ii[3],jj[3],kk[3];
  vtkIdType      idx;

  // Initialize variables ...
  //
  totalarea = 0.0;

  // Some of the stuff to compute the area has been copied from vtkMassProperties
  // so that the unwanted stuff is not computed....
  for (cellId=0; cellId < numCells; cellId++)
    {
    if ( trianglePolys->GetCellType(cellId) != VTK_TRIANGLE)
      {
      vtkWarningMacro(<<"Input data type must be VTK_TRIANGLE");
      continue;
      }
    trianglePolys->GetCellPoints(cellId,ptIds);
    numIds = ptIds->GetNumberOfIds();

    // store current vertix (x,y,z) coordinates ...
    //
    for (idx=0; idx < numIds; idx++)
      {
      trianglePolys->GetPoint(ptIds->GetId(idx), p);
      x[idx] = p[0]; y[idx] = p[1]; z[idx] = p[2];
      }

    // get i j k vectors ... 
    i[0] = ( x[1] - x[0]); j[0] = (y[1] - y[0]); k[0] = (z[1] - z[0]);
    i[1] = ( x[2] - x[0]); j[1] = (y[2] - y[0]); k[1] = (z[2] - z[0]);
    i[2] = ( x[2] - x[1]); j[2] = (y[2] - y[1]); k[2] = (z[2] - z[1]);

    // This is reduced to ...
    ii[0] = i[0] * i[0]; ii[1] = i[1] * i[1]; ii[2] = i[2] * i[2];
    jj[0] = j[0] * j[0]; jj[1] = j[1] * j[1]; jj[2] = j[2] * j[2];
    kk[0] = k[0] * k[0]; kk[1] = k[1] * k[1]; kk[2] = k[2] * k[2];

    // area of a triangle...
    a = sqrt(ii[1] + jj[1] + kk[1]);
    b = sqrt(ii[0] + jj[0] + kk[0]);
    c = sqrt(ii[2] + jj[2] + kk[2]);
    s = 0.5 * (a + b + c);
    area = sqrt( fabs(s*(s-a)*(s-b)*(s-c)));
    totalarea += area;
    }

  ptIds->Delete();
  pd->Delete();
  triangleFilter->Delete();

  return totalarea;
}

  
//----------------------------------------------------------------------------
// Compute the perimeter by summing the L2 norms of each line segment in the
// vtkPolyLine
// 
double vtkContourStatistics::ComputePerimeter( const vtkPolyData *poly ) const
{
  // Computes the perimeter by summing the length of each line in
  // the polydata.
  
  vtkPolyData *pd = const_cast< vtkPolyData * >( poly );
  double perimeter=0.0;
  vtkCellArray *lines = pd->GetLines();

  const vtkIdType ncells = pd->GetNumberOfCells(); 
  // Usually = 1 for the one and only contour, but if there is
  // more than 1, we will add the perimeters for all the cells.
   
  vtkIdType npts;
  vtkIdType *pts;
  lines->InitTraversal();

  // For each cell, usually just 1.
  for ( int i=0; i< ncells; i++)
    {
    lines->GetNextCell(npts,pts); 
    
    // Get the points in each line segment of the cell 
    // (polyline in the case of a contour)
    
    double p1[3], p2[3], p3[3];
    pd->GetPoint( pts[0], p3 );
    for ( int j=1; j < npts; j++ )
      {
      pd->GetPoint( pts[j-1], p1 );
      pd->GetPoint( pts[j], p2 );
      perimeter += sqrt((p2[0]-p1[0])*(p2[0]-p1[0]) 
                      + (p2[1]-p1[1])*(p2[1]-p1[1])
                      + (p2[2]-p1[2])*(p2[2]-p1[2]));
      }
    perimeter += sqrt((p2[0]-p3[0])*(p2[0]-p3[0]) 
                    + (p2[1]-p3[1])*(p2[1]-p3[1])
                    + (p2[2]-p3[2])*(p2[2]-p3[2]));
    }
  return perimeter;
}


//----------------------------------------------------------------------------
// Takes the portion of the contour image data within the contour bounds 
// and re-orients it along the XY plane.
// 
template< class T >
int vtkReorientContourRegionImageData( vtkContourStatistics *self,
      int contourRegion[6], vtkImageData* reorientedData, T*) 
{
  int extent[6];
  reorientedData->GetExtent( extent );

  // Sanity check 
  if ( contourRegion[1] < contourRegion[0] ||
       contourRegion[3] < contourRegion[2] || 
       contourRegion[5] < contourRegion[4] ||
       extent[1]        < extent[0] ||
       extent[3]        < extent[2] || 
       extent[5]        < extent[4])
    {
    return 1;
    } 
  
  vtkImageIterator< T > it(self->GetImageData(), contourRegion);
  vtkImageIterator< double > oit(reorientedData, extent);

  // Compute increments based on contour orientation
  if( self->GetContourOrientation() == 2 )
    {
    // Contour perpendicular to Z axis
    while( !it.IsAtEnd() )
      { 
      T *inSI = it.BeginSpan();  
      T *inSIEnd = it.EndSpan();
      double *outSI = oit.BeginSpan();
      while (inSI != inSIEnd) 
        {
        *outSI = static_cast< double >(*inSI);
        ++inSI;
        ++outSI;
        }
      it.NextSpan();
      oit.NextSpan();
      }
    }
  
  else if( self->GetContourOrientation() == 1 )
    {
    // Contour perpendicular to Y axis
    int width = extent[1] - extent[0] + 1;
    unsigned int ctr = static_cast< unsigned  int >(width);
    double *outSI = oit.BeginSpan();
    double *outSIEnd = oit.EndSpan();
    while( !it.IsAtEnd() )
      { 
      T *inSI = it.BeginSpan();  
      T *inSIEnd = it.EndSpan();
      while (inSI != inSIEnd) 
        {
        *outSI = static_cast< double >(*inSI);
        ++inSI;
        ++outSI;
        --ctr;
        }
      it.NextSpan();
      if (ctr == 0)
        {
        oit.NextSpan();
        outSIEnd = oit.EndSpan();
        ctr=width;
        }
      }
    }
  
  else if( self->GetContourOrientation() == 0 )
    {
    // Sanity check
    if (contourRegion[2] != extent[0] ||
        contourRegion[3] != extent[1] ||
        contourRegion[4] != extent[2] ||
        contourRegion[5] != extent[3])
      {
      vtkGenericWarningMacro( << "Contour extents and allocated image extents "
        << "do not match. Cannot re-orient and copy data into allocated image.");
      return 1;
      }
    
    // Contour perpendicular to X axis, on sagittal plane
    int width = extent[1] - extent[0] + 1;
    unsigned int ctr = static_cast< unsigned  int >(width);
    double *outSI = oit.BeginSpan();
    double *outSIEnd = oit.EndSpan();
    while( !it.IsAtEnd() )
      { 
      T *inSI = it.BeginSpan();  
      T *inSIEnd = it.EndSpan();
      while (inSI != inSIEnd) 
        {
        *outSI = static_cast< double >(*inSI);
        ++inSI;
        ++outSI;
        }
      it.NextSpan();
      --ctr;
      if (ctr == 0)
        {
        oit.NextSpan();
        outSIEnd = oit.EndSpan();
        ctr=width;
        }
      }
    }

  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
//    
int vtkContourStatistics::ReorientPolyline( const vtkPolyData *poly, 
                                                  vtkPolyData *polyOut )
{  
  vtkPolyData *polyIn = const_cast< vtkPolyData * >(poly);

  int numberOfContourPoints = polyIn->GetNumberOfPoints();
  if (numberOfContourPoints < 3) // Less than 3 points.. sanity check failed
    {
    this->StatisticsComputeFailed = 1;
    this->SetStatisticsComputeFailedHow("Less than 3 contour points");
    return 1;
    }
  
  // Contour is already along the axial plane.. just copy the old contour, 
  if( this->ContourOrientation == 2 && this->ObtainSliceFromContourPolyData)
    {
    polyOut->DeepCopy( polyIn );
    return 0;
    }
  
  double x[3];
  
  // Get info from the image data
  double spacing[3], origin[3];
  int extent[6];
  this->ImageData->GetSpacing( spacing );
  this->ImageData->GetOrigin(  origin  );
  this->ImageData->GetExtent(  extent );
  
  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());

  // Contour is already along the axial plane. If the  
  // the statistics are to be evaluated on a slice different from the one
  // contour is on. In that case, we will shift the contour to that new slice.
  if( this->ContourOrientation == 2 )
    {
    double z = (spacing[2] * ((double)(this->Slice - extent[4]))) + origin[2];
      
    for (vtkIdType i=0; i<numberOfContourPoints; i++)
      {
      pointsIn->GetPoint( i, x );
      points->InsertPoint(i, x[0], x[1], z);
      lineIndices[i] = i;
      }
    }
 
  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
    if (this->ObtainSliceFromContourPolyData)
      {
      for (vtkIdType i=0; i<numberOfContourPoints; i++)
        {
        pointsIn->GetPoint( i, x );
        points->InsertPoint(i, x[1], x[2], x[0]);
        lineIndices[i] = i;
        }
      }
    else
      {
      // Use the Z value from the input slice
      double z = (spacing[0] * ((double)(this->Slice - extent[0]))) + origin[0];
      for (vtkIdType i=0; i<numberOfContourPoints; i++)
        {
        pointsIn->GetPoint( i, x );
        points->InsertPoint(i, x[1], x[2], z);
        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
    if (this->ObtainSliceFromContourPolyData)
      {
      for (vtkIdType i=0; i<numberOfContourPoints; i++)
        {
        pointsIn->GetPoint( i, x );
        points->InsertPoint(i, x[0], x[2], x[1]);
        lineIndices[i] = i;
        }
      }
    else
      {
      // Use the Z value from the input slice
      double z = (spacing[1] * ((double)(this->Slice - extent[2]))) + origin[1];
      for (vtkIdType i=0; i<numberOfContourPoints; i++)
        {
        pointsIn->GetPoint( i, x );
        points->InsertPoint(i, x[0], x[2], z);
        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;
}


//----------------------------------------------------------------------------
// An alternative implementation that is based on rasterizing. 
// The polydata argument must contain one closed cell, that must be a 
// vtkPolyLine. 
// 
// This function does the following. 
// 
// 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. Extract the portion of image data that lies in the rectangular bounding
//    box of the contour. Re-orient this image data. This is done by 
//    vtkReorientContourRegionImageData. The resulting image data is one slice
//    thick and lies on the XY plane and has the same extents as the contour.
// 4. Use vtkContourToImageStencil to rasterize the contour and produce a 
//    vtkImageStencilData. This is the stencil mask (that contains tuples 
//    defining the extents of the contour along each scan line).
// 5. The function ComputeStatisticsWithinStencil() computes statistics within
//    using the stencil and the reoriented image data.
//    
void vtkContourStatistics::ComputeMinMaxStatistics( const vtkPolyData *poly )
{
  // If there is no imagedata or if data is multicomponent, fail.
  // Note that the area/perimeter is still valid.. the StatisticsComputeFailed
  // flag is the flag of the "Statistics" within the contour ie the min/max
  // etc..
  if( !this->ImageData )
    {
    this->SetStatisticsComputeFailedHow("No image data");
    this->StatisticsComputeFailed = 1;
    }
    
  if (this->ImageData->GetNumberOfScalarComponents() != 1) 
    {
    this->SetStatisticsComputeFailedHow("More than 1 component in the image");
    this->StatisticsComputeFailed = 1;
    return;
    }
  
  // 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 * >(poly);
  const vtkIdType ncells = polyIn->GetNumberOfCells(); 
  if ( ncells < 1 )
    { // Sanity check failed
    return;
    }

  // The ContourOrientation ivar indicates the axis aligned plane the contour
  // is on.
  double contourBounds[6];
  polyIn->GetCell(0)->GetBounds(contourBounds);
  if ( contourBounds[0]==contourBounds[1] )
    {
    this->ContourOrientation = 0;
    }
  else if ( contourBounds[2]==contourBounds[3] )
    {
    this->ContourOrientation = 1;
    }
  else if ( contourBounds[5]==contourBounds[4] )
    {
    this->ContourOrientation = 2;
    }
  else 
    {
    // Not supported 
    this->ContourOrientation = 3;
    this->SetStatisticsComputeFailedHow(
      "Contour not parallel to the axis planes. Check the vtkPolyData bounds");
    this->StatisticsComputeFailed = 1;
    return;
    }
  
  // Get bounding box region of the contour
  double spacing[3], origin[3];
  int extent[6], contourRegion[6], contourXYRegion[6];
  this->ImageData->GetSpacing( spacing );
  this->ImageData->GetOrigin(  origin  );
  this->ImageData->GetExtent( extent );
  this->ImageData->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];
    }

  // If the contour statistics are to be evaluated on a different slice than 
  // the one the contour is on...
  if (!this->ObtainSliceFromContourPolyData)
    {
    // Ensure that the slice is within the extents.
    if (this->Slice > extent[2 * this->ContourOrientation + 1] ||
        this->Slice < extent[2 * this->ContourOrientation])
      {
      this->SetStatisticsComputeFailedHow(
        "Slice set is not within the extents of the image data.");
      this->StatisticsComputeFailed = 1;
      return;
      }

    contourRegion[2 * this->ContourOrientation]     = this->Slice;
    contourRegion[2 * this->ContourOrientation + 1] = this->Slice;
    }
 
  // Re-map the data in the contour bounding box into a 2D image.
  // A vtkImageData with 1 slice thickness.
  vtkImageData *contourRegionImageData = 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(1);
  contourRegionImageData->AllocateScalars();
  
  vtkPolyData *pd = vtkPolyData::New();
  
  // Here we will attempt to re-orient the contour polydata, poly along the axial
  // plane and store the result in pd.
  if (this->ReorientPolyline(polyIn, pd) == 1)
    {
    this->StatisticsComputeFailed = 1;
    return;
    }
    
  int failed;
  switch (this->ImageData->GetScalarType())
    {
    vtkTemplateMacro( failed = 
      vtkReorientContourRegionImageData( 
        this, contourRegion, 
        contourRegionImageData ,static_cast< VTK_TT * >(0)));
    default:
      vtkErrorMacro(<< "vtkContourStatistics: Unknown ScalarType");
      return;
    }

  if (failed)
    {
    this->SetStatisticsComputeFailedHow(
       "vtkReorientContourRegionImageData failed for some reason"); 
    this->StatisticsComputeFailed = 1;
    contourRegionImageData->Delete();
    pd->Delete();
    return;
    }

  vtkContourToImageStencil *contourToImageStencil = vtkContourToImageStencil::New();
  contourToImageStencil->SetInput( pd );
  contourToImageStencil->SetSpacing( contourRegionImageData->GetSpacing() );
  contourToImageStencil->SetOrigin( contourRegionImageData->GetOrigin() );
  contourToImageStencil->Update();
  vtkImageStencilData *stencilData = contourToImageStencil->GetOutput();
  
  // Iterate over the contourRegionImageData and glean statistics. 
  // contourRegionImageData contains the region of the image within the contour
  // bounding box, aligned along the XY plane. maskStencil is a 
  // vtkImageStencilData that tells us the contour boundaries.
  this->StatisticsComputeFailed  = 
    this->ComputeStatisticsWithinStencil( contourRegionImageData, stencilData);
  
  // Delete created objects
  contourToImageStencil->Delete();
  contourRegionImageData->Delete();
  pd->Delete();
}

//----------------------------------------------------------------------------
// Iterate over the image and glean statistics if it is within the stencil
// The function returns 1 on failure. The image and the stencil are assumed
// to be 1 slice thick and assumed to lie in the XY plane. 
//
int vtkContourStatistics::ComputeStatisticsWithinStencil( 
         vtkImageData *image, vtkImageStencilData *stencil)
{
  int extent[6];
  image->GetExtent( extent );

  // Sanity check 
  if ( extent[1]        < extent[0] ||
       extent[3]        < extent[2] || 
       extent[5]       != extent[4])
    {
    this->SetStatisticsComputeFailedHow(
      "Contour polydata found to be thicker than 1 slice.");
    this->StatisticsComputeFailed = 1;
    return 1;
    } 
  
  
  double min = VTK_DOUBLE_MAX; 
  double max = VTK_DOUBLE_MIN;
  unsigned int numPointsInContour = 0;
  double sum = 0.0;
  double sumSquares = 0.0;
  int z = extent[4];
  
  // Run through each scan line (or span as the iterators refer to it) and 
  // check if the pixel lies within the stencil extents (the stencil stores
  // a bunch of tuples for each scan line that indicate the begin and end
  // co-ordinates for the contour on this scan line).
  vtkIdType increments[3];
  image->GetIncrements( increments );
  
  int iter = 0;

  // For each scan line
  for (int y=extent[2]; y <= extent[3]; y++, iter = 0)
    {
    int r1,r2;
    int moreSubExtents = 1;
    while( moreSubExtents )
      {
      moreSubExtents = stencil->GetNextExtent( r1, r2, extent[0], extent[1], y, z, iter);

      // sanity check
      if (r1 <= r2 ) 
        { 
        double *beginExtent = (double *)(image->GetScalarPointer( r1, y, z ));
        double *endExtent   = (double *)(image->GetScalarPointer( r2, y, z ));

        while (beginExtent <= endExtent)
          {
          double value = *beginExtent;
#if 0 
          if( value ) std::cout << "1"; else std::cout << "0"; 
#endif
          sum += value;
          sumSquares += value * value; 
          ++numPointsInContour;
          if (value < min )
            {
            min = value;
            }
          if (value > max )
            {
            max = value;
            }
          
          beginExtent += increments[0];
          } // end for each data value within the extent tuple
        } // end make sure extents are ok
      } // end for each extent tuple
#if 0
      std::cout << std::endl;
#endif
    } // end for each scan line
    
  if ( numPointsInContour == 0 ) 
    // There are no pixels in the contour. Maybe we should 
    // interpolate and create the min/max, but just fill with zeroes for now
    {
    this->Mean              = 0.0;
    this->Maximum           = 0.0; 
    this->Minimum           = 0.0;
    this->StandardDeviation = 0.0;
    }
  else
    {
    this->Mean = sum / numPointsInContour;
    this->StandardDeviation = sqrt(((double)(sumSquares) 
                - ((double)sum*sum/(double)numPointsInContour)) /  (double)numPointsInContour);
    this->Maximum  = max;
    this->Minimum  = min;
    }
 
  this->NumberOfPixelsInContour = numPointsInContour;
  if (this->NumberOfPixelsInContour == 0)
    {
    this->StatisticsComputeFailed = 1;
    this->SetStatisticsComputeFailedHow("Zero pixels lie in the contour.");
    return 1;
    }
    
  return 0;
}

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

  vtkPolyData *input = vtkPolyData::SafeDownCast(this->GetInput(0));
  if (!input) 
    {
    return;
    }
  os << indent << "Area             : " << this->GetArea () << endl;
  os << indent << "Perimeter        : " << this->GetPerimeter () << endl;
  if ( this->ImageData )
    {
    os << indent << "ImageData:" << endl;
    this->ImageData->PrintSelf(os,indent.GetNextIndent());
    os << indent << "Mean             : " << this->Mean << endl;
    os << indent << "Max              : " << this->Maximum << endl;
    os << indent << "Min              : " << this->Minimum << endl;
    os << indent << "StdDev           : " << this->StandardDeviation << endl;
    os << indent << "NumPixelInContour: " << this->NumberOfPixelsInContour << endl;
    }
  else 
    {
    os << indent << "ImageData: (None)" << endl;
    }
  os << indent << "Contour is perpendicular to (0=X, 1=Y, 2=Z):" 
                              << this->ContourOrientation << endl;
  os << indent << "ObtainSliceFromContourPolyData: " << 
          (this->ObtainSliceFromContourPolyData ? "On\n":"Off\n");
  if (!this->ObtainSliceFromContourPolyData)
    {
    os << indent << "Slice on which to compute contour statistics: " 
                                                << this->Slice << endl;
    }
  os << indent << "Statistics computation failed or statistics not computed yet: " 
    << this->StatisticsComputeFailed << endl;
  if (this->StatisticsComputeFailed && this->StatisticsComputeFailedHow)
    {
    os << indent << "Statistics computation failed the last time it was computed "
                 << "because: " << this->StatisticsComputeFailedHow << endl;
    }
    
  os << indent << "StatisticsBuildTime: " << this->StatisticsBuildTime << endl;
  os << indent << "LastAreaBuildTime: " << this->LastAreaBuildTime << endl;
}

