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

  Program:   Visualization Toolkit
  Module:    vtkGlyphSource2D.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 "vtkGlyphSource2D.h"

#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkIdList.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkPolyData.h"
#include "vtkUnsignedCharArray.h"

vtkStandardNewMacro(vtkGlyphSource2D);

//------------------------------------------------------------------------------
vtkGlyphSource2D::vtkGlyphSource2D()
{
  this->Center[0] = 0.0;
  this->Center[1] = 0.0;
  this->Center[2] = 0.0;
  this->Scale = 1.0;
  this->Scale2 = 1.5;
  this->Color[0] = 1.0;
  this->Color[1] = 1.0;
  this->Color[2] = 1.0;
  this->Filled = 1;
  this->Cross = 0;
  this->Dash = 0;
  this->RotationAngle = 0.0;
  this->Resolution = 8;
  this->OutputPointsPrecision = SINGLE_PRECISION;
  this->GlyphType = VTK_VERTEX_GLYPH;

  this->SetNumberOfInputPorts(0);
}

//------------------------------------------------------------------------------
int vtkGlyphSource2D::RequestData(vtkInformation* vtkNotUsed(request),
  vtkInformationVector** vtkNotUsed(inputVector), vtkInformationVector* outputVector)
{
  // get the info object
  vtkInformation* outInfo = outputVector->GetInformationObject(0);

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

  // Allocate storage
  vtkPoints* pts = vtkPoints::New();

  // Set the desired precision for the points in the output.
  if (this->OutputPointsPrecision == vtkAlgorithm::DOUBLE_PRECISION)
  {
    pts->SetDataType(VTK_DOUBLE);
  }
  else
  {
    pts->SetDataType(VTK_FLOAT);
  }

  pts->Allocate(6, 6);
  vtkCellArray* verts = vtkCellArray::New();
  verts->AllocateEstimate(1, 1);
  vtkCellArray* lines = vtkCellArray::New();
  lines->AllocateEstimate(4, 2);
  vtkCellArray* polys = vtkCellArray::New();
  polys->AllocateEstimate(1, 4);
  vtkUnsignedCharArray* colors = vtkUnsignedCharArray::New();
  colors->SetNumberOfComponents(3);
  colors->Allocate(2, 2);
  colors->SetName("Colors");

  this->ConvertColor();

  // Special options
  if (this->Dash)
  {
    int filled = this->Filled;
    this->Filled = 0;
    this->CreateDash(pts, lines, polys, colors, this->Scale2);
    this->Filled = filled;
  }
  if (this->Cross)
  {
    int filled = this->Filled;
    this->Filled = 0;
    this->CreateCross(pts, lines, polys, colors, this->Scale2);
    this->Filled = filled;
  }

  // Call the right function
  switch (this->GlyphType)
  {
    case VTK_NO_GLYPH:
      break;
    case VTK_VERTEX_GLYPH:
      this->CreateVertex(pts, verts, colors);
      break;
    case VTK_DASH_GLYPH:
      this->CreateDash(pts, lines, polys, colors, 1.0);
      break;
    case VTK_CROSS_GLYPH:
      this->CreateCross(pts, lines, polys, colors, 1.0);
      break;
    case VTK_THICKCROSS_GLYPH:
      this->CreateThickCross(pts, lines, polys, colors);
      break;
    case VTK_TRIANGLE_GLYPH:
      this->CreateTriangle(pts, lines, polys, colors);
      break;
    case VTK_SQUARE_GLYPH:
      this->CreateSquare(pts, lines, polys, colors);
      break;
    case VTK_CIRCLE_GLYPH:
      this->CreateCircle(pts, lines, polys, colors);
      break;
    case VTK_DIAMOND_GLYPH:
      this->CreateDiamond(pts, lines, polys, colors);
      break;
    case VTK_ARROW_GLYPH:
      this->CreateArrow(pts, lines, polys, colors);
      break;
    case VTK_THICKARROW_GLYPH:
      this->CreateThickArrow(pts, lines, polys, colors);
      break;
    case VTK_HOOKEDARROW_GLYPH:
      this->CreateHookedArrow(pts, lines, polys, colors);
      break;
    case VTK_EDGEARROW_GLYPH:
      this->CreateEdgeArrow(pts, lines, polys, colors);
      break;
  }

  this->TransformGlyph(pts);

  // Clean up
  output->SetPoints(pts);
  pts->Delete();

  output->SetVerts(verts);
  verts->Delete();

  output->SetLines(lines);
  lines->Delete();

  output->SetPolys(polys);
  polys->Delete();

  output->GetCellData()->SetScalars(colors);
  colors->Delete();

  return 1;
}

