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

  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.

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

Copyright (c) 2008 Atamai, Inc.

Use, modification and redistribution of the software, in source or
binary forms, are permitted provided that the following terms and
conditions are met:

1) Redistribution of the source code, in verbatim or modified
   form, must retain the above copyright notice, this license,
   the following disclaimer, and any notices that refer to this
   license and/or the following disclaimer.  

2) Redistribution in binary form must include the above copyright
   notice, a copy of this license and the following disclaimer
   in the documentation or with other materials provided with the
   distribution.

3) Modified copies of the source code must be clearly marked as such,
   and must not be misrepresented as verbatim copies of the source code.

THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS"
WITHOUT EXPRESSED OR IMPLIED WARRANTY INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  IN NO EVENT SHALL ANY COPYRIGHT HOLDER OR OTHER PARTY WHO MAY
MODIFY AND/OR REDISTRIBUTE THE SOFTWARE UNDER THE TERMS OF THIS LICENSE
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, LOSS OF DATA OR DATA BECOMING INACCURATE
OR LOSS OF PROFIT OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF
THE USE OR INABILITY TO USE THE SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

=========================================================================*/
#include "vtkPlanarPolyDataToImageStencil.h"
#include "vtkImageStencilData.h"
#include "vtkObjectFactory.h"

#include "vtkLine.h"
#include "vtkCellArray.h"
#include "vtkDataArray.h"
#include "vtkDoubleArray.h"
#include "vtkSignedCharArray.h"
#include "vtkMergePoints.h"
#include "vtkPoints.h"
#include "vtkPointData.h"
#include "vtkCellData.h"
#include "vtkGenericCell.h"
#include "vtkLine.h"
#include "vtkImageData.h"
#include "vtkPolyData.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkStreamingDemandDrivenPipeline.h"

#include <math.h>

#include <vtkstd/vector>
#include <vtkstd/algorithm>


vtkStandardNewMacro(vtkPlanarPolyDataToImageStencil);

//----------------------------------------------------------------------------
vtkPlanarPolyDataToImageStencil::vtkPlanarPolyDataToImageStencil()
{
}

//----------------------------------------------------------------------------
vtkPlanarPolyDataToImageStencil::~vtkPlanarPolyDataToImageStencil()
{
}

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

//----------------------------------------------------------------------------
inline int vtkPlanarPolyDataToImageStencilFloor(double x)
{
#if defined i386 || defined _M_IX86 || defined _M_X64
  // This code assumes IEEE 754 64-bit double.
  // It uses a denormalizer to round the double at the
  // 2^(-16) position, or around 1e-5, and then extracts
  // the integer portion.  So, essentially, it is a floor()
  // operation that is accurate to within 1e-5.
  // We use it because it is many, many times
  // faster than the floor() function.
  union { vtkTypeFloat64 d; vtkTypeUInt32 i[2]; } dual;
  dual.d = x + 103079215104.0;  // (2**(52-16))*1.5
  return static_cast<int>((dual.i[1]<<16)|(dual.i[0]>>16));
#else
  // This doesn't assume IEEE 754 and is a good, fast
  // floor() approximation on most architectures
  x += 2147483648.0;
  vtkTypeUInt32 i = static_cast<unsigned int>(x);
  return static_cast<int>(i - 2147483648U);
#endif
}

