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

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

#include "vtkDataObject.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkImageData.h"
#include "vtkImageStencilData.h"
#include "vtkImageStencilDataFlip.h"
#include "vtkSmartPointer.h"
#include "vtkMatrix4x4.h"
#include "vtkCamera.h"
#include "vtkMath.h"
#include "vtkCommand.h"

#define min(x,y) ((x<y) ? (x) : (y))
#define max(x,y) ((x>y) ? (x) : (y))

vtkCxxRevisionMacro(vtkStencilProjectionImageFilter, "$Revision: 1.14 $");
vtkStandardNewMacro(vtkStencilProjectionImageFilter);
vtkCxxSetObjectMacro(vtkStencilProjectionImageFilter, StencilAxes, vtkMatrix4x4);
vtkCxxSetObjectMacro(vtkStencilProjectionImageFilter, Camera, vtkCamera);
vtkCxxSetObjectMacro(vtkStencilProjectionImageFilter, 
    CompositeProjectionTransformMatrix, vtkMatrix4x4);

//----------------------------------------------------------------------------
vtkStencilProjectionImageFilter::vtkStencilProjectionImageFilter()
{
  this->Camera                           = NULL;
  this->StencilAxes                      = NULL;
  this->NumberOfPixelsReplaced           = 0;
  this->ReplaceValue                     = 0;
  this->SegmentInside                    = 1;
  this->Stencil                          = NULL;
  this->VolumetricProjection             = 0;
  this->SetSegmentationExtent( 0, 0, 0, 0, 0, 0);
  this->CompositeProjectionTransformMatrix = NULL;
  this->InverseCompositeProjectionTransformMatrix = NULL;

  this->SetNumberOfInputPorts(2);
}

//----------------------------------------------------------------------------
vtkStencilProjectionImageFilter::~vtkStencilProjectionImageFilter()
{
  this->SetStencilAxes(NULL);
  this->SetCamera(NULL);
  this->SetCompositeProjectionTransformMatrix(NULL);
  if (this->InverseCompositeProjectionTransformMatrix)
    {
    this->InverseCompositeProjectionTransformMatrix->Delete();
    }
}

//----------------------------------------------------------------------------
void vtkStencilProjectionImageFilter::SetStencil(vtkImageStencilData *stencil)
{
  this->SetInput(1, stencil);
}

//----------------------------------------------------------------------------
vtkImageStencilData *vtkStencilProjectionImageFilter::GetStencil()
{
  if (this->GetNumberOfInputConnections(1) < 1)
    {
    return NULL;
    }
  return vtkImageStencilData::SafeDownCast(
      this->GetExecutive()->GetInputData(1,0));
}

//----------------------------------------------------------------------------
// Resamples a stencil to new spacing and origin and translates it 
// 
static void ResampleStencil( double t[3], double newSpacing[3], 
    double newOrigin[3], const vtkImageStencilData *stencil_In, 
                                      vtkImageStencilData *stencilOut )
{
  vtkImageStencilData *stencilIn = const_cast< vtkImageStencilData * >(stencil_In);
  
  int extentIn[6], extentOut[6];
  double spacingIn[3], originIn[3];
  stencilIn->GetExtent(extentIn);
  stencilIn->GetSpacing(spacingIn);
  stencilIn->GetOrigin(originIn);
  double boundingBox[6];
  boundingBox[0] = extentIn[0] * spacingIn[0] + originIn[0] + t[0];
  boundingBox[1] = extentIn[1] * spacingIn[0] + originIn[0] + t[0];
  boundingBox[2] = extentIn[2] * spacingIn[1] + originIn[1] + t[1];
  boundingBox[3] = extentIn[3] * spacingIn[1] + originIn[1] + t[1];
  boundingBox[4] = extentIn[4] * spacingIn[2] + originIn[2] + t[2];
  boundingBox[5] = extentIn[5] * spacingIn[2] + originIn[2] + t[2];
  extentOut[0] = (int)((boundingBox[0]-newOrigin[0])/newSpacing[0] + 0.5);
  extentOut[1] = (int)((boundingBox[1]-newOrigin[0])/newSpacing[0] + 0.5);
  extentOut[2] = (int)((boundingBox[2]-newOrigin[1])/newSpacing[1] + 0.5);
  extentOut[3] = (int)((boundingBox[3]-newOrigin[1])/newSpacing[1] + 0.5);
  extentOut[4] = (int)((boundingBox[4]-newOrigin[2])/newSpacing[2] + 0.5);
  extentOut[5] = (int)((boundingBox[5]-newOrigin[2])/newSpacing[2] + 0.5);
  stencilOut->SetExtent(extentOut);
  stencilOut->SetSpacing(newSpacing);
  stencilOut->SetOrigin(newOrigin);
  stencilOut->AllocateExtents();
  
  int r1, r2, r1new, r2new, idynew, idznew, iter = 0;
  for (int idz=extentIn[4]; idz<=extentIn[5]; idz++, iter=0)
    {
    for (int idy = extentIn[2]; idy <= extentIn[3]; idy++, iter=0)
      {
      int moreSubExtents = 1;
      while( moreSubExtents )
        {
        moreSubExtents = stencilIn->GetNextExtent( 
          r1, r2, extentIn[0], extentIn[1], idy, idz, iter);

        if (r1 <= r2 ) // sanity check 
          { 
          r1new  = (int)((((double)r1) * spacingIn[0] 
                + originIn[0] + t[0] - newOrigin[0])/newSpacing[0] + 0.5);
          r2new  = (int)((((double)r2) * spacingIn[0] 
                + originIn[0] + t[0] - newOrigin[0])/newSpacing[0] + 0.5);
          idynew = (int)((((double)idy) * spacingIn[1] 
                + originIn[1] + t[1] - newOrigin[1])/newSpacing[1] + 0.5);
          idznew = (int)((((double)idz) * spacingIn[2] 
                + originIn[2] + t[2] - newOrigin[2])/newSpacing[2] + 0.5);
          stencilOut->InsertNextExtent( r1new, r2new, idynew, idznew );
          }
        }
      }
    }
}