void vtkGlyphSource2D::ConvertColor()
{
  this->RGB[0] = static_cast<unsigned char>(255.0 * this->Color[0]);
  this->RGB[1] = static_cast<unsigned char>(255.0 * this->Color[1]);
  this->RGB[2] = static_cast<unsigned char>(255.0 * this->Color[2]);
}

void vtkGlyphSource2D::TransformGlyph(vtkPoints* pts)
{
  double x[3];
  vtkIdType i;
  vtkIdType numPts = pts->GetNumberOfPoints();

  if (this->RotationAngle == 0.0)
  {
    for (i = 0; i < numPts; i++)
    {
      pts->GetPoint(i, x);
      x[0] = this->Center[0] + this->Scale * x[0];
      x[1] = this->Center[1] + this->Scale * x[1];
      pts->SetPoint(i, x);
    }
  }
  else
  {
    double angle = vtkMath::RadiansFromDegrees(this->RotationAngle);
    double xt;
    for (i = 0; i < numPts; i++)
    {
      pts->GetPoint(i, x);
      xt = x[0] * cos(angle) - x[1] * sin(angle);
      x[1] = x[0] * sin(angle) + x[1] * cos(angle);
      x[0] = xt;
      x[0] = this->Center[0] + this->Scale * x[0];
      x[1] = this->Center[1] + this->Scale * x[1];
      pts->SetPoint(i, x);
    }
  }
}

void vtkGlyphSource2D::CreateVertex(
  vtkPoints* pts, vtkCellArray* verts, vtkUnsignedCharArray* colors)
{
  vtkIdType ptIds[1];
  ptIds[0] = pts->InsertNextPoint(0.0, 0.0, 0.0);
  verts->InsertNextCell(1, ptIds);
  colors->InsertNextValue(this->RGB[0]);
  colors->InsertNextValue(this->RGB[1]);
  colors->InsertNextValue(this->RGB[2]);
}

void vtkGlyphSource2D::CreateCross(vtkPoints* pts, vtkCellArray* lines, vtkCellArray* polys,
  vtkUnsignedCharArray* colors, double scale)
{
  vtkIdType ptIds[4];

  if (this->Filled)
  {
    this->CreateThickCross(pts, lines, polys, colors);
  }
  else
  {
    ptIds[0] = pts->InsertNextPoint(-0.5 * scale, 0.0, 0.0);
    ptIds[1] = pts->InsertNextPoint(0.5 * scale, 0.0, 0.0);
    lines->InsertNextCell(2, ptIds);
    colors->InsertNextValue(this->RGB[0]);
    colors->InsertNextValue(this->RGB[1]);
    colors->InsertNextValue(this->RGB[2]);
    ptIds[0] = pts->InsertNextPoint(0.0, -0.5 * scale, 0.0);
    ptIds[1] = pts->InsertNextPoint(0.0, 0.5 * scale, 0.0);
    lines->InsertNextCell(2, ptIds);
    colors->InsertNextValue(this->RGB[0]);
    colors->InsertNextValue(this->RGB[1]);
    colors->InsertNextValue(this->RGB[2]);
  }
}

