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

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

#include "vtkCellArray.h"
#include "vtkFieldData.h"
#include "vtkFieldDataToAttributeDataFilter.h"
#include "vtkInformation.h"
#include "vtkInformationExecutivePortKey.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkPolyData.h"
#include "vtkRectilinearGrid.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkStructuredGrid.h"
#include "vtkStructuredPoints.h"
#include "vtkUnstructuredGrid.h"

vtkStandardNewMacro(vtkDataObjectToDataSetFilter);

//------------------------------------------------------------------------------
// Instantiate object with no input and no defined output.
vtkDataObjectToDataSetFilter::vtkDataObjectToDataSetFilter()
{
  int i;
  this->Updating = 0;

  this->DataSetType = VTK_POLY_DATA;
  vtkPolyData* output = vtkPolyData::New();
  this->GetExecutive()->SetOutputData(0, output);
  // Releasing data for pipeline parallism.
  // Filters will know it is empty.
  output->ReleaseData();
  output->Delete();

  for (i = 0; i < 3; i++)
  {
    this->PointArrays[i] = nullptr;
    this->PointArrayComponents[i] = -1; // uninitialized
    this->PointComponentRange[i][0] = this->PointComponentRange[i][1] = -1;
    this->PointNormalize[i] = 1; // yes, normalize
  }

  this->VertsArray = nullptr;
  this->VertsArrayComponent = -1;
  this->VertsComponentRange[0] = this->VertsComponentRange[1] = -1;

  this->LinesArray = nullptr;
  this->LinesArrayComponent = -1;
  this->LinesComponentRange[0] = this->LinesComponentRange[1] = -1;

  this->PolysArray = nullptr;
  this->PolysArrayComponent = -1;
  this->PolysComponentRange[0] = this->PolysComponentRange[1] = -1;

  this->StripsArray = nullptr;
  this->StripsArrayComponent = -1;
  this->StripsComponentRange[0] = this->StripsComponentRange[1] = -1;

  this->CellTypeArray = nullptr;
  this->CellTypeArrayComponent = -1;
  this->CellTypeComponentRange[0] = this->CellTypeComponentRange[1] = -1;

  this->CellConnectivityArray = nullptr;
  this->CellConnectivityArrayComponent = -1;
  this->CellConnectivityComponentRange[0] = this->CellConnectivityComponentRange[1] = -1;

  this->DefaultNormalize = 0;

  this->DimensionsArray = nullptr; // the name of the array
  this->DimensionsArrayComponent = -1;
  this->DimensionsComponentRange[0] = this->DimensionsComponentRange[1] = -1;

  this->SpacingArray = nullptr; // the name of the array
  this->SpacingArrayComponent = -1;
  this->SpacingComponentRange[0] = this->SpacingComponentRange[1] = -1;

  this->OriginArray = nullptr; // the name of the array
  this->OriginArrayComponent = -1;
  this->OriginComponentRange[0] = this->OriginComponentRange[1] = -1;

  this->Dimensions[0] = this->Dimensions[1] = this->Dimensions[2] = 0;
  this->Spacing[0] = this->Spacing[1] = this->Spacing[2] = 0.0;
  this->Origin[0] = this->Origin[1] = this->Origin[2] = 0.0;
}

//------------------------------------------------------------------------------
vtkDataObjectToDataSetFilter::~vtkDataObjectToDataSetFilter()
{
  for (int i = 0; i < 3; i++)
  {
    delete[] this->PointArrays[i];
  }
  delete[] this->VertsArray;
  delete[] this->LinesArray;
  delete[] this->PolysArray;
  delete[] this->StripsArray;
  delete[] this->CellTypeArray;
  delete[] this->CellConnectivityArray;
  delete[] this->DimensionsArray;
  delete[] this->SpacingArray;
  delete[] this->OriginArray;
}

void vtkDataObjectToDataSetFilter::SetDataSetType(int dt)
{
  if (dt == this->DataSetType)
  {
    return;
  }

  vtkDataSet* output;
  switch (dt)
  {
    case VTK_POLY_DATA:
      output = vtkPolyData::New();
      this->GetExecutive()->SetOutputData(0, output);
      output->Delete();
      break;
    case VTK_STRUCTURED_GRID:
      output = vtkStructuredGrid::New();
      this->GetExecutive()->SetOutputData(0, output);
      output->Delete();
      break;
    case VTK_STRUCTURED_POINTS:
      output = vtkStructuredPoints::New();
      this->GetExecutive()->SetOutputData(0, output);
      output->Delete();
      break;
    case VTK_UNSTRUCTURED_GRID:
      output = vtkUnstructuredGrid::New();
      this->GetExecutive()->SetOutputData(0, output);
      output->Delete();
      break;
    case VTK_RECTILINEAR_GRID:
      output = vtkRectilinearGrid::New();
      this->GetExecutive()->SetOutputData(0, output);
      output->Delete();
      break;
    default:
      vtkWarningMacro("unknown type in SetDataSetType");
  }
  this->DataSetType = dt;
  this->Modified();
}