//----------------------------------------------------------------------------
// Below we have 3 specialized functions to handle projection of the stencil 
// along the axis aligned planes. This is faster than projecting along an
// arbitrary direction since the stencil is the same across one of the 
// orthogonal planes. 
// 
// This function projects a stencil lying on the XY plane, normal along Z
// Returns the number of pixels inside the segmentation or  -1 on failure
//
template< class T >
int ParallelProjectStencilAlongXYZ( vtkStencilProjectionImageFilter *self, 
                                                    T replaceValue)
  // Assumed    - stencil normal is along Z  (axial slice)
  //             +x of the stencil is along +x of the image
  //             +y of the stencil is along +y of the image.
  // This means that the stencil spacing should be 
  //  stencilSpacing[0] = imagespacing[0];
  //  stencilSpacing[1] = imagespacing[1];
  //  stencilSpacing[2] = imagespacing[2];
  // 
  // Of course The stencil origin could be shifted.. Add the last column of the
  // StencilAxes matrix to the current stencil origin and update extents.
{
  
  if (self->GetNumberOfInputConnections(0) < 1)
    {
    return 0;
    }
  
  vtkImageData *image = vtkImageData::SafeDownCast(
            self->GetExecutive()->GetInputData(0,0));
  vtkImageStencilData *stencilIn = self->GetStencil();
  
  double imageSpacing[3], imageOrigin[3], translation[3];
  image->GetSpacing(imageSpacing);
  image->GetOrigin(imageOrigin);
  
  vtkMatrix4x4 * stencilAxes = self->GetStencilAxes();
  translation[0] = stencilAxes->GetElement(0,3);
  translation[1] = stencilAxes->GetElement(1,3);
  translation[2] = 0.0;
  
  vtkImageStencilData *stencilTransformed = vtkImageStencilData::New();
  ResampleStencil( translation, imageSpacing, imageOrigin, 
                                       stencilIn, stencilTransformed );
  
  int imageExtent[6], stencilExtent[6], sliceExtent[6];
  image->GetExtent( imageExtent );
  stencilTransformed->GetExtent( stencilExtent );
  
  // Get the stencil for the region to be discarded (set to ReplaceValue).
  // Contour is perpendicular to X.
  vtkSmartPointer< vtkImageStencilData > stencil;
  if (self->GetSegmentInside())
    {
    sliceExtent[0] = imageExtent[0];
    sliceExtent[1] = imageExtent[1];
    sliceExtent[2] = imageExtent[2];
    sliceExtent[3] = imageExtent[3];
    sliceExtent[4] = stencilExtent[4]; // Of course sliceExtent[4] = sliceExtent[5]
    sliceExtent[5] = stencilExtent[5];
    vtkImageStencilDataFlip *stencilFlipper = vtkImageStencilDataFlip::New();
    stencilFlipper->SetInput(stencilTransformed);
    stencilFlipper->SetFlipExtent(sliceExtent);
    stencilFlipper->Update();
    stencil = stencilFlipper->GetOutput();
    stencilFlipper->Delete();
    }
  else
    {
    stencilTransformed->GetExtent(sliceExtent);
    sliceExtent[4] = stencilExtent[4]; // Of course sliceExtent[4] = sliceExtent[5]
    sliceExtent[5] = stencilExtent[5];
    stencil = stencilTransformed;
    }
  stencilTransformed->Delete();
 
  // Intersect the extrusion extent with the user specified segmentation extents.
  // This restricts segmentation to the region specified by the user.
   
  int extrusionExtent[6], segmentationExtent[6];
  extrusionExtent[0] = sliceExtent[0];   extrusionExtent[1] = sliceExtent[1];
  extrusionExtent[2] = sliceExtent[2];   extrusionExtent[3] = sliceExtent[3];
  extrusionExtent[4] = imageExtent[4];   extrusionExtent[5] = imageExtent[5];
  
  if (!self->IntersectWithSegmentationExtent( extrusionExtent, 
                                              segmentationExtent ) )
    {
    return -1; // user specified extents do not overlap with the image at all!
    }

  // Iterate over the pixels the stencil projects onto and set 
  // them to replaceValue
  int r1, r2, iter = 0, numPixelsReplaced = 0;
  T *beginPtr, *endPtr;

  self->InvokeEvent(vtkCommand::StartEvent);

  const int nComp = image->GetNumberOfScalarComponents();

  // Loop through and replace pixels..
  
  for (int z=segmentationExtent[4]; z<=segmentationExtent[5]; z++)
    {
    for (int y=segmentationExtent[2]; y<=segmentationExtent[3]; y++, iter=0)
      {
      int moreSubExtents = 1;
      while( moreSubExtents )
        {
        moreSubExtents = stencil->GetNextExtent( 
            r1, r2, segmentationExtent[0], segmentationExtent[1], y, sliceExtent[4], iter);

        if (r1 <= r2 )  // sanity check
          { 
          beginPtr = (T *)(image->GetScalarPointer(r1, y, z));
          endPtr   = (T *)(image->GetScalarPointer(r2, y, z));
          while (beginPtr <= endPtr)
            {
            for (int nc = 0; nc < nComp; nc++)
              {
              *beginPtr = replaceValue;
              ++beginPtr;
              }
            ++numPixelsReplaced;
            }
          }
        }
      }
    double progress = (double)z/(double)
        (segmentationExtent[5]-segmentationExtent[4]+1);
    self->InvokeEvent( vtkCommand::ProgressEvent, &progress);
    }
    
  self->InvokeEvent(vtkCommand::EndEvent);
  
  return numPixelsReplaced;
}