void vtkGlyphSource2D::CreateThickCross(
  vtkPoints* pts, vtkCellArray* lines, vtkCellArray* polys, vtkUnsignedCharArray* colors)
{
  if (this->Filled)
  {
    vtkIdType ptIds[4];
    ptIds[0] = pts->InsertNextPoint(-0.5, -0.1, 0.0);
    ptIds[1] = pts->InsertNextPoint(0.5, -0.1, 0.0);
    ptIds[2] = pts->InsertNextPoint(0.5, 0.1, 0.0);
    ptIds[3] = pts->InsertNextPoint(-0.5, 0.1, 0.0);
    polys->InsertNextCell(4, ptIds);
    colors->InsertNextValue(this->RGB[0]);
    colors->InsertNextValue(this->RGB[1]);
    colors->InsertNextValue(this->RGB[2]);
    ptIds[0] = pts->InsertNextPoint(-0.1, -0.5, 0.0);
    ptIds[1] = pts->InsertNextPoint(0.1, -0.5, 0.0);
    ptIds[2] = pts->InsertNextPoint(0.1, 0.5, 0.0);
    ptIds[3] = pts->InsertNextPoint(-0.1, 0.5, 0.0);
    polys->InsertNextCell(4, ptIds);
    colors->InsertNextValue(this->RGB[0]);
    colors->InsertNextValue(this->RGB[1]);
    colors->InsertNextValue(this->RGB[2]);
  }
  else
  {
    vtkIdType ptIds[13];
    ptIds[0] = pts->InsertNextPoint(-0.5, -0.1, 0.0);
    ptIds[1] = pts->InsertNextPoint(-0.1, -0.1, 0.0);
    ptIds[2] = pts->InsertNextPoint(-0.1, -0.5, 0.0);
    ptIds[3] = pts->InsertNextPoint(0.1, -0.5, 0.0);
    ptIds[4] = pts->InsertNextPoint(0.1, -0.1, 0.0);
    ptIds[5] = pts->InsertNextPoint(0.5, -0.1, 0.0);
    ptIds[6] = pts->InsertNextPoint(0.5, 0.1, 0.0);
    ptIds[7] = pts->InsertNextPoint(0.1, 0.1, 0.0);
    ptIds[8] = pts->InsertNextPoint(0.1, 0.5, 0.0);
    ptIds[9] = pts->InsertNextPoint(-0.1, 0.5, 0.0);
    ptIds[10] = pts->InsertNextPoint(-0.1, 0.1, 0.0);
    ptIds[11] = pts->InsertNextPoint(-0.5, 0.1, 0.0);
    ptIds[12] = ptIds[0];
    lines->InsertNextCell(13, ptIds);
    colors->InsertNextValue(this->RGB[0]);
    colors->InsertNextValue(this->RGB[1]);
    colors->InsertNextValue(this->RGB[2]);
  }
}

void vtkGlyphSource2D::CreateTriangle(
  vtkPoints* pts, vtkCellArray* lines, vtkCellArray* polys, vtkUnsignedCharArray* colors)
{
  vtkIdType ptIds[4];

  ptIds[0] = pts->InsertNextPoint(-0.375, -0.25, 0.0);
  ptIds[1] = pts->InsertNextPoint(0.0, 0.5, 0.0);
  ptIds[2] = pts->InsertNextPoint(0.375, -0.25, 0.0);

  if (this->Filled)
  {
    polys->InsertNextCell(3, ptIds);
  }
  else
  {
    ptIds[3] = ptIds[0];
    lines->InsertNextCell(4, ptIds);
  }
  colors->InsertNextValue(this->RGB[0]);
  colors->InsertNextValue(this->RGB[1]);
  colors->InsertNextValue(this->RGB[2]);
}

void vtkGlyphSource2D::CreateSquare(
  vtkPoints* pts, vtkCellArray* lines, vtkCellArray* polys, vtkUnsignedCharArray* colors)
{
  vtkIdType ptIds[5];

  ptIds[0] = pts->InsertNextPoint(-0.5, -0.5, 0.0);
  ptIds[1] = pts->InsertNextPoint(0.5, -0.5, 0.0);
  ptIds[2] = pts->InsertNextPoint(0.5, 0.5, 0.0);
  ptIds[3] = pts->InsertNextPoint(-0.5, 0.5, 0.0);

  if (this->Filled)
  {
    polys->InsertNextCell(4, ptIds);
  }
  else
  {
    ptIds[4] = ptIds[0];
    lines->InsertNextCell(5, ptIds);
  }
  colors->InsertNextValue(this->RGB[0]);
  colors->InsertNextValue(this->RGB[1]);
  colors->InsertNextValue(this->RGB[2]);
}

