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

  Program:   Visualization Toolkit
  Module:    vtkArcPlotter.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkArcPlotter.h"

#include "vtkCamera.h"
#include "vtkCellArray.h"
#include "vtkFloatArray.h"
#include "vtkMath.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkPlane.h"
#include "vtkPointData.h"
#include "vtkPolyData.h"

vtkStandardNewMacro(vtkArcPlotter);

vtkCxxSetObjectMacro(vtkArcPlotter,Camera,vtkCamera);

vtkArcPlotter::vtkArcPlotter()
{
  this->Camera = nullptr;
  this->PlotMode = VTK_PLOT_SCALARS;
  this->PlotComponent = (-1); //plot all components
  this->Radius = 0.5;
  this->Height = 0.5;
  this->Offset = 0.0;

  this->DefaultNormal[0] = this->DefaultNormal[1] = 0.0;
  this->DefaultNormal[2] = 1.0;
  this->UseDefaultNormal = 0;

  this->FieldDataArray = 0;

  this->DataRange = nullptr;
  this->Tuple = nullptr;
  this->ActiveComponent = 0;
  this->NumberOfComponents = 0;
}

vtkArcPlotter::~vtkArcPlotter()
{
  if ( this->DataRange )
  {
    delete [] this->DataRange;
    delete [] this->Tuple;
  }
  if ( this->Camera )
  {
    this->Camera->UnRegister (this);
    this->Camera = nullptr;
  }
}