//----------------------------------------------------------------------------
// This function projects a stencil lying on the XZ plane, normal along Y
// Returns the number of pixels inside the segmentation or  -1 on failure
//
template< class T >
int ParallelProjectStencilAlongXZY( vtkStencilProjectionImageFilter *self, T replaceValue)
{
  // Assumed    - stencil normal is along y  (usually a coronal slice)
  //             +x of the stencil is along +x of the image
  //             +y of the stencil is along +y of the image.
  // This means that the stencil spacing should be 
  //  stencilSpacing[0] = imagespacing[0];
  //  stencilSpacing[1] = imagespacing[1];
  //  stencilSpacing[2] = imagespacing[2];
  // 
  // Of course The stencil origin could be shifted.. Add the last column of the
  // StencilAxes matrix to the current stencil origin and update extents.

  if (self->GetNumberOfInputConnections(0) < 1)
    {
    return 0;
    }
  
  vtkImageData *image = vtkImageData::SafeDownCast(
            self->GetExecutive()->GetInputData(0,0));
  vtkImageStencilData *stencilIn = self->GetStencil();
  
  double imageSpacing[3], imageOrigin[3], translation[3], newOrigin[3], newSpacing[3];
  image->GetOrigin(imageOrigin);
  image->GetSpacing(imageSpacing);

  newOrigin[0] = imageOrigin[0];    newSpacing[0] = imageSpacing[0];
  newOrigin[1] = imageOrigin[2];    newSpacing[1] = imageSpacing[2];
  newOrigin[2] = imageOrigin[1];    newSpacing[2] = imageSpacing[1];

  vtkMatrix4x4 * stencilAxes = self->GetStencilAxes();
  translation[0] = stencilAxes->GetElement(0,3);
  translation[1] = 0.0;
  translation[2] = stencilAxes->GetElement(2,3);
  
  vtkImageStencilData *stencilTransformed = vtkImageStencilData::New();
  ResampleStencil( translation, newSpacing, newOrigin, 
                                       stencilIn, stencilTransformed );
  
  int imageExtent[6], stencilExtent[6], sliceExtent[6];
  image->GetExtent( imageExtent );
  stencilTransformed->GetExtent( stencilExtent );

  // Get the stencil for the region to be discarded (set to ReplaceValue).
  // Contour is perpendicular to X.
  vtkSmartPointer< vtkImageStencilData > stencil;
  if (self->GetSegmentInside())
    {
    sliceExtent[0] = imageExtent[0];
    sliceExtent[1] = imageExtent[1];
    sliceExtent[2] = imageExtent[4];
    sliceExtent[3] = imageExtent[5];
    sliceExtent[4] = stencilExtent[4]; // Of course sliceExtent[4] = sliceExtent[5]
    sliceExtent[5] = stencilExtent[5];
    vtkImageStencilDataFlip *stencilFlipper = vtkImageStencilDataFlip::New();
    stencilFlipper->SetInput(stencilTransformed);
    stencilFlipper->SetFlipExtent(sliceExtent);
    stencilFlipper->Update();
    stencil  = stencilFlipper->GetOutput();
    stencilFlipper->Delete();
    }
  else
    {
    stencil = stencilTransformed;
    stencil->GetExtent(sliceExtent);
    sliceExtent[4] = stencilExtent[4];
    sliceExtent[5] = stencilExtent[5];
    }
  stencilTransformed->Delete();

  // Intersect the extrusion extent with the user specified segmentation extents.
  // This restricts segmentation to the region specified by the user.
   
 
  int extrusionExtent[6], segmentationExtent[6];
  extrusionExtent[0] = sliceExtent[0];   extrusionExtent[1] = sliceExtent[1];
  extrusionExtent[2] = imageExtent[2];   extrusionExtent[3] = imageExtent[3];
  extrusionExtent[4] = sliceExtent[2];   extrusionExtent[5] = sliceExtent[3];

  if (!self->IntersectWithSegmentationExtent( extrusionExtent, 
                                              segmentationExtent ) )
    {
    return -1; // user specified extents do not overlap with the image at all!
    }

  
  // Iterate over the pixels the stencil projects onto and set 
  // them to replaceValue
  int r1, r2, iter = 0, numPixelsReplaced = 0;
  T *beginPtr, *endPtr;

  self->InvokeEvent(vtkCommand::StartEvent);

  // Loop through and replace pixels..

  for (int z=segmentationExtent[4]; z<=segmentationExtent[5]; z++, iter=0)
    {
    int moreSubExtents = 1;
    while( moreSubExtents )
      {
      moreSubExtents = stencil->GetNextExtent( 
          r1, r2, segmentationExtent[0], segmentationExtent[1], z, sliceExtent[4], iter);
      if (r1 <= r2 )  // sanity check
        { 
        for (int y=segmentationExtent[2]; y<=segmentationExtent[3]; y++)
          {
          beginPtr = (T *)(image->GetScalarPointer(r1, y, z));
          endPtr   = (T *)(image->GetScalarPointer(r2, y, z));
          while (beginPtr <= endPtr)
            {
            *beginPtr = replaceValue;
            ++beginPtr;
            ++numPixelsReplaced;
            }
          }
        }
      }
    double progress = (double)z/(double)
        (segmentationExtent[5]-segmentationExtent[4]+1);
    self->InvokeEvent( vtkCommand::ProgressEvent, &progress);
    }
  
  self->InvokeEvent(vtkCommand::EndEvent);
  
  return numPixelsReplaced;
}