void vtkGlyphSource2D::CreateCircle(
  vtkPoints* pts, vtkCellArray* lines, vtkCellArray* polys, vtkUnsignedCharArray* colors)
{
  vtkIdList* ptIds = vtkIdList::New();
  if (this->Filled) // it's a polygon!
  {
    ptIds->SetNumberOfIds(this->Resolution);
  }
  else
  {
    ptIds->SetNumberOfIds(this->Resolution + 1);
  }

  double x[3], theta;

  // generate points around a circle
  x[2] = 0.0;
  theta = 2.0 * vtkMath::Pi() / static_cast<double>(this->Resolution);
  for (int i = 0; i < this->Resolution; i++)
  {
    x[0] = 0.5 * cos(i * theta);
    x[1] = 0.5 * sin(i * theta);
    ptIds->SetId(i, pts->InsertNextPoint(x));
  }

  if (this->Filled)
  {
    polys->InsertNextCell(ptIds);
  }
  else
  { // close the line
    ptIds->SetId(this->Resolution, ptIds->GetId(0));
    lines->InsertNextCell(ptIds);
  }
  colors->InsertNextValue(this->RGB[0]);
  colors->InsertNextValue(this->RGB[1]);
  colors->InsertNextValue(this->RGB[2]);

  ptIds->Delete();
}

void vtkGlyphSource2D::CreateDiamond(
  vtkPoints* pts, vtkCellArray* lines, vtkCellArray* polys, vtkUnsignedCharArray* colors)
{
  vtkIdType ptIds[5];

  ptIds[0] = pts->InsertNextPoint(0.0, -0.5, 0.0);
  ptIds[1] = pts->InsertNextPoint(0.5, 0.0, 0.0);
  ptIds[2] = pts->InsertNextPoint(0.0, 0.5, 0.0);
  ptIds[3] = pts->InsertNextPoint(-0.5, 0.0, 0.0);

  if (this->Filled)
  {
    polys->InsertNextCell(4, ptIds);
  }
  else
  {
    ptIds[4] = ptIds[0];
    lines->InsertNextCell(5, ptIds);
  }
  colors->InsertNextValue(this->RGB[0]);
  colors->InsertNextValue(this->RGB[1]);
  colors->InsertNextValue(this->RGB[2]);
}

void vtkGlyphSource2D::CreateArrow(
  vtkPoints* pts, vtkCellArray* lines, vtkCellArray* polys, vtkUnsignedCharArray* colors)
{
  if (this->Filled) // create two convex polygons
  {
    this->CreateThickArrow(pts, lines, polys, colors);
  }
  else
  {
    // stem
    vtkIdType ptIds[3];
    ptIds[0] = pts->InsertNextPoint(-0.5, 0.0, 0.0);
    ptIds[1] = pts->InsertNextPoint(0.5, 0.0, 0.0);
    lines->InsertNextCell(2, ptIds);
    colors->InsertNextValue(this->RGB[0]);
    colors->InsertNextValue(this->RGB[1]);
    colors->InsertNextValue(this->RGB[2]);

    // arrow head
    ptIds[0] = pts->InsertNextPoint(0.2, -0.1, 0.0);
    ptIds[1] = pts->InsertNextPoint(0.5, 0.0, 0.0);
    ptIds[2] = pts->InsertNextPoint(0.2, 0.1, 0.0);
    lines->InsertNextCell(3, ptIds);
    colors->InsertNextValue(this->RGB[0]);
    colors->InsertNextValue(this->RGB[1]);
    colors->InsertNextValue(this->RGB[2]);
  }
}