//----------------------------------------------------------------------------
static void vtkFloatingEndPointScanConvertLine2D(
  double pt1[2], double pt2[2], int inflection1, int inflection2,
  double tolerance, int extent[6],
  vtkstd::vector< vtkstd::vector<double> >& zyBucket)
{
  double x1 = pt1[0];
  double x2 = pt2[0];
  double y1 = pt1[1];
  double y2 = pt2[1];

  // swap end points if necessary
  if (y1 > y2)
    {
    x1 = pt2[0];
    x2 = pt1[0];
    y1 = pt2[1];
    y2 = pt1[1];
    }

  // find min and max of x values
  double xmin = x1;
  double xmax = x2;
  if (x1 > x2)
    {
    xmin = x2;
    xmax = x1;
    }

  // check for parallel to the x-axis
  if (y1 == y2)
    {
    return;
    }
  
  // Integer y values for start and end of line
  int Ay, By;

  double ymin = y1;
  double ymax = y2;
  if (inflection1 < 0 || inflection2 < 0)
    {
    // if this is a lower inflection point, include a tolerance
    ymin -= tolerance;
    }
  if (inflection1 > 0 || inflection2 > 0)
    {
    // likewise, if upper inflection, add tolerance at top
    ymax += tolerance;
    }

  // The "+1" is important, it means that if we have two polygons
  // that share a line, and make a stencil from each, the produced
  // regions are guaranteed not to overlap if tolerance=0.
  Ay = vtkPlanarPolyDataToImageStencilFloor(ymin) + 1;
  By = vtkPlanarPolyDataToImageStencilFloor(ymax);

  // Precompute values for a Bresenham-like line algorithm
  double grad = (x2 - x1)/(y2 - y1);
  double delta = (Ay - y1)*grad;

  // Go along y and place each x in the proper (y,z) bucket.
  for( int y = Ay; y <= By; y++ )
    {
    delta += grad;
    double x = x1 + delta;
    // clamp x (because of tolerance, it might not be in range)
    if (x < xmin)
      {
      x = xmin;
      }
    else if (x > xmax)
      {
      x = xmax;
      }
    zyBucket[y - extent[2]].push_back( x );
    }
}