//----------------------------------------------------------------------------
// This function projects a stencil lying on the YZ plane, normal along X
// Returns the number of pixels inside the segmentation or  -1 on failure
//
template< class T >
int ParallelProjectStencilAlongYZX( vtkStencilProjectionImageFilter *self, T replaceValue)
{
  // Assumed    - stencil normal is along x  (usually a sagittal slice)
  //             +x of the stencil is along +y of the image
  //             +y of the stencil is along +z of the image.
  // This means that the stencil spacing should be 
  //  stencilSpacing[0] = imagespacing[1];
  //  stencilSpacing[1] = imagespacing[2];
  //  stencilSpacing[2] = imagespacing[0];
  // 
  // Of course The stencil origin could be shifted.. Add the last column of the
  // StencilAxes matrix to the current stencil origin and update extents.

  if (self->GetNumberOfInputConnections(0) < 1)
    {
    return 0;
    }
  
  vtkImageData *image = vtkImageData::SafeDownCast(
            self->GetExecutive()->GetInputData(0,0));
  vtkImageStencilData *stencilIn = self->GetStencil();
  
  
  double imageSpacing[3], imageOrigin[3], translation[3], newOrigin[3], newSpacing[3];
  image->GetOrigin(imageOrigin);
  image->GetSpacing(imageSpacing);
  newOrigin[0] = imageOrigin[1];    newSpacing[0] = imageSpacing[1];
  newOrigin[1] = imageOrigin[2];    newSpacing[1] = imageSpacing[2];
  newOrigin[2] = imageOrigin[0];    newSpacing[2] = imageSpacing[0];

  vtkMatrix4x4 * stencilAxes = self->GetStencilAxes();
  translation[0] = 0.0;
  translation[1] = stencilAxes->GetElement(1,3);
  translation[2] = stencilAxes->GetElement(2,3);
  
  
  vtkImageStencilData *stencilTransformed = vtkImageStencilData::New();
  ResampleStencil( translation, newSpacing, newOrigin, 
                                       stencilIn, stencilTransformed );
  
  int imageExtent[6], stencilExtent[6], sliceExtent[6];
  image->GetExtent( imageExtent );
  stencilTransformed->GetExtent( stencilExtent );

  // Get the stencil for the region to be discarded (set to ReplaceValue).
  // Contour is perpendicular to X.
  vtkSmartPointer< vtkImageStencilData > stencil;
  if (self->GetSegmentInside())
    {
    // stencil is everything but the contour.
    sliceExtent[0] = imageExtent[2];
    sliceExtent[1] = imageExtent[3];
    sliceExtent[2] = imageExtent[4];
    sliceExtent[3] = imageExtent[5];
    sliceExtent[4] = stencilExtent[4]; // Of course sliceExtent[4] = sliceExtent[5]
    sliceExtent[5] = stencilExtent[5];
    vtkImageStencilDataFlip *stencilFlipper = vtkImageStencilDataFlip::New();
    stencilFlipper->SetInput(stencilTransformed);
    stencilFlipper->SetFlipExtent(sliceExtent);
    stencilFlipper->Update();
    stencil = stencilFlipper->GetOutput();
    stencilFlipper->Delete();
    }
  else
    {
    // stencil is inside contour
    stencil = stencilTransformed;
    stencil->GetExtent(sliceExtent);
    sliceExtent[4] = stencilExtent[4];
    sliceExtent[5] = stencilExtent[5];
    }
  stencilTransformed->Delete();
    
  // Intersect the extrusion extent with the user specified segmentation extents.
  // This restricts segmentation to the region specified by the user.
   
  int extrusionExtent[6], segmentationExtent[6];
  extrusionExtent[0] = imageExtent[0];   extrusionExtent[1] = imageExtent[1];
  extrusionExtent[2] = sliceExtent[0];   extrusionExtent[3] = sliceExtent[1];
  extrusionExtent[4] = sliceExtent[2];   extrusionExtent[5] = sliceExtent[3];
  
  if (!self->IntersectWithSegmentationExtent( extrusionExtent, 
                                              segmentationExtent ) )
    {
    return -1; // user specified extents do not overlap with the image at all!
    }

  
  // Iterate over the pixels the stencil projects onto and set 
  // them to replaceValue
  int r1, r2, iter = 0, numPixelsReplaced = 0;
  T *beginPtr, *endPtr;
  
  self->InvokeEvent(vtkCommand::StartEvent);

  // Loop through and replace pixels..

  for (int z=segmentationExtent[4]; z<=segmentationExtent[5]; z++, iter=0)
    {
    int moreSubExtents = 1;
    while( moreSubExtents )
      {
      moreSubExtents = stencil->GetNextExtent( 
          r1, r2, segmentationExtent[2], segmentationExtent[3], z, sliceExtent[4], iter);

      if (r1 <= r2 )  // sanity check
        { 
        for (int y=r1; y<=r2; y++)
          {
          beginPtr = (T *)(image->GetScalarPointer(segmentationExtent[0], y, z));
          endPtr   = (T *)(image->GetScalarPointer(segmentationExtent[1], y, z));
          while (beginPtr <= endPtr)
            {
            *beginPtr = replaceValue;
            ++beginPtr;
            ++numPixelsReplaced;
            }
          }
        }
      }
    double progress = (double)z/(double)
        (segmentationExtent[5]-segmentationExtent[4]+1);
    self->InvokeEvent( vtkCommand::ProgressEvent, &progress);
    }

  self->InvokeEvent(vtkCommand::EndEvent);

  return numPixelsReplaced;
}