void vtkGlyphSource2D::CreateThickArrow(
  vtkPoints* pts, vtkCellArray* lines, vtkCellArray* polys, vtkUnsignedCharArray* colors)
{
  vtkIdType ptIds[8];

  ptIds[0] = pts->InsertNextPoint(-0.5, -0.1, 0.0);
  ptIds[1] = pts->InsertNextPoint(0.1, -0.1, 0.0);
  ptIds[2] = pts->InsertNextPoint(0.1, -0.2, 0.0);
  ptIds[3] = pts->InsertNextPoint(0.5, 0.0, 0.0);
  ptIds[4] = pts->InsertNextPoint(0.1, 0.2, 0.0);
  ptIds[5] = pts->InsertNextPoint(0.1, 0.1, 0.0);
  ptIds[6] = pts->InsertNextPoint(-0.5, 0.1, 0.0);

  if (this->Filled) // create two convex polygons
  {
    polys->InsertNextCell(4);
    polys->InsertCellPoint(ptIds[0]);
    polys->InsertCellPoint(ptIds[1]);
    polys->InsertCellPoint(ptIds[5]);
    polys->InsertCellPoint(ptIds[6]);
    colors->InsertNextValue(this->RGB[0]);
    colors->InsertNextValue(this->RGB[1]);
    colors->InsertNextValue(this->RGB[2]);

    polys->InsertNextCell(5, ptIds + 1);
    colors->InsertNextValue(this->RGB[0]);
    colors->InsertNextValue(this->RGB[1]);
    colors->InsertNextValue(this->RGB[2]);
  }
  else
  {
    ptIds[7] = ptIds[0];
    lines->InsertNextCell(8, ptIds);
    colors->InsertNextValue(this->RGB[0]);
    colors->InsertNextValue(this->RGB[1]);
    colors->InsertNextValue(this->RGB[2]);
  }
}

void vtkGlyphSource2D::CreateHookedArrow(
  vtkPoints* pts, vtkCellArray* lines, vtkCellArray* polys, vtkUnsignedCharArray* colors)
{
  if (this->Filled)
  {
    // create two convex polygons
    vtkIdType ptIds[4];
    ptIds[0] = pts->InsertNextPoint(-0.5, -0.1, 0.0);
    ptIds[1] = pts->InsertNextPoint(0.1, -0.1, 0.0);
    ptIds[2] = pts->InsertNextPoint(0.1, 0.075, 0.0);
    ptIds[3] = pts->InsertNextPoint(-0.5, 0.075, 0.0);
    polys->InsertNextCell(4, ptIds);
    colors->InsertNextValue(this->RGB[0]);
    colors->InsertNextValue(this->RGB[1]);
    colors->InsertNextValue(this->RGB[2]);

    ptIds[0] = pts->InsertNextPoint(0.1, -0.1, 0.0);
    ptIds[1] = pts->InsertNextPoint(0.5, -0.1, 0.0);
    ptIds[2] = pts->InsertNextPoint(0.1, 0.2, 0.0);
    polys->InsertNextCell(3, ptIds);
    colors->InsertNextValue(this->RGB[0]);
    colors->InsertNextValue(this->RGB[1]);
    colors->InsertNextValue(this->RGB[2]);
  }
  else
  {
    vtkIdType ptIds[3];
    ptIds[0] = pts->InsertNextPoint(-0.5, 0.0, 0.0);
    ptIds[1] = pts->InsertNextPoint(0.5, 0.0, 0.0);
    ptIds[2] = pts->InsertNextPoint(0.2, 0.1, 0.0);
    lines->InsertNextCell(3, ptIds);
    colors->InsertNextValue(this->RGB[0]);
    colors->InsertNextValue(this->RGB[1]);
    colors->InsertNextValue(this->RGB[2]);
  }
}