int vtkArcPlotter::RequestData(
  vtkInformation *vtkNotUsed(request),
  vtkInformationVector **inputVector,
  vtkInformationVector *outputVector)
{
  // get the info objects
  vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
  vtkInformation *outInfo = outputVector->GetInformationObject(0);

  // get the input and output
  vtkPolyData *input = vtkPolyData::SafeDownCast(
    inInfo->Get(vtkDataObject::DATA_OBJECT()));
  vtkPolyData *output = vtkPolyData::SafeDownCast(
    outInfo->Get(vtkDataObject::DATA_OBJECT()));

  vtkPointData *inPD;
  vtkPoints *inPts;
  vtkCellArray *inLines;
  int j;
  vtkIdType numPts, i;
  double x[3], normal[3], point[3], aveNormal[3];
  int id;
  vtkIdType *pts = nullptr;
  vtkIdType npts = 0;
  double x1[3], x2[3], x21[3], n[3];
  vtkFloatArray *lineNormals;
  vtkPoints *newPts;
  vtkCellArray *newLines;
  double *range, offset;
  int plotNum, compNum;
  vtkPoints *projPts;

  inPD = input->GetPointData();

  // Initialize
  //
  vtkDebugMacro(<<"Plotting along arc");

  if ( !(inPts=input->GetPoints()) ||
  (numPts=inPts->GetNumberOfPoints()) < 1 ||
  !(inLines=input->GetLines()) || inLines->GetNumberOfCells() < 1 )
  {
    vtkErrorMacro(<< "No input data!");
    return 0;
  }

  // Process attribute data to determine ranges, number of components, etc.
  if ( this->ProcessComponents(numPts, inPD) <= 0 )
  {
    return 0;
  }

  // Allocate points for projection
  //
  // Determine the projection plane. Project to a plane if camera is available
  // and defulat normal is not desired.
  if ( this->Camera && ! this->UseDefaultNormal )
  {
    double xProj[3];
    projPts = vtkPoints::New();
    projPts->SetNumberOfPoints(numPts);
    this->Camera->GetViewPlaneNormal(normal);
    this->Camera->GetFocalPoint(point);
    vtkMath::Normalize(normal);
    for ( i=0; i < numPts; i++ )
    {
      inPts->GetPoint(i, x);
      vtkPlane::ProjectPoint(x, point, normal, xProj);
      projPts->SetPoint(i,xProj);
    }
  }
  else
  {
    normal[0] = this->DefaultNormal[0];
    normal[1] = this->DefaultNormal[1];
    normal[2] = this->DefaultNormal[2];
    vtkMath::Normalize(normal);
    projPts = inPts; //use existing points
  }

  // For each polyline, compute a normal that lies in the
  // projection plane and is roughly perpendicular to the projected
  // polyline. Then generate the arc.
  //
  newPts = vtkPoints::New();
  newPts->Allocate(numPts,numPts);
  lineNormals = vtkFloatArray::New();
  lineNormals->SetNumberOfComponents(3);

  newLines = vtkCellArray::New();
  newLines->Allocate(inLines->GetSize());

  for (inLines->InitTraversal(); inLines->GetNextCell(npts,pts); )
  {
    lineNormals->SetNumberOfTuples(npts);
    if ( !this->Camera || this->UseDefaultNormal )
    {//use default normal
      for (i=0; i < npts; i++)
      {
        lineNormals->SetTuple(i,normal);
      }
    }
    else //generate normals
    {
      // Compute normals on each line segment perpendicular to view normal
      for (i=0; i < (npts-1); i++)
      {
        projPts->GetPoint(pts[i], x1);
        projPts->GetPoint(pts[i+1], x2);
        for (j=0; j<3; j++)
        {
          x21[j] = x2[j] - x1[j];
        }
        vtkMath::Cross(normal,x21,n);
        vtkMath::Normalize(n);
        lineNormals->SetTuple(i,n);
      }
      lineNormals->SetTuple(npts-1,n);
    }

    // Now average the normal calculation to get smoother results
    //
    vtkIdType window = npts / 100;
    if ( window < 5 )
    {
      window = 5;
    }
    // Start by computing an initial average normal
    aveNormal[0] = aveNormal[1] = aveNormal[2] = 0.0;
    for (i=0; i < npts && i < window; i++)
    {
      lineNormals->GetTuple(i,n);
      aveNormal[0] += n[0]; aveNormal[1] += n[1]; aveNormal[2] += n[2];
    }

    for (i=0; i < npts; i++)
    {
      if ( (i+window) < npts )
      {
        lineNormals->GetTuple(i+window,n);
        aveNormal[0] += n[0]; aveNormal[1] += n[1]; aveNormal[2] += n[2];
      }
      if ( (i-window) >= 0 )
      {
        lineNormals->GetTuple(i-window,n);
        aveNormal[0] -= n[0]; aveNormal[1] -= n[1]; aveNormal[2] -= n[2];
      }
      n[0] = aveNormal[0]; n[1] = aveNormal[1]; n[2] = aveNormal[2];
      vtkMath::Normalize(n);
      lineNormals->SetTuple(i, n);
    }
    this->UpdateProgress(0.50);

    // For each component, create an offset plot.
    for (plotNum=0, compNum=this->StartComp; compNum <= this->EndComp;
         compNum++, plotNum++)
    {
      offset = this->Radius + plotNum*this->Offset;
      range = this->DataRange + 2*compNum;

      newLines->InsertNextCell(npts);

      // Continue average normal computation using sliding window
      for (i=0; i < npts; i++)
      {
        this->Data->GetTuple(pts[i], this->Tuple);
        lineNormals->GetTuple(i,n);
        id = this->OffsetPoint(pts[i], inPts, n, newPts,
                               offset, range, this->Tuple[compNum]);
        newLines->InsertCellPoint(id);
      }
    } //for all components
  } //for all polylines
  this->UpdateProgress(0.90);

  lineNormals->Delete();
  if ( projPts != inPts )
  {
    projPts->Delete();
  }

  // Update output
  output->SetPoints(newPts);
  newPts->Delete();
  output->SetLines(newLines);
  newLines->Delete();

  return 1;
}