//----------------------------------------------------------------------------
// Projects the contour onto a stencil that lies on an arbitrarly oriented
// plane. The direction cosines and origin of this plane are specified
// by SetStencilAxes. This is specific to the case of parallel projection.
//    
// myStencilProjectionImageFilter->SetInput(image); 
// myStencilProjectionImageFilter->SetStencilAxes(
//        imagePlaneWidget->GetReslice()->GetResliceAxes());
// myStencilProjectionImageFilter->Update();
// 
template< class T >
int ParallelProjectStencilAlongStencilAxes( 
    vtkStencilProjectionImageFilter *self, T replaceValue )
{
  if (self->GetNumberOfInputConnections(0) < 1)
    {
    return 0;
    }
  vtkImageData *image = vtkImageData::SafeDownCast(
            self->GetExecutive()->GetInputData(0,0));

  int imageExtent[6], numPixelsReplaced = 0;
  double imageSpacing[3], imageOrigin[3], p[3];
  image->GetExtent(imageExtent);
  image->GetSpacing(imageSpacing);
  image->GetOrigin( imageOrigin);

  T * ptr = (T*)(image->GetScalarPointer());

  // Intersect the extrusion extent with the user specified segmentation extents.
  // This restricts segmentation to the region specified by the user.
   
  int extrusionExtent[6], segmentationExtent[6];
  extrusionExtent[0] = imageExtent[0];   extrusionExtent[1] = imageExtent[1];
  extrusionExtent[2] = imageExtent[2];   extrusionExtent[3] = imageExtent[3];
  extrusionExtent[4] = imageExtent[4];   extrusionExtent[5] = imageExtent[5];
  
  if (!self->IntersectWithSegmentationExtent( extrusionExtent, 
                                              segmentationExtent ) )
    {
    return -1; // user specified extents do not overlap with the image at all!
    }

  
  if (self->GetSegmentInside())
    {
    for (int k = segmentationExtent[4]; k <= segmentationExtent[5]; k++)
      {
      for (int j = segmentationExtent[2]; j <= segmentationExtent[3]; j++)
        {
        for (int i = segmentationExtent[0]; i <= segmentationExtent[1]; i++)
          {
          p[0] =  imageOrigin[0] + (i - segmentationExtent[0])*imageSpacing[0];
          p[1] =  imageOrigin[1] + (j - segmentationExtent[2])*imageSpacing[1];
          p[2] =  imageOrigin[2] + (k - segmentationExtent[4])*imageSpacing[2];
          if (!self->CheckIfPointProjectionIsWithinStencil(p))
            {
            *ptr = replaceValue;
            ++numPixelsReplaced;
            }
          ++ptr;
          }
        }
      double progress = (double)k/(double)
        (segmentationExtent[5]-segmentationExtent[4]+1);
      self->InvokeEvent( vtkCommand::ProgressEvent, &progress);
      }
    }
  else
    {
    for (int k = segmentationExtent[4]; k <= segmentationExtent[5]; k++)
      {
      for (int j = segmentationExtent[2]; j <= segmentationExtent[3]; j++)
        {
        for (int i = segmentationExtent[0]; i <= segmentationExtent[1]; i++)
          {
          p[0] =  imageOrigin[0] + (i - segmentationExtent[0])*imageSpacing[0];
          p[1] =  imageOrigin[1] + (j - segmentationExtent[2])*imageSpacing[1];
          p[2] =  imageOrigin[2] + (k - segmentationExtent[4])*imageSpacing[2];
          if (self->CheckIfPointProjectionIsWithinStencil(p))
            {
            *ptr = replaceValue;
            ++numPixelsReplaced;
            }
          ++ptr;
          }
        }
      double progress = (double)k/(double)
        (segmentationExtent[5]-segmentationExtent[4]+1);
      self->InvokeEvent( vtkCommand::ProgressEvent, &progress);
      }
    }

  self->InvokeEvent(vtkCommand::EndEvent);
 
  return numPixelsReplaced;
}