void vtkGlyphSource2D::CreateEdgeArrow(
  vtkPoints* pts, vtkCellArray* lines, vtkCellArray* polys, vtkUnsignedCharArray* colors)
{
  vtkIdType ptIds[3];

  double x = 0.5 / sqrt(3.0);
  ptIds[0] = pts->InsertNextPoint(-1.0, x, 0.0);
  ptIds[1] = pts->InsertNextPoint(0.0, 0.0, 0.0);
  ptIds[2] = pts->InsertNextPoint(-1.0, -x, 0.0);

  if (this->Filled)
  {
    polys->InsertNextCell(3, ptIds);
  }
  else
  {
    lines->InsertNextCell(3, ptIds);
  }
  colors->InsertNextValue(this->RGB[0]);
  colors->InsertNextValue(this->RGB[1]);
  colors->InsertNextValue(this->RGB[2]);
}

void vtkGlyphSource2D::CreateDash(vtkPoints* pts, vtkCellArray* lines, vtkCellArray* polys,
  vtkUnsignedCharArray* colors, double scale)
{
  if (this->Filled)
  {
    vtkIdType ptIds[4];
    ptIds[0] = pts->InsertNextPoint(-0.5, -0.1, 0.0);
    ptIds[1] = pts->InsertNextPoint(0.5, -0.1, 0.0);
    ptIds[2] = pts->InsertNextPoint(0.5, 0.1, 0.0);
    ptIds[3] = pts->InsertNextPoint(-0.5, 0.1, 0.0);
    polys->InsertNextCell(4, ptIds);
  }
  else
  {
    vtkIdType ptIds2D[2];
    ptIds2D[0] = pts->InsertNextPoint(-0.5 * scale, 0.0, 0.0);
    ptIds2D[1] = pts->InsertNextPoint(0.5 * scale, 0.0, 0.0);
    colors->InsertNextValue(this->RGB[0]);
    colors->InsertNextValue(this->RGB[1]);
    colors->InsertNextValue(this->RGB[2]);
    lines->InsertNextCell(2, ptIds2D);
  }
  colors->InsertNextValue(this->RGB[0]);
  colors->InsertNextValue(this->RGB[1]);
  colors->InsertNextValue(this->RGB[2]);
}

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

  os << indent << "Center: (" << this->Center[0] << ", " << this->Center[1] << ", "
     << this->Center[2] << ")\n";

  os << indent << "Scale: " << this->Scale << "\n";
  os << indent << "Scale2: " << this->Scale2 << "\n";
  os << indent << "Rotation Angle: " << this->RotationAngle << "\n";
  os << indent << "Resolution: " << this->Resolution << "\n";

  os << indent << "Color: (" << this->Color[0] << ", " << this->Color[1] << ", " << this->Color[2]
     << ")\n";

  os << indent << "Filled: " << (this->Filled ? "On\n" : "Off\n");
  os << indent << "Dash: " << (this->Dash ? "On\n" : "Off\n");
  os << indent << "Cross: " << (this->Cross ? "On\n" : "Off\n");

  os << indent << "Glyph Type";
  switch (this->GlyphType)
  {
    case VTK_NO_GLYPH:
      os << "No Glyph\n";
      break;
    case VTK_VERTEX_GLYPH:
      os << "Vertex\n";
      break;
    case VTK_DASH_GLYPH:
      os << "Dash\n";
      break;
    case VTK_CROSS_GLYPH:
      os << "Cross\n";
      break;
    case VTK_THICKCROSS_GLYPH:
      os << "Cross\n";
      break;
    case VTK_TRIANGLE_GLYPH:
      os << "Triangle\n";
      break;
    case VTK_SQUARE_GLYPH:
      os << "Square\n";
      break;
    case VTK_CIRCLE_GLYPH:
      os << "Circle\n";
      break;
    case VTK_DIAMOND_GLYPH:
      os << "Diamond\n";
      break;
    case VTK_ARROW_GLYPH:
      os << "Arrow\n";
      break;
    case VTK_THICKARROW_GLYPH:
      os << "Arrow\n";
      break;
    case VTK_HOOKEDARROW_GLYPH:
      os << "Hooked Arrow\n";
      break;
    case VTK_EDGEARROW_GLYPH:
      os << "Edge Arrow\n";
      break;
  }
  os << indent << "Output Points Precision: " << this->OutputPointsPrecision << "\n";
}