//------------------------------------------------------------------------------
vtkDataObject* vtkDataObjectToDataSetFilter::GetInput()
{
  if (this->GetNumberOfInputConnections(0) < 1)
  {
    return nullptr;
  }

  return this->GetExecutive()->GetInputData(0, 0);
}

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

  vtkDataObject* input = inInfo->Get(vtkDataObject::DATA_OBJECT());
  vtkExecutive* inputExec = vtkExecutive::PRODUCER()->GetExecutive(inInfo);

  switch (this->DataSetType)
  {
    case VTK_POLY_DATA:
      break;

    case VTK_STRUCTURED_POINTS:
      // We need the array to get the dimensions
      inputExec->Update();
      this->ConstructDimensions(input);
      this->ConstructSpacing(input);
      this->ConstructOrigin(input);

      outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), 0, this->Dimensions[0] - 1, 0,
        this->Dimensions[1] - 1, 0, this->Dimensions[2] - 1);
      outInfo->Set(vtkDataObject::ORIGIN(), this->Origin, 3);
      outInfo->Set(vtkDataObject::SPACING(), this->Spacing, 3);
      break;

    case VTK_STRUCTURED_GRID:
      // We need the array to get the dimensions
      inputExec->Update();
      this->ConstructDimensions(input);
      outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), 0, this->Dimensions[0] - 1, 0,
        this->Dimensions[1] - 1, 0, this->Dimensions[2] - 1);
      break;

    case VTK_RECTILINEAR_GRID:
      // We need the array to get the dimensions
      inputExec->Update();
      this->ConstructDimensions(input);
      outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), 0, this->Dimensions[0] - 1, 0,
        this->Dimensions[1] - 1, 0, this->Dimensions[2] - 1);
      break;

    case VTK_UNSTRUCTURED_GRID:
      break;

    default:
      vtkErrorMacro(<< "Unsupported dataset type!");
  }

  return 1;
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::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
  vtkDataObject* input = inInfo->Get(vtkDataObject::DATA_OBJECT());
  vtkDataSet* output = vtkDataSet::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));

  vtkIdType npts;

  vtkDebugMacro(<< "Generating dataset from field data");

  switch (this->DataSetType)
  {
    case VTK_POLY_DATA:
      if ((npts = this->ConstructPoints(input, vtkPolyData::SafeDownCast(output))))
      {
        this->ConstructCells(input, vtkPolyData::SafeDownCast(output));
      }
      else
      {
        vtkErrorMacro(<< "Couldn't create any points");
      }
      break;

    case VTK_STRUCTURED_POINTS:
    {
      this->ConstructDimensions(input);
      this->ConstructSpacing(input);
      this->ConstructOrigin(input);
      vtkStructuredPoints* sp = vtkStructuredPoints::SafeDownCast(output);
      sp->SetDimensions(this->Dimensions);
      sp->SetOrigin(this->Origin);
      sp->SetSpacing(this->Spacing);
      break;
    }

    case VTK_STRUCTURED_GRID:
      if ((npts = this->ConstructPoints(input, this->GetStructuredGridOutput())))
      {
        this->ConstructDimensions(input);
        if (npts == (this->Dimensions[0] * this->Dimensions[1] * this->Dimensions[2]))
        {
          vtkStructuredGrid* sg = vtkStructuredGrid::SafeDownCast(output);
          sg->SetDimensions(this->Dimensions);
        }
        else
        {
          vtkErrorMacro(<< "Number of points don't match dimensions");
        }
      }
      break;

    case VTK_RECTILINEAR_GRID:
      if ((npts = this->ConstructPoints(input, this->GetRectilinearGridOutput())))
      {
        this->ConstructDimensions(input);
        if (npts == (this->Dimensions[0] * this->Dimensions[1] * this->Dimensions[2]))
        {
          vtkRectilinearGrid* rg = vtkRectilinearGrid::SafeDownCast(output);
          rg->SetDimensions(this->Dimensions);
        }
        else
        {
          vtkErrorMacro(<< "Number of points don't match dimensions");
        }
      }
      break;

    case VTK_UNSTRUCTURED_GRID:
      if (this->ConstructPoints(input, vtkUnstructuredGrid::SafeDownCast(output)))
      {
        this->ConstructCells(input, vtkUnstructuredGrid::SafeDownCast(output));
      }
      else
      {
        vtkErrorMacro(<< "Couldn't create any points");
      }
      break;

    default:
      vtkErrorMacro(<< "Unsupported dataset type!");
  }

  vtkFieldData* inFD = input->GetFieldData();
  vtkFieldData* outFD = output->GetFieldData();
  outFD->CopyAllOn();
  outFD->PassData(inFD);

  return 1;
}

// Get the output as vtkPolyData.
vtkPolyData* vtkDataObjectToDataSetFilter::GetPolyDataOutput()
{
  return vtkPolyData::SafeDownCast(this->GetOutput());
}

//------------------------------------------------------------------------------
vtkDataSet* vtkDataObjectToDataSetFilter::GetOutput()
{
  if (this->GetNumberOfOutputPorts() < 1)
  {
    return nullptr;
  }

  return vtkDataSet::SafeDownCast(this->GetExecutive()->GetOutputData(0));
}

// Get the output as vtkStructuredPoints.
vtkStructuredPoints* vtkDataObjectToDataSetFilter::GetStructuredPointsOutput()
{
  return vtkStructuredPoints::SafeDownCast(this->GetOutput());
}

// Get the output as vtkStructuredGrid.
vtkStructuredGrid* vtkDataObjectToDataSetFilter::GetStructuredGridOutput()
{
  return vtkStructuredGrid::SafeDownCast(this->GetOutput());
}

// Get the output as vtkUnstructuredGrid.
vtkUnstructuredGrid* vtkDataObjectToDataSetFilter::GetUnstructuredGridOutput()
{
  return vtkUnstructuredGrid::SafeDownCast(this->GetOutput());
}