//----------------------------------------------------------------------------
// Projects the contour onto a stencil that lies on an arbitrarly oriented
// plane. The direction cosines and origin of this plane are specified
// by SetStencilAxes. This is specific to the case of parallel projection.
//    
// myStencilProjectionImageFilter->SetInput(image); 
// myStencilProjectionImageFilter->SetStencilAxes(
//        imagePlaneWidget->GetReslice()->GetResliceAxes());
// myStencilProjectionImageFilter->Update();
// 
template< class T >
int PerspectiveProjectStencilAlongStencilAxes( 
    vtkStencilProjectionImageFilter *self, T replaceValue )
{
  if (self->GetNumberOfInputConnections(0) < 1)
    {
    return 0;
    }
  vtkImageData *image = vtkImageData::SafeDownCast(
            self->GetExecutive()->GetInputData(0,0));

  int imageExtent[6], numPixelsReplaced = 0;
  double imageSpacing[3], imageOrigin[3], p[4];
  image->GetExtent(imageExtent);
  image->GetSpacing(imageSpacing);
  image->GetOrigin( imageOrigin);

  T * ptr = (T*)(image->GetScalarPointer());

  // Intersect the extrusion extent with the user specified segmentation extents.
  // This restricts segmentation to the region specified by the user.
   
  int extrusionExtent[6], segmentationExtent[6];
  extrusionExtent[0] = imageExtent[0];   extrusionExtent[1] = imageExtent[1];
  extrusionExtent[2] = imageExtent[2];   extrusionExtent[3] = imageExtent[3];
  extrusionExtent[4] = imageExtent[4];   extrusionExtent[5] = imageExtent[5];
  
  if (!self->IntersectWithSegmentationExtent( extrusionExtent, 
                                              segmentationExtent ) )
    {
    return -1; // user specified extents do not overlap with the image at all!
    }

  
  const bool segmentInside = self->GetSegmentInside();

  for (int k = segmentationExtent[4]; k <= segmentationExtent[5]; k++)
    {
    for (int j = segmentationExtent[2]; j <= segmentationExtent[3]; j++)
      {
      for (int i = segmentationExtent[0]; i <= segmentationExtent[1]; i++)
        {
        p[0] =  imageOrigin[0] + (i - segmentationExtent[0])*imageSpacing[0];
        p[1] =  imageOrigin[1] + (j - segmentationExtent[2])*imageSpacing[1];
        p[2] =  imageOrigin[2] + (k - segmentationExtent[4])*imageSpacing[2];
        self->PerspectiveProjectPointOnFocalPlane(p);
        if ((!self->CheckIfPointProjectionIsWithinStencil(p) && segmentInside) ||
            (self->CheckIfPointProjectionIsWithinStencil(p) && !segmentInside))
          {
          *ptr = replaceValue;
          ++numPixelsReplaced;
          }
        ++ptr;
        }
      }
    double progress = (double)k/(double)
      (segmentationExtent[5]-segmentationExtent[4]+1);
    self->InvokeEvent( vtkCommand::ProgressEvent, &progress);
    }

  self->InvokeEvent(vtkCommand::EndEvent);
 
  return numPixelsReplaced;
}

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

  vtkImageData *input = vtkImageData::SafeDownCast(  
          inInfo->Get(vtkDataObject::DATA_OBJECT()));
  vtkImageStencilData *stencil = this->GetStencil();
  if (!input || !stencil || !this->StencilAxes)
    {
    return 1;
    }
    
  // let superclass allocate data
  this->Superclass::RequestData(request, inputVector, outputVector);

  // If no segmentation extents have been set, initialize it with the image
  // extents
  if (this->SegmentationExtent[0] == 0 &&
      this->SegmentationExtent[1] == 0 &&
      this->SegmentationExtent[2] == 0 &&
      this->SegmentationExtent[3] == 0 &&
      this->SegmentationExtent[4] == 0 &&
      this->SegmentationExtent[5] == 0 )
    {
    input->GetExtent( this->SegmentationExtent );
    }

  // Now check if the camera has been set. If the camera has been set, derive
  // the projection parameters and stencil axes from the camera parameters.
  if (this->VolumetricProjection && 
      this->Camera && !this->Camera->GetParallelProjection())
    {
    // Precompute the inverse of the projection matrix.. needed later
    if (!this->InverseCompositeProjectionTransformMatrix)
      {
      this->InverseCompositeProjectionTransformMatrix = vtkMatrix4x4::New();
      }
    this->InverseCompositeProjectionTransformMatrix->DeepCopy(
                      this->CompositeProjectionTransformMatrix);
    this->InverseCompositeProjectionTransformMatrix->Invert();

    // Precompute the focal depth.. needed later.
    double fp[4];
    this->Camera->GetFocalPoint( fp );
    fp[3] = 1.0;
    this->CompositeProjectionTransformMatrix->MultiplyPoint(fp,fp);
    this->FocalDepth = fp[2] / fp[3];
    }
  
  // Project the stencil onto the imagedata.
  // 
  // We check if the direction cosines are along the axis aligned planes. If 
  // they are, we will treat them seperately since we know that the given
  // stencil is the same across all the slices along its normal and things 
  // can be sped up. There are 4 ProjectStencil functions that handle each
  // of the cases separately.
  // 
  // Are the direction cosines parallel to the axes ?
  int normal = this->CheckIfAxisAlignedDirection();

  if (normal == 0)
    {
    switch (input->GetScalarType())
      {
      vtkTemplateMacro( this->NumberOfPixelsReplaced = 
        ParallelProjectStencilAlongYZX( this, 
          static_cast< VTK_TT >(this->ReplaceValue)));
      default:
        {
        vtkErrorMacro(<< "vtkStencilProjectionImageFilter: Unknown ScalarType");
        return 1;
        }
      }
    }
  else if (normal == 1)
    {
    switch (input->GetScalarType())
      {
      vtkTemplateMacro( this->NumberOfPixelsReplaced = 
        ParallelProjectStencilAlongXZY( this,
          static_cast< VTK_TT >(this->ReplaceValue)));
      default:
        {
        vtkErrorMacro(<< "vtkStencilProjectionImageFilter: Unknown ScalarType");
        return 1;
        }
      }
    }
  else if (normal == 2)
    {
    switch (input->GetScalarType())
      {
      vtkTemplateMacro( this->NumberOfPixelsReplaced = 
        ParallelProjectStencilAlongXYZ( this,
          static_cast< VTK_TT >(this->ReplaceValue)));
      default:
        {
        vtkErrorMacro(<< "vtkStencilProjectionImageFilter: Unknown ScalarType");
        return 1;
        }
      }
    }
  else if (normal == 3)
    {
    if (this->Camera && !this->Camera->GetParallelProjection())
      {
      // Perspective projection case.

      stencil->GetSpacing( this->StencilSpacing );
      stencil->GetOrigin( this->StencilOrigin );
      stencil->GetExtent( this->StencilExtent );
      this->Stencil = stencil;

      switch (input->GetScalarType())
        {
        vtkTemplateMacro( this->NumberOfPixelsReplaced = 
          PerspectiveProjectStencilAlongStencilAxes( this,
            static_cast< VTK_TT >(this->ReplaceValue)));
        default:
          {
          vtkErrorMacro(<< "vtkStencilProjectionImageFilter: Unknown ScalarType");
          return 1;
          }
        }
       
      }
    else
      {
      // Handle the parallel projection case separately. 
      stencil->GetSpacing( this->StencilSpacing );
      stencil->GetOrigin( this->StencilOrigin );
      stencil->GetExtent( this->StencilExtent );
      this->Stencil = stencil;
      switch (input->GetScalarType())
        {
        vtkTemplateMacro( this->NumberOfPixelsReplaced = 
          ParallelProjectStencilAlongStencilAxes( this, 
            static_cast< VTK_TT >(this->ReplaceValue)));
        default:
          {
          vtkErrorMacro(<< "vtkStencilProjectionImageFilter: Unknown ScalarType");
          return 1;
          }
        }
      }
    }

  return 1;
}