//----------------------------------------------------------------------------
void vtkPlanarPolyDataToImageStencil::PlanarThreadedExecute(
  vtkImageStencilData *data,
  int extent[6],
  int threadId)
{
  if (extent[5] != extent[4])
    {
    vtkErrorMacro( << "Input poly is not planar." );
    return;
    }

  // Description of algorithm:
  // 2) find all "loose ends" and connect them to make polygons
  //    (if the input polydata is closed, there will be no loose ends) 
  // 3) go through all line segments, and for each integer y value on
  //    a line segment, store the x value at that point in a bucket
  // 4) for each (y,z) integer index, find all the stored x values
  //    and use them to create the vtkStencilData
    
  // the spacing and origin of the generated stencil
  double *spacing = data->GetSpacing();
  double *origin = data->GetOrigin();

  double tolerance = this->Tolerance;

  // if we have no data then return
  if (!this->GetInput()->GetNumberOfPoints())
    {
    return;
    }

  // Only divide once
  double invspacing[3];
  invspacing[0] = 1.0/spacing[0];
  invspacing[1] = 1.0/spacing[1];
  invspacing[2] = 1.0/spacing[2];

  // get the input data
  vtkPolyData *input = this->GetInput();

  // the locator to use with the data
  vtkMergePoints *locator = vtkMergePoints::New();

  // Just copy the input polydata. Its assumed to be cut anyway.
  vtkPolyData *slice = vtkPolyData::New();
  slice->DeepCopy(input); //kk

  // This vector stores all line segments by recording all "x"
  // positions on the surface for each (y,z) integer position.
  vtkstd::vector< vtkstd::vector<double> > zyBucket((extent[3] - extent[2]) + 1);

  // Loop through the slices
  int idxY;

  if (!slice->GetNumberOfLines())
    {
    return;
    }

  // convert to structured coords via origin and spacing
  vtkPoints *points = slice->GetPoints();
  vtkIdType numberOfPoints = points->GetNumberOfPoints();

  for (vtkIdType j = 0; j < numberOfPoints; j++)
    {
    double tempPoint[3];
    points->GetPoint(j, tempPoint);
    tempPoint[0] = (tempPoint[0] - origin[0])*invspacing[0];
    tempPoint[1] = (tempPoint[1] - origin[1])*invspacing[1];
    tempPoint[2] = (tempPoint[2] - origin[2])*invspacing[2];
    points->SetPoint(j, tempPoint);
    }

  // Step 2: Find and connect all the loose ends
  vtkCellArray *lines = slice->GetLines();

  vtkIdList *looseEndIdList = vtkIdList::New();
  vtkIdList *looseEndNeighborList = vtkIdList::New();
  vtkSignedCharArray *inflectionPointList = vtkSignedCharArray::New();
  
  // find all points with just a single adjacent point,
  // also look for lower inflection points
  for (vtkIdType i = 0; i < numberOfPoints; i++)
    {
    double yval = points->GetPoint(i)[1];
    int bottomPoint = 1;
    int topPoint = 1;

    int numberOfNeighbors = 0;
    vtkIdType neighborId = 0;
    
    lines->InitTraversal();
    vtkIdType npts;
    vtkIdType *pointIds;
    while( lines->GetNextCell(npts, pointIds) )
      {
      for (vtkIdType j = 0; j < npts; j++)
        {
        if ( pointIds[j] == i )
          {
          if (j > 0)
            {
            numberOfNeighbors++;
            neighborId = pointIds[j-1];
            double yneighbor = points->GetPoint(neighborId)[1];
            if (yneighbor < yval)
              {
              bottomPoint = 0;
              }
            else if (yneighbor > yval)
              {
              topPoint = 0;
              }
            }
          if (j < npts-1)
            {
            numberOfNeighbors++;
            neighborId = pointIds[j+1];
            double yneighbor = points->GetPoint(neighborId)[1];
            if (yneighbor < yval)
              {
              bottomPoint = 0;
              }
            else if (yneighbor > yval)
              {
              topPoint = 0;
              }
            }
          break;
          }
        }
      }
    if (numberOfNeighbors == 1)
      {
      // store the loose end
      looseEndIdList->InsertNextId( i );
      looseEndNeighborList->InsertNextId( neighborId );
      }
    // mark lower inflection points
    int inflection = 0;
    if (bottomPoint)
      {
      inflection = -1;
      }
    else if (topPoint)
      {
      inflection = 1;
      }
    inflectionPointList->InsertNextValue( inflection );
    }

  while (looseEndIdList->GetNumberOfIds() >= 2)
    {
    // first loose end point in the list
    vtkIdType firstLooseEndId = looseEndIdList->GetId(0);
    vtkIdType neighborId = looseEndNeighborList->GetId(0);
    double firstLooseEnd[3];
    slice->GetPoint( firstLooseEndId, firstLooseEnd );
    double neighbor[3];
    slice->GetPoint( neighborId, neighbor);

    // second loose end in the list
    vtkIdType secondLooseEndId = looseEndIdList->GetId(1);
    double secondLooseEnd[3];
    slice->GetPoint( secondLooseEndId, secondLooseEnd );

    // search for the loose end closest to the first one
    double maxval = -VTK_LARGE_FLOAT;
    
    for(vtkIdType j = 1; j < looseEndIdList->GetNumberOfIds(); j++)
      {
      vtkIdType currentLooseEndId = looseEndIdList->GetId( j );
      if (currentLooseEndId != neighborId)
        {
        double currentLooseEnd[3];
        slice->GetPoint( currentLooseEndId, currentLooseEnd );

        // When connecting loose ends, use dot product to favor
        // continuing in same direction as the line already
        // connected to the loose end, but also favour short
        // distances by dividing dotprod by square of distance.
        double v1[2], v2[2];
        v1[0] = firstLooseEnd[0] - neighbor[0];
        v1[1] = firstLooseEnd[1] - neighbor[1];
        v2[0] = currentLooseEnd[0] - firstLooseEnd[0];
        v2[1] = currentLooseEnd[1] - firstLooseEnd[1];
        double dotprod = v1[0]*v2[0] + v1[1]*v2[1];
        double distance2 = v2[0]*v2[0] + v2[1]*v2[1];

        if (dotprod > maxval*distance2 && distance2 > 0.0)
          {
          maxval = dotprod/distance2;
          secondLooseEndId = currentLooseEndId;
          }
        }
      }

    // create a new line segment by connecting these two points
    looseEndIdList->DeleteId( firstLooseEndId );
    looseEndIdList->DeleteId( secondLooseEndId );
    looseEndNeighborList->DeleteId( firstLooseEndId );
    looseEndNeighborList->DeleteId( secondLooseEndId );

    lines->InsertNextCell( 2 );
    lines->InsertCellPoint( firstLooseEndId );
    lines->InsertCellPoint( secondLooseEndId );
    }

  // Step 3: Go through all the line segments for this slice,
  // and for each integer y position on the line segment,
  // drop the corresponding x position into the (y,z) bucket.
  lines->InitTraversal();
  vtkIdType *pts = 0;
  vtkIdType npts = 0;

  while ( lines->GetNextCell(npts, pts) )
    {
    for (vtkIdType j = 1; j < npts; j++)
      {
      double point1[3], point2[3];
      points->GetPoint(pts[j-1], point1);
      points->GetPoint(pts[j], point2);
      // check to see if line contains a lower inflection point
      int inflection1 = inflectionPointList->GetValue(pts[j-1]);
      int inflection2 = inflectionPointList->GetValue(pts[j]);
    
      vtkFloatingEndPointScanConvertLine2D(point1, point2,
                                           inflection1, inflection2,
                                           tolerance,
                                           extent, zyBucket);
      }
    }

  looseEndIdList->Delete();
  looseEndNeighborList->Delete();
  inflectionPointList->Delete();
 

  // Step 4: The final part of the algorithm is to fill in the
  // stencil, i.e. rasterization of the polyhedron.
  int r1, r2, lastr2;
  for (idxY = extent[2]; idxY <= extent[3]; idxY++)
    {
    vtkstd::vector<double>& xList = zyBucket[idxY - extent[2]];

    if (xList.empty())
      {
      continue;
      }
    
    // handle pairs
    lastr2 = extent[0] - 1;
    if (xList.size() % 2 == 0)
      {
      vtkstd::sort(xList.begin(), xList.end());

      vtkstd::vector<double>::iterator xIter = xList.begin();
      while (xIter != xList.end())
        {
        double x1 = *xIter++;
        double x2 = *xIter++;
        // Use floor()+1 instead of ceil() to ensure that, if
        // tolerance is zero and there are neighboring extents,
        // there will be no overlap.
        r1 = vtkPlanarPolyDataToImageStencilFloor(x1 - tolerance) + 1;
        // Take floor() of second value in pair
        r2 = vtkPlanarPolyDataToImageStencilFloor(x2 + tolerance);

        // extents are not allowed to overlap
        if (r1 <= lastr2)
          {
          r1 = lastr2 + 1;
          // eliminate empty extents
          if (r1 > r2)
            {
            continue;
            }
          }

        data->InsertNextExtent(r1, r2, idxY, extent[4]);

        lastr2 = r2;
        }
      }
    }

  slice->Delete();
  locator->Delete();
}

//----------------------------------------------------------------------------
int vtkPlanarPolyDataToImageStencil::RequestData(
  vtkInformation *request,
  vtkInformationVector **inputVector,
  vtkInformationVector *outputVector)
{
  vtkImageStencilSource::RequestData(request, inputVector, outputVector);

  vtkInformation *outInfo = outputVector->GetInformationObject(0);

  vtkImageStencilData *data = vtkImageStencilData::SafeDownCast(
    outInfo->Get(vtkDataObject::DATA_OBJECT()));

  int extent[6];
  data->GetExtent(extent);

  // PlanarThreadedExecute is only called from a single thread for
  // now, but it could as easily be called from ThreadedRequestData
  this->PlanarThreadedExecute(data, extent, 0);

  return 1;
}