// Get the output as vtkRectilinearGrid.
vtkRectilinearGrid* vtkDataObjectToDataSetFilter::GetRectilinearGridOutput()
{
  return vtkRectilinearGrid::SafeDownCast(this->GetOutput());
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::RequestUpdateExtent(vtkInformation* vtkNotUsed(request),
  vtkInformationVector** inputVector, vtkInformationVector* vtkNotUsed(outputVector))
{
  // get the info object
  vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);

  inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER(), 0);
  inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES(), 1);
  inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS(), 0);
  inInfo->Set(vtkStreamingDemandDrivenPipeline::EXACT_EXTENT(), 1);
  return 1;
}

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

  os << indent << "Data Set Type: ";
  if (this->DataSetType == VTK_POLY_DATA)
  {
    os << "vtkPolyData\n";
  }
  else if (this->DataSetType == VTK_STRUCTURED_POINTS)
  {
    os << "vtkStructuredPoints\n";
  }
  else if (this->DataSetType == VTK_STRUCTURED_GRID)
  {
    os << "vtkStructuredGrid\n";
  }
  else if (this->DataSetType == VTK_RECTILINEAR_GRID)
  {
    os << "vtkRectilinearGrid\n";
  }
  else // if ( this->DataSetType == VTK_UNSTRUCTURED_GRID )
  {
    os << "vtkUnstructuredGrid\n";
  }

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

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

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

  os << indent << "Default Normalize: " << (this->DefaultNormalize ? "On\n" : "Off\n");
}

//------------------------------------------------------------------------------
// Stuff related to points --------------------------------------------
//
void vtkDataObjectToDataSetFilter::SetPointComponent(
  int comp, const char* arrayName, int arrayComp, int min, int max, int normalize)
{
  if (comp < 0 || comp > 2)
  {
    vtkErrorMacro(<< "Point component must be between (0,2)");
    return;
  }

  vtkFieldDataToAttributeDataFilter::SetArrayName(this, this->PointArrays[comp], arrayName);
  if (this->PointArrayComponents[comp] != arrayComp)
  {
    this->PointArrayComponents[comp] = arrayComp;
    this->Modified();
  }
  if (this->PointComponentRange[comp][0] != min)
  {
    this->PointComponentRange[comp][0] = min;
    this->Modified();
  }
  if (this->PointComponentRange[comp][1] != max)
  {
    this->PointComponentRange[comp][1] = max;
    this->Modified();
  }
  if (this->PointNormalize[comp] != normalize)
  {
    this->PointNormalize[comp] = normalize;
    this->Modified();
  }
}