//----------------------------------------------------------------------------
int vtkStencilProjectionImageFilter::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(), "vtkImageStencilData");
    return 1;
    }
  return 0;
}

//----------------------------------------------------------------------------
// 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 vtkStencilProjectionImageFilter::RequestUpdateExtent (
  vtkInformation * vtkNotUsed(request),
  vtkInformationVector **inputVector,
  vtkInformationVector *outputVector)
{
  // get the info objects
  vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
  vtkInformation *stencilInfo = inputVector[1]->GetInformationObject(0);
  vtkImageData *input = vtkImageData::SafeDownCast(
    inInfo->Get(vtkDataObject::DATA_OBJECT()));
  vtkImageStencilData *stencil = vtkImageStencilData::SafeDownCast(  
     stencilInfo->Get(vtkDataObject::DATA_OBJECT()));
  
  int stencilExtent[6], imageExtent[6];
  stencil->GetExtent(stencilExtent);
  stencilInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), stencilExtent, 6);
  input->GetExtent(imageExtent);
  inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), imageExtent, 6);
 
  return 1;
}

//----------------------------------------------------------------------------
// Checks the values of the direction cosines to see if they are along the 
// axis aligned directions (within a certain tolerance). 
// returns 0 - stencil normal is along x  (usually a sagittal slice)
//             +x of the stencil is along +y of the image
//             +y of the stencil is along +z of the image.
//         1 - Stencil Normal is along Y  (Usually a coronal slice)
//             +X of the stencil is along +X of the image
//             +Y of the stencil is along +Z of the image
//         2 - Stencil Normal is along Z  (Usually a axial slice)
//             +X of the stencil is along +X of the image
//             +Y of the stencil is along +Y of the image.
//         3 - Not an axis aligned Normal
//
int vtkStencilProjectionImageFilter::CheckIfAxisAlignedDirection() const
{
  const double tolerance = 0.001;
  const double x[3][3] = {{0,0,1},{1,0,0},{0,1,0}};
  bool stencilNormalIsX = true;
  unsigned int i;
  for (i = 0; i<3; i++)
    {
    for (unsigned int j = 0; j<3; j++)
      {
      if (fabs(this->StencilAxes->GetElement(i,j) - x[i][j]) > tolerance)
        {
        stencilNormalIsX = false;
        }
      }
    }
  if (stencilNormalIsX)
    {
    return 0;
    }
  
  const double y[3][3] = {{1,0,0},{0,0,1},{0,1,0}};
  bool stencilNormalIsY = true;
  for (i = 0; i<3; i++)
    {
    for (unsigned int j = 0; j<3; j++)
      {
      if (fabs(this->StencilAxes->GetElement(i,j) - y[i][j]) > tolerance)
        {
        stencilNormalIsY = false;
        }
      }
    }
  if (stencilNormalIsY)
    {
    return 1;
    }
   
  const double z[3][3] = {{1,0,0},{0,1,0},{0,0,1}};
  bool stencilNormalIsZ = true;
  for (i = 0; i<3; i++)
    {
    for (unsigned int j = 0; j<3; j++)
      {
      if (fabs(this->StencilAxes->GetElement(i,j) - z[i][j]) > tolerance)
        {
        stencilNormalIsZ = false;
        }
      }
    }
  if (stencilNormalIsZ)
    {
    return 2;
    }

  return 3; // Not one of the axis aligned planes.
}