int vtkArcPlotter::ProcessComponents(vtkIdType numPts, vtkPointData *pd)
{
  vtkIdType i;
  int j;
  double *range;

  this->Data = nullptr;
  switch (this->PlotMode)
  {
    case VTK_PLOT_SCALARS:
      if ( pd->GetScalars() )
      {
        this->Data = pd->GetScalars();
      }
      break;
    case VTK_PLOT_VECTORS:
      if ( pd->GetVectors() )
      {
        this->Data = pd->GetVectors();
      }
      break;
    case VTK_PLOT_NORMALS:
      if ( pd->GetNormals() )
      {
        this->Data = pd->GetNormals();
      }
      break;
    case VTK_PLOT_TCOORDS:
      if ( pd->GetTCoords() )
      {
        this->Data = pd->GetTCoords();
      }
      break;
    case VTK_PLOT_TENSORS:
      if ( pd->GetTensors() )
      {
        this->Data = pd->GetTensors();
      }
      break;
    case VTK_PLOT_FIELD_DATA:
      int arrayNum = (this->FieldDataArray < pd->GetNumberOfArrays() ?
                      this->FieldDataArray : pd->GetNumberOfArrays() - 1);
      this->Data = pd->GetArray(arrayNum);
      break;
  }

  // Determine the number of components
  if ( this->Data )
  {
    this->NumberOfComponents = this->Data->GetNumberOfComponents();
    if ( this->PlotComponent >= 0 )
    {
      this->ActiveComponent = (this->PlotComponent < this->NumberOfComponents ?
                           this->PlotComponent : this->NumberOfComponents - 1);
      this->StartComp = this->EndComp = this->ActiveComponent;
    }
    else
    {
      this->StartComp = 0;
      this->EndComp = this->NumberOfComponents - 1;
    }
  }
  else
  {
    vtkErrorMacro(<<"Need input data to plot");
    return 0;
  }

  // Get the range of the components (for scaling the plot later)
  if ( this->DataRange )
  {
    delete [] this->DataRange;
    delete [] this->Tuple;
  }

  this->DataRange = new double [2*this->NumberOfComponents];
  this->Tuple = new double [this->NumberOfComponents];

  for (i=this->StartComp; i <= this->EndComp; i++)
  {
    range = this->DataRange + 2*i;
    range[0] =  VTK_FLOAT_MAX;
    range[1] =  -VTK_FLOAT_MAX;
  }

  for (i=0; i<numPts; i++)
  {
    this->Data->GetTuple(i, this->Tuple);

    for (j=this->StartComp; j <= this->EndComp; j++)
    {
      range = this->DataRange + 2*j;
      if ( this->Tuple[j] < range[0] )
      {
        range[0] = this->Tuple[j];
      }
      if ( this->Tuple[j] > range[1] )
      {
        range[1] = this->Tuple[j];
      }
    }
  }

  return this->NumberOfComponents;
}


int  vtkArcPlotter::OffsetPoint(vtkIdType ptId, vtkPoints *inPts, double n[3],
                                vtkPoints *newPts, double offset,
                                double *range, double v)
{
  double x[3], xNew[3];
  int i;
  double median = (range[0] + range[1])/2.0;
  double denom = range[1] - range[0];

  inPts->GetPoint(ptId, x);
  for (i=0; i<3; i++)
  {
    xNew[i] = x[i] + n[i] * (offset + ((v - median)/denom)*this->Height);
  }

  return newPts->InsertNextPoint(xNew);
}

vtkMTimeType vtkArcPlotter::GetMTime()
{
  vtkMTimeType mTime=this->Superclass::GetMTime();
  vtkMTimeType cameraMTime;

  if ( this->Camera && ! this->UseDefaultNormal )
  {
    cameraMTime = this->Camera->GetMTime();
    return (cameraMTime > mTime ? cameraMTime : mTime);
  }
  else
  {
    return mTime;
  }
}

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

  if ( this->Camera )
  {
    os << indent << "Camera:\n";
    this->Camera->PrintSelf(os,indent.GetNextIndent());
  }
  else
  {
    os << indent << "Camera: (none)\n";
  }

  os << indent << "Plot Mode: ";
  if ( this->PlotMode == VTK_PLOT_SCALARS )
  {
    os << "Plot Scalars\n";
  }
  else if ( this->PlotMode == VTK_PLOT_VECTORS )
  {
    os << "Plot Vectors\n";
  }
  else if ( this->PlotMode == VTK_PLOT_NORMALS )
  {
    os << "Plot Normals\n";
  }
  else if ( this->PlotMode == VTK_PLOT_TCOORDS )
  {
    os << "Plot TCoords\n";
  }
  else if ( this->PlotMode == VTK_PLOT_TENSORS )
  {
    os << "Plot Tensors\n";
  }
  else
  {
    os << "Plot Field Data\n";
  }

  os << indent << "Plot Component: ";
  if ( this->PlotComponent < 0 )
  {
    os << "(All Components)\n";
  }
  else
  {
    os << this->PlotComponent << "\n";
  }

  os << indent << "Field Data Array: " << this->FieldDataArray << "\n";

  os << indent << "Use Default Normal: "
     << (this->UseDefaultNormal ? "On\n" : "Off\n");
  os << indent << "Default Normal: " << "( " << this->DefaultNormal[0]
     << ", " << this->DefaultNormal[1] << ", " << this->DefaultNormal[2]
     << " )\n";

  os << indent << "Radius: " << this->Radius << "\n";
  os << indent << "Height: " << this->Height << "\n";
  os << indent << "Offset: " << this->Offset << "\n";
}