//------------------------------------------------------------------------------
const char* vtkDataObjectToDataSetFilter::GetPointComponentArrayName(int comp)
{
  comp = (comp < 0 ? 0 : (comp > 2 ? 2 : comp));
  return this->PointArrays[comp];
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetPointComponentArrayComponent(int comp)
{
  comp = (comp < 0 ? 0 : (comp > 2 ? 2 : comp));
  return this->PointArrayComponents[comp];
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetPointComponentMinRange(int comp)
{
  comp = (comp < 0 ? 0 : (comp > 2 ? 2 : comp));
  return this->PointComponentRange[comp][0];
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetPointComponentMaxRange(int comp)
{
  comp = (comp < 0 ? 0 : (comp > 2 ? 2 : comp));
  return this->PointComponentRange[comp][1];
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetPointComponentNormailzeFlag(int comp)
{
  comp = (comp < 0 ? 0 : (comp > 2 ? 2 : comp));
  return this->PointNormalize[comp];
}

//------------------------------------------------------------------------------
vtkIdType vtkDataObjectToDataSetFilter::ConstructPoints(vtkDataObject* input, vtkPointSet* ps)
{
  int i, updated = 0;
  vtkDataArray* fieldArray[3];
  vtkIdType npts;
  vtkFieldData* fd = input->GetFieldData();

  for (i = 0; i < 3; i++)
  {
    fieldArray[i] = vtkFieldDataToAttributeDataFilter::GetFieldArray(
      fd, this->PointArrays[i], this->PointArrayComponents[i]);

    if (fieldArray[i] == nullptr)
    {
      vtkErrorMacro(<< "Can't find array requested");
      return 0;
    }
    updated |= vtkFieldDataToAttributeDataFilter::UpdateComponentRange(
      fieldArray[i], this->PointComponentRange[i]);
  }

  npts = this->PointComponentRange[0][1] - this->PointComponentRange[0][0] + 1;
  if (npts != (this->PointComponentRange[1][1] - this->PointComponentRange[1][0] + 1) ||
    npts != (this->PointComponentRange[2][1] - this->PointComponentRange[2][0] + 1))
  {
    vtkErrorMacro(<< "Number of point components not consistent");
    return 0;
  }

  // Try using the arrays directly if possible; otherwise copy data
  vtkPoints* newPts = vtkPoints::New();
  if (fieldArray[0]->GetNumberOfComponents() == 3 && fieldArray[0] == fieldArray[1] &&
    fieldArray[1] == fieldArray[2] && fieldArray[0]->GetNumberOfTuples() == npts &&
    !this->PointNormalize[0] && !this->PointNormalize[1] && !this->PointNormalize[2])
  {
    newPts->SetData(fieldArray[0]);
  }
  else // have to copy data into created array
  {
    newPts->SetDataType(vtkFieldDataToAttributeDataFilter::GetComponentsType(3, fieldArray));
    newPts->SetNumberOfPoints(npts);

    for (i = 0; i < 3; i++)
    {
      if (vtkFieldDataToAttributeDataFilter::ConstructArray(newPts->GetData(), i, fieldArray[i],
            this->PointArrayComponents[i], this->PointComponentRange[i][0],
            this->PointComponentRange[i][1], this->PointNormalize[i]) == 0)
      {
        newPts->Delete();
        return 0;
      }
    }
  }

  ps->SetPoints(newPts);
  newPts->Delete();
  if (updated) // reset for next execution pass
  {
    for (i = 0; i < 3; i++)
    {
      this->PointComponentRange[i][0] = this->PointComponentRange[i][1] = -1;
    }
  }

  return npts;
}

//------------------------------------------------------------------------------
vtkIdType vtkDataObjectToDataSetFilter::ConstructPoints(
  vtkDataObject* input, vtkRectilinearGrid* rg)
{
  int i, nXpts, nYpts, nZpts, updated = 0;
  vtkIdType npts;
  vtkDataArray* fieldArray[3];
  vtkFieldData* fd = input->GetFieldData();

  for (i = 0; i < 3; i++)
  {
    fieldArray[i] = vtkFieldDataToAttributeDataFilter::GetFieldArray(
      fd, this->PointArrays[i], this->PointArrayComponents[i]);

    if (fieldArray[i] == nullptr)
    {
      vtkErrorMacro(<< "Can't find array requested");
      return 0;
    }
  }

  for (i = 0; i < 3; i++)
  {
    updated |= vtkFieldDataToAttributeDataFilter::UpdateComponentRange(
      fieldArray[i], this->PointComponentRange[i]);
  }

  nXpts = this->PointComponentRange[0][1] - this->PointComponentRange[0][0] + 1;
  nYpts = this->PointComponentRange[1][1] - this->PointComponentRange[1][0] + 1;
  nZpts = this->PointComponentRange[2][1] - this->PointComponentRange[2][0] + 1;
  npts = nXpts * nYpts * nZpts;

  // Create the coordinate arrays
  vtkDataArray* XPts;
  vtkDataArray* YPts;
  vtkDataArray* ZPts;

  // Decide whether to use the field array or whether to copy data
  // First look at the x-coordinates
  if (fieldArray[0]->GetNumberOfComponents() == 1 && fieldArray[0]->GetNumberOfTuples() == nXpts &&
    !this->PointNormalize[0])
  {
    XPts = fieldArray[0];
    XPts->Register(this);
  }
  else // have to copy data into created array
  {
    XPts = vtkDataArray::CreateDataArray(
      vtkFieldDataToAttributeDataFilter::GetComponentsType(1, fieldArray));
    XPts->SetNumberOfComponents(1);
    XPts->SetNumberOfTuples(nXpts);

    if (vtkFieldDataToAttributeDataFilter::ConstructArray(XPts, 0, fieldArray[0],
          this->PointArrayComponents[0], this->PointComponentRange[0][0],
          this->PointComponentRange[0][1], this->PointNormalize[0]) == 0)
    {
      XPts->Delete();
      return 0;
    }
  }

  // Look at the y-coordinates
  if (fieldArray[1]->GetNumberOfComponents() == 1 && fieldArray[1]->GetNumberOfTuples() == nYpts &&
    !this->PointNormalize[1])
  {
    YPts = fieldArray[1];
    YPts->Register(this);
  }
  else // have to copy data into created array
  {
    YPts = vtkDataArray::CreateDataArray(
      vtkFieldDataToAttributeDataFilter::GetComponentsType(1, fieldArray + 1));
    YPts->SetNumberOfComponents(1);
    YPts->SetNumberOfTuples(nYpts);

    if (vtkFieldDataToAttributeDataFilter::ConstructArray(YPts, 0, fieldArray[1],
          this->PointArrayComponents[1], this->PointComponentRange[1][0],
          this->PointComponentRange[1][1], this->PointNormalize[1]) == 0)
    {
      XPts->Delete();
      YPts->Delete();
      return 0;
    }
  }

  // Look at the z-coordinates
  if (fieldArray[2]->GetNumberOfComponents() == 1 && fieldArray[2]->GetNumberOfTuples() == nZpts &&
    !this->PointNormalize[2])
  {
    ZPts = fieldArray[2];
    ZPts->Register(this);
  }
  else // have to copy data into created array
  {
    ZPts = vtkDataArray::CreateDataArray(
      vtkFieldDataToAttributeDataFilter::GetComponentsType(1, fieldArray + 2));
    ZPts->SetNumberOfComponents(1);
    ZPts->SetNumberOfTuples(nZpts);

    if (vtkFieldDataToAttributeDataFilter::ConstructArray(ZPts, 0, fieldArray[2],
          this->PointArrayComponents[2], this->PointComponentRange[2][0],
          this->PointComponentRange[2][1], this->PointNormalize[2]) == 0)
    {
      XPts->Delete();
      YPts->Delete();
      ZPts->Delete();
      return 0;
    }
  }

  rg->SetXCoordinates(XPts);
  rg->SetYCoordinates(YPts);
  rg->SetZCoordinates(ZPts);
  XPts->Delete();
  YPts->Delete();
  ZPts->Delete();

  if (updated) // reset for next execution pass
  {
    for (i = 0; i < 3; i++)
    {
      this->PointComponentRange[i][0] = this->PointComponentRange[i][1] = -1;
    }
  }

  return npts;
}

//------------------------------------------------------------------------------
// Stuff related to vtkPolyData --------------------------------------------
//
void vtkDataObjectToDataSetFilter::SetVertsComponent(
  const char* arrayName, int arrayComp, int min, int max)
{
  vtkFieldDataToAttributeDataFilter::SetArrayName(this, this->VertsArray, arrayName);
  if (this->VertsArrayComponent != arrayComp)
  {
    this->VertsArrayComponent = arrayComp;
    this->Modified();
  }
  if (this->VertsComponentRange[0] != min)
  {
    this->VertsComponentRange[0] = min;
    this->Modified();
  }
  if (this->VertsComponentRange[1] != max)
  {
    this->VertsComponentRange[1] = max;
    this->Modified();
  }
}

//------------------------------------------------------------------------------
const char* vtkDataObjectToDataSetFilter::GetVertsComponentArrayName()
{
  return this->VertsArray;
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetVertsComponentArrayComponent()
{
  return this->VertsArrayComponent;
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetVertsComponentMinRange()
{
  return this->VertsComponentRange[0];
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetVertsComponentMaxRange()
{
  return this->VertsComponentRange[1];
}

//------------------------------------------------------------------------------
void vtkDataObjectToDataSetFilter::SetLinesComponent(
  const char* arrayName, int arrayComp, int min, int max)
{
  vtkFieldDataToAttributeDataFilter::SetArrayName(this, this->LinesArray, arrayName);
  if (this->LinesArrayComponent != arrayComp)
  {
    this->LinesArrayComponent = arrayComp;
    this->Modified();
  }
  if (this->LinesComponentRange[0] != min)
  {
    this->LinesComponentRange[0] = min;
    this->Modified();
  }
  if (this->LinesComponentRange[1] != max)
  {
    this->LinesComponentRange[1] = max;
    this->Modified();
  }
}

//------------------------------------------------------------------------------
const char* vtkDataObjectToDataSetFilter::GetLinesComponentArrayName()
{
  return this->LinesArray;
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetLinesComponentArrayComponent()
{
  return this->LinesArrayComponent;
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetLinesComponentMinRange()
{
  return this->LinesComponentRange[0];
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetLinesComponentMaxRange()
{
  return this->LinesComponentRange[1];
}

//------------------------------------------------------------------------------
void vtkDataObjectToDataSetFilter::SetPolysComponent(
  const char* arrayName, int arrayComp, int min, int max)
{
  vtkFieldDataToAttributeDataFilter::SetArrayName(this, this->PolysArray, arrayName);
  if (this->PolysArrayComponent != arrayComp)
  {
    this->PolysArrayComponent = arrayComp;
    this->Modified();
  }
  if (this->PolysComponentRange[0] != min)
  {
    this->PolysComponentRange[0] = min;
    this->Modified();
  }
  if (this->PolysComponentRange[1] != max)
  {
    this->PolysComponentRange[1] = max;
    this->Modified();
  }
}

//------------------------------------------------------------------------------
const char* vtkDataObjectToDataSetFilter::GetPolysComponentArrayName()
{
  return this->PolysArray;
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetPolysComponentArrayComponent()
{
  return this->PolysArrayComponent;
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetPolysComponentMinRange()
{
  return this->PolysComponentRange[0];
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetPolysComponentMaxRange()
{
  return this->PolysComponentRange[1];
}

//------------------------------------------------------------------------------
void vtkDataObjectToDataSetFilter::SetStripsComponent(
  const char* arrayName, int arrayComp, int min, int max)
{
  vtkFieldDataToAttributeDataFilter::SetArrayName(this, this->StripsArray, arrayName);
  if (this->StripsArrayComponent != arrayComp)
  {
    this->StripsArrayComponent = arrayComp;
    this->Modified();
  }
  if (this->StripsComponentRange[0] != min)
  {
    this->StripsComponentRange[0] = min;
    this->Modified();
  }
  if (this->StripsComponentRange[1] != max)
  {
    this->StripsComponentRange[1] = max;
    this->Modified();
  }
}

//------------------------------------------------------------------------------
const char* vtkDataObjectToDataSetFilter::GetStripsComponentArrayName()
{
  return this->StripsArray;
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetStripsComponentArrayComponent()
{
  return this->StripsArrayComponent;
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetStripsComponentMinRange()
{
  return this->StripsComponentRange[0];
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetStripsComponentMaxRange()
{
  return this->StripsComponentRange[1];
}

//------------------------------------------------------------------------------
// Stuff related to vtkUnstructuredGrid --------------------------------------
void vtkDataObjectToDataSetFilter::SetCellTypeComponent(
  const char* arrayName, int arrayComp, int min, int max)
{
  vtkFieldDataToAttributeDataFilter::SetArrayName(this, this->CellTypeArray, arrayName);
  if (this->CellTypeArrayComponent != arrayComp)
  {
    this->CellTypeArrayComponent = arrayComp;
    this->Modified();
  }
  if (this->CellTypeComponentRange[0] != min)
  {
    this->CellTypeComponentRange[0] = min;
    this->Modified();
  }
  if (this->CellTypeComponentRange[1] != max)
  {
    this->CellTypeComponentRange[1] = max;
    this->Modified();
  }
}

//------------------------------------------------------------------------------
const char* vtkDataObjectToDataSetFilter::GetCellTypeComponentArrayName()
{
  return this->CellTypeArray;
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetCellTypeComponentArrayComponent()
{
  return this->CellTypeArrayComponent;
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetCellTypeComponentMinRange()
{
  return this->CellTypeComponentRange[0];
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetCellTypeComponentMaxRange()
{
  return this->CellTypeComponentRange[1];
}

//------------------------------------------------------------------------------
void vtkDataObjectToDataSetFilter::SetCellConnectivityComponent(
  const char* arrayName, int arrayComp, int min, int max)
{
  vtkFieldDataToAttributeDataFilter::SetArrayName(this, this->CellConnectivityArray, arrayName);
  if (this->CellConnectivityArrayComponent != arrayComp)
  {
    this->CellConnectivityArrayComponent = arrayComp;
    this->Modified();
  }
  if (this->CellConnectivityComponentRange[0] != min)
  {
    this->CellConnectivityComponentRange[0] = min;
    this->Modified();
  }
  if (this->CellConnectivityComponentRange[1] != max)
  {
    this->CellConnectivityComponentRange[1] = max;
    this->Modified();
  }
}

//------------------------------------------------------------------------------
const char* vtkDataObjectToDataSetFilter::GetCellConnectivityComponentArrayName()
{
  return this->CellConnectivityArray;
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetCellConnectivityComponentArrayComponent()
{
  return this->CellConnectivityArrayComponent;
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetCellConnectivityComponentMinRange()
{
  return this->CellConnectivityComponentRange[0];
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::GetCellConnectivityComponentMaxRange()
{
  return this->CellConnectivityComponentRange[1];
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::ConstructCells(vtkDataObject* input, vtkPolyData* pd)
{
  vtkIdType ncells = 0;
  vtkDataArray* fieldArray[4];
  vtkFieldData* fd = input->GetFieldData();

  fieldArray[0] = vtkFieldDataToAttributeDataFilter::GetFieldArray(
    fd, this->VertsArray, this->VertsArrayComponent);
  if (this->VertsArray && fieldArray[0] == nullptr)
  {
    vtkErrorMacro(<< "Can't find array requested for vertices");
    return 0;
  }

  fieldArray[1] = vtkFieldDataToAttributeDataFilter::GetFieldArray(
    fd, this->LinesArray, this->LinesArrayComponent);
  if (this->LinesArray && fieldArray[1] == nullptr)
  {
    vtkErrorMacro(<< "Can't find array requested for lines");
    return 0;
  }

  fieldArray[2] = vtkFieldDataToAttributeDataFilter::GetFieldArray(
    fd, this->PolysArray, this->PolysArrayComponent);
  if (this->PolysArray && fieldArray[2] == nullptr)
  {
    vtkErrorMacro(<< "Can't find array requested for polygons");
    return 0;
  }

  fieldArray[3] = vtkFieldDataToAttributeDataFilter::GetFieldArray(
    fd, this->StripsArray, this->StripsArrayComponent);
  if (this->StripsArray && fieldArray[3] == nullptr)
  {
    vtkErrorMacro(<< "Can't find array requested for triangle strips");
    return 0;
  }

  if (fieldArray[0])
  {
    vtkFieldDataToAttributeDataFilter::UpdateComponentRange(
      fieldArray[0], this->VertsComponentRange);
    vtkCellArray* verts =
      this->ConstructCellArray(fieldArray[0], this->VertsArrayComponent, this->VertsComponentRange);
    if (verts != nullptr)
    {
      pd->SetVerts(verts);
      ncells += verts->GetNumberOfCells();
      verts->Delete();
    }
    this->VertsComponentRange[0] = this->VertsComponentRange[1] = -1;
  }

  if (fieldArray[1])
  {
    vtkFieldDataToAttributeDataFilter::UpdateComponentRange(
      fieldArray[1], this->LinesComponentRange);
    vtkCellArray* lines =
      this->ConstructCellArray(fieldArray[1], this->LinesArrayComponent, this->LinesComponentRange);
    if (lines != nullptr)
    {
      pd->SetLines(lines);
      ncells += lines->GetNumberOfCells();
      lines->Delete();
    }
    this->LinesComponentRange[0] = this->LinesComponentRange[1] = -1;
  }

  if (fieldArray[2])
  {
    vtkFieldDataToAttributeDataFilter::UpdateComponentRange(
      fieldArray[2], this->PolysComponentRange);
    vtkCellArray* polys =
      this->ConstructCellArray(fieldArray[2], this->PolysArrayComponent, this->PolysComponentRange);
    if (polys != nullptr)
    {
      pd->SetPolys(polys);
      ncells += polys->GetNumberOfCells();
      polys->Delete();
    }
    this->PolysComponentRange[0] = this->PolysComponentRange[1] = -1;
  }

  if (fieldArray[3])
  {
    vtkFieldDataToAttributeDataFilter::UpdateComponentRange(
      fieldArray[3], this->StripsComponentRange);
    vtkCellArray* triStrips = this->ConstructCellArray(
      fieldArray[3], this->StripsArrayComponent, this->StripsComponentRange);
    if (triStrips != nullptr)
    {
      pd->SetStrips(triStrips);
      ncells += triStrips->GetNumberOfCells();
      triStrips->Delete();
    }
    this->StripsComponentRange[0] = this->StripsComponentRange[1] = -1;
  }

  return ncells;
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::ConstructCells(vtkDataObject* input, vtkUnstructuredGrid* ug)
{
  int i, *types, typesAllocated = 0;
  vtkDataArray* fieldArray[2];
  int ncells;
  vtkFieldData* fd = input->GetFieldData();

  fieldArray[0] = vtkFieldDataToAttributeDataFilter::GetFieldArray(
    fd, this->CellTypeArray, this->CellTypeArrayComponent);

  if (fieldArray[0] == nullptr)
  {
    vtkErrorMacro(<< "Can't find array requested for cell types");
    return 0;
  }

  vtkFieldDataToAttributeDataFilter::UpdateComponentRange(
    fieldArray[0], this->CellTypeComponentRange);
  ncells = this->CellTypeComponentRange[1] - this->CellTypeComponentRange[0] + 1;

  fieldArray[1] = vtkFieldDataToAttributeDataFilter::GetFieldArray(
    fd, this->CellConnectivityArray, this->CellConnectivityArrayComponent);
  if (fieldArray[1] == nullptr)
  {
    vtkErrorMacro(<< "Can't find array requested for cell connectivity");
    return 0;
  }

  // Okay, let's piece it together
  if (fieldArray[0]) // cell types defined
  {
    // first we create the integer array of types
    vtkDataArray* da = fieldArray[0];

    if (da->GetDataType() == VTK_INT && da->GetNumberOfComponents() == 1 &&
      this->CellTypeArrayComponent == 0 && this->CellTypeComponentRange[0] == 0 &&
      this->CellTypeComponentRange[1] == da->GetMaxId())
    {
      types = static_cast<vtkIntArray*>(da)->GetPointer(0);
    }
    // Otherwise, we'll copy the data by inserting it into a vtkCellArray
    else
    {
      typesAllocated = 1;
      types = new int[ncells];
      for (i = this->CellTypeComponentRange[0]; i <= this->CellTypeComponentRange[1]; i++)
      {
        types[i] = static_cast<int>(da->GetComponent(i, this->CellTypeArrayComponent));
      }
    }
    this->CellTypeComponentRange[0] = this->CellTypeComponentRange[1] = -1;

    // create connectivity
    if (fieldArray[1]) // cell connectivity defined
    {
      vtkFieldDataToAttributeDataFilter::UpdateComponentRange(
        fieldArray[1], this->CellConnectivityComponentRange);
      vtkCellArray* carray = this->ConstructCellArray(
        fieldArray[1], this->CellConnectivityArrayComponent, this->CellConnectivityComponentRange);
      if (carray != nullptr) // insert into unstructured grid
      {
        ug->SetCells(types, carray);
        carray->Delete();
      }
      this->CellConnectivityComponentRange[0] = this->CellConnectivityComponentRange[1] = -1;
    }
    if (typesAllocated)
    {
      delete[] types;
    }
  }

  return ncells;
}

//------------------------------------------------------------------------------
vtkCellArray* vtkDataObjectToDataSetFilter::ConstructCellArray(
  vtkDataArray* da, int comp, vtkIdType compRange[2])
{
  int j, min, max, numComp = da->GetNumberOfComponents();
  vtkCellArray* carray;
  vtkIdType npts, ncells, i;

  min = 0;
  max = da->GetMaxId();

  if (comp < 0 || comp >= numComp)
  {
    vtkErrorMacro(<< "Bad component specification");
    return nullptr;
  }

  carray = vtkCellArray::New();

  // If the data type is vtkIdType, and the number of components is 1, then
  // we can directly use the data array without copying it. We just have to
  // figure out how many cells we have.
  if (da->GetDataType() == VTK_ID_TYPE && da->GetNumberOfComponents() == 1 && comp == 0 &&
    compRange[0] == 0 && compRange[1] == max)
  {
    vtkIdTypeArray* ia = static_cast<vtkIdTypeArray*>(da);
    for (ncells = i = 0; i < ia->GetMaxId(); i += (npts + 1))
    {
      ncells++;
      npts = ia->GetValue(i);
    }
    carray->AllocateExact(ncells, ia->GetNumberOfValues() - ncells);
    carray->ImportLegacyFormat(ia);
  }
  // Otherwise, we'll copy the data by inserting it into a vtkCellArray
  else
  {
    for (i = min; i < max; i += (npts + 1))
    {
      npts = static_cast<int>(da->GetComponent(i, comp));
      if (npts <= 0)
      {
        vtkErrorMacro(<< "Error constructing cell array");
        carray->Delete();
        return nullptr;
      }
      else
      {
        carray->InsertNextCell(npts);
        for (j = 1; j <= npts; j++)
        {
          carray->InsertCellPoint(static_cast<int>(da->GetComponent(i + j, comp)));
        }
      }
    }
  }

  return carray;
}

//------------------------------------------------------------------------------
// Alternative methods for Dimensions, Spacing, and Origin -------------------
//
void vtkDataObjectToDataSetFilter::SetDimensionsComponent(
  const char* arrayName, int arrayComp, int min, int max)
{
  vtkFieldDataToAttributeDataFilter::SetArrayName(this, this->DimensionsArray, arrayName);
  if (this->DimensionsArrayComponent != arrayComp)
  {
    this->DimensionsArrayComponent = arrayComp;
    this->Modified();
  }
  if (this->DimensionsComponentRange[0] != min)
  {
    this->DimensionsComponentRange[0] = min;
    this->Modified();
  }
  if (this->DimensionsComponentRange[1] != max)
  {
    this->DimensionsComponentRange[1] = max;
    this->Modified();
  }
}

//------------------------------------------------------------------------------
void vtkDataObjectToDataSetFilter::SetSpacingComponent(
  const char* arrayName, int arrayComp, int min, int max)
{
  vtkFieldDataToAttributeDataFilter::SetArrayName(this, this->SpacingArray, arrayName);
  if (this->SpacingArrayComponent != arrayComp)
  {
    this->SpacingArrayComponent = arrayComp;
    this->Modified();
  }
  if (this->SpacingComponentRange[0] != min)
  {
    this->SpacingComponentRange[0] = min;
    this->Modified();
  }
  if (this->SpacingComponentRange[1] != max)
  {
    this->SpacingComponentRange[1] = max;
    this->Modified();
  }
}

//------------------------------------------------------------------------------
void vtkDataObjectToDataSetFilter::SetOriginComponent(
  const char* arrayName, int arrayComp, int min, int max)
{
  vtkFieldDataToAttributeDataFilter::SetArrayName(this, this->OriginArray, arrayName);
  if (this->OriginArrayComponent != arrayComp)
  {
    this->OriginArrayComponent = arrayComp;
    this->Modified();
  }
  if (this->OriginComponentRange[0] != min)
  {
    this->OriginComponentRange[0] = min;
    this->Modified();
  }
  if (this->OriginComponentRange[1] != max)
  {
    this->OriginComponentRange[1] = max;
    this->Modified();
  }
}

//------------------------------------------------------------------------------
void vtkDataObjectToDataSetFilter::ConstructDimensions(vtkDataObject* input)
{
  if (this->DimensionsArray == nullptr || this->DimensionsArrayComponent < 0)
  {
    return; // assume dimensions have been set
  }
  else
  {
    vtkFieldData* fd = input->GetFieldData();
    vtkDataArray* fieldArray = vtkFieldDataToAttributeDataFilter::GetFieldArray(
      fd, this->DimensionsArray, this->DimensionsArrayComponent);
    if (fieldArray == nullptr)
    {
      vtkErrorMacro(<< "Can't find array requested for dimensions");
      return;
    }

    vtkFieldDataToAttributeDataFilter::UpdateComponentRange(
      fieldArray, this->DimensionsComponentRange);

    for (int i = 0; i < 3; i++)
    {
      this->Dimensions[i] = static_cast<int>(fieldArray->GetComponent(
        this->DimensionsComponentRange[0] + i, this->DimensionsArrayComponent));
    }
  }

  this->DimensionsComponentRange[0] = this->DimensionsComponentRange[1] = -1;
}

//------------------------------------------------------------------------------
void vtkDataObjectToDataSetFilter::ConstructSpacing(vtkDataObject* input)
{
  if (this->SpacingArray == nullptr || this->SpacingArrayComponent < 0)
  {
    return; // assume Spacing have been set
  }
  else
  {
    vtkFieldData* fd = input->GetFieldData();
    vtkDataArray* fieldArray = vtkFieldDataToAttributeDataFilter::GetFieldArray(
      fd, this->SpacingArray, this->SpacingArrayComponent);
    if (fieldArray == nullptr)
    {
      vtkErrorMacro(<< "Can't find array requested for Spacing");
      return;
    }

    vtkFieldDataToAttributeDataFilter::UpdateComponentRange(
      fieldArray, this->SpacingComponentRange);

    for (int i = 0; i < 3; i++)
    {
      this->Spacing[i] =
        fieldArray->GetComponent(this->SpacingComponentRange[0] + i, this->SpacingArrayComponent);
    }
  }
  this->SpacingComponentRange[0] = this->SpacingComponentRange[1] = -1;
}

//------------------------------------------------------------------------------
vtkDataSet* vtkDataObjectToDataSetFilter::GetOutput(int idx)
{
  return vtkDataSet::SafeDownCast(this->GetExecutive()->GetOutputData(idx));
}

//------------------------------------------------------------------------------
void vtkDataObjectToDataSetFilter::ConstructOrigin(vtkDataObject* input)
{
  if (this->OriginArray == nullptr || this->OriginArrayComponent < 0)
  {
    return; // assume Origin have been set
  }
  else
  {
    vtkFieldData* fd = input->GetFieldData();
    vtkDataArray* fieldArray = vtkFieldDataToAttributeDataFilter::GetFieldArray(
      fd, this->OriginArray, this->OriginArrayComponent);
    if (fieldArray == nullptr)
    {
      vtkErrorMacro(<< "Can't find array requested for Origin");
      return;
    }

    vtkFieldDataToAttributeDataFilter::UpdateComponentRange(fieldArray, this->OriginComponentRange);

    for (int i = 0; i < 3; i++)
    {
      this->Origin[i] =
        fieldArray->GetComponent(this->OriginComponentRange[0] + i, this->OriginArrayComponent);
    }
  }

  this->OriginComponentRange[0] = this->OriginComponentRange[1] = -1;
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::FillInputPortInformation(int, vtkInformation* info)
{
  info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataObject");
  return 1;
}

//------------------------------------------------------------------------------
int vtkDataObjectToDataSetFilter::RequestDataObject(
  vtkInformation*, vtkInformationVector**, vtkInformationVector* outputVector)
{
  vtkInformation* outInfo = outputVector->GetInformationObject(0);
  vtkDataSet* output = vtkDataSet::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));
  if (!output || (output->GetDataObjectType() != this->DataSetType))
  {
    switch (this->DataSetType)
    {
      case VTK_POLY_DATA:
        output = vtkPolyData::New();
        break;
      case VTK_STRUCTURED_GRID:
        output = vtkStructuredGrid::New();
        break;
      case VTK_STRUCTURED_POINTS:
        output = vtkStructuredPoints::New();
        break;
      case VTK_UNSTRUCTURED_GRID:
        output = vtkUnstructuredGrid::New();
        break;
      case VTK_RECTILINEAR_GRID:
        output = vtkRectilinearGrid::New();
        break;
      default:
        vtkWarningMacro("unknown DataSetType");
    }
    if (output)
    {
      outInfo->Set(vtkDataObject::DATA_OBJECT(), output);
      output->Delete();
    }
  }
  return 1;
}