//----------------------------------------------------------------------------
// Use parallel projection to project every point from an image onto a stencil 
// lying on a certian plane (the direciton is the normal to the stencil) to 
// see if it lies within the stencil or not.
// Returns:        1 if inside, 0 if outside.
// 
int vtkStencilProjectionImageFilter::
CheckIfPointProjectionIsWithinStencil(double p[3])
{
  // Project the point p into the stencil plane.
  double v[3]; // vector from Origin of the stencil axes plane to p
  v[0] = p[0] - this->StencilAxes->GetElement(0,3);
  v[1] = p[1] - this->StencilAxes->GetElement(1,3);
  v[2] = p[2] - this->StencilAxes->GetElement(2,3);
  
  // (x,y) on the stencil = (V . X_axis_of_stencil, V . Y_axis_os_stencil)
  double x,y;
  x =    v[0] * this->StencilAxes->GetElement(0,0) + 
         v[1] * this->StencilAxes->GetElement(1,0) +
         v[2] * this->StencilAxes->GetElement(2,0);
  y =    v[0] * this->StencilAxes->GetElement(0,1) + 
         v[1] * this->StencilAxes->GetElement(1,1) +
         v[2] * this->StencilAxes->GetElement(2,1);

  // Get the (row,col) on the stencil
  int row = (int) (((y-this->StencilOrigin[1])/this->StencilSpacing[1]) + 0.5);
  int col = (int) (((x-this->StencilOrigin[0])/this->StencilSpacing[0]) + 0.5);

  // Check if (row,col) lies within the stencil.
  if (row < this->StencilExtent[2] || row > this->StencilExtent[3] ||
      col < this->StencilExtent[0] || col > this->StencilExtent[1] )
    {
    return 0;
    }
  else
    {
    int iter = 0, r1, r2, moreSubExtents = 1;
    while( moreSubExtents )
      {
      moreSubExtents = this->Stencil->GetNextExtent( r1, r2, 
        this->StencilExtent[0], this->StencilExtent[1], 
                     row, this->StencilExtent[4], iter);

      if (r1 <= r2 && col >= r1 && col <= r2) 
        { 
        return 1;
        }
      }
    }
  return 0;
}

//----------------------------------------------------------------------------
// Project a point p onto the focal plane, using the camera parameters.
// Although this is generic enough to work for both parallel and perspective
// projections, this is necessary only for perspective projections. For parallel 
// projection, we adopt a faster strategy by simply extruding along the 
// direction of projection.
// 
void vtkStencilProjectionImageFilter::
PerspectiveProjectPointOnFocalPlane(double p[4])
{
  p[3] = 1.0;

  // Project the point p into the focal plane.

  //  - Just a WorldToView operation.
  this->CompositeProjectionTransformMatrix->MultiplyPoint(p,p);
  p[0] /= p[3];
  p[1] /= p[3];
  p[2] /= p[3];
  p[3] = 1.0;

  //  - Use the focal depth to place the view on the focal plane and
  //    transform back to the world on the focal plane.
  p[2] = this->FocalDepth;
  this->InverseCompositeProjectionTransformMatrix->MultiplyPoint(p,p);
  p[0] /= p[3];
  p[1] /= p[3];
  p[2] /= p[3];
}

//----------------------------------------------------------------------------
int vtkStencilProjectionImageFilter::IntersectWithSegmentationExtent( 
    int inExtent[6], int outExtent[6] )
{
  outExtent[0] = max( this->SegmentationExtent[0], inExtent[0] );
  outExtent[1] = min( this->SegmentationExtent[1], inExtent[1] );
  outExtent[2] = max( this->SegmentationExtent[2], inExtent[2] );
  outExtent[3] = min( this->SegmentationExtent[3], inExtent[3] );
  outExtent[4] = max( this->SegmentationExtent[4], inExtent[4] );
  outExtent[5] = min( this->SegmentationExtent[5], inExtent[5] );
  if ( outExtent[0] > outExtent[1] || 
       outExtent[2] > outExtent[3] ||
       outExtent[4] > outExtent[5] )
    {
    return 0; // They don't intersect
    }

  return 1;
}

//----------------------------------------------------------------------------
void vtkStencilProjectionImageFilter::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
  os << indent << "StencilAxes: " << this->StencilAxes << endl;
  if (this->StencilAxes)
    {
    this->StencilAxes->PrintSelf(os,indent.GetNextIndent());
    }
  os << indent << "ReplaceValue: " << this->ReplaceValue << endl;
  os << indent << "NumberOfPixelsReplaced: " << 
                  this->NumberOfPixelsReplaced << endl;
  os << indent << "SegmentInside: " << this->SegmentInside << endl;
  
  os << indent << "SegmentationExtent:     " 
     << this->SegmentationExtent[0] << ", " 
     << this->SegmentationExtent[1] << ", " << this->SegmentationExtent[2] << ", " 
     << this->SegmentationExtent[3] << ", " << this->SegmentationExtent[4] << ", " 
     << this->SegmentationExtent[5] << endl; 

  // These ivars are for temporary storage
  // this->Stencil 
  // this->StencilSpacing
  // this->StencilOrigin
  // this->StencilExtent
}

