// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#include "vtkXYPlotActor.h"

#include "vtkAlgorithm.h"
#include "vtkAlgorithmOutput.h"
#include "vtkAppendPolyData.h"
#include "vtkAxisActor2D.h"
#include "vtkCellArray.h"
#include "vtkDataObjectCollection.h"
#include "vtkDataSetCollection.h"
#include "vtkDoubleArray.h"
#include "vtkFieldData.h"
#include "vtkGlyph2D.h"
#include "vtkGlyphSource2D.h"
#include "vtkIntArray.h"
#include "vtkLegendBoxActor.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkPlane.h"
#include "vtkPlanes.h"
#include "vtkPointData.h"
#include "vtkPolyData.h"
#include "vtkPolyDataMapper2D.h"
#include "vtkProperty2D.h"
#include "vtkStringFormatter.h"
#include "vtkTextActor.h"
#include "vtkTextMapper.h"
#include "vtkTextProperty.h"
#include "vtkTrivialProducer.h"
#include "vtkViewport.h"

#include <algorithm>

#define VTK_MAX_PLOTS 50

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkXYPlotActor);

vtkCxxSetObjectMacro(vtkXYPlotActor, TitleTextProperty, vtkTextProperty);
vtkCxxSetObjectMacro(vtkXYPlotActor, AxisLabelTextProperty, vtkTextProperty);

class vtkXYPlotActorConnections : public vtkAlgorithm
{
public:
  static vtkXYPlotActorConnections* New();
  vtkTypeMacro(vtkXYPlotActorConnections, vtkAlgorithm);

  vtkXYPlotActorConnections() { this->SetNumberOfInputPorts(1); }
};

vtkStandardNewMacro(vtkXYPlotActorConnections);

//------------------------------------------------------------------------------
// Instantiate object
vtkXYPlotActor::vtkXYPlotActor()
{
  this->PositionCoordinate->SetCoordinateSystemToNormalizedViewport();
  this->PositionCoordinate->SetValue(.25, .25);
  this->Position2Coordinate->SetValue(.5, .5);

  this->InputConnectionHolder = vtkXYPlotActorConnections::New();
  this->SelectedInputScalars = nullptr;
  this->SelectedInputScalarsComponent = vtkIntArray::New();

  this->DataObjectInputConnectionHolder = vtkXYPlotActorConnections::New();

  this->Title = nullptr;
  this->XTitle = new char[7];
  auto result = vtk::format_to_n(this->XTitle, 7, "{:s}", "X Axis");
  *result.out = '\0';

  this->YTitleActor = vtkTextActor::New();
  this->YTitleActor->SetInput("Y Axis");
  this->YTitleActor->GetPositionCoordinate()->SetCoordinateSystemToViewport();
  this->YTitleActor->GetPosition2Coordinate()->SetCoordinateSystemToViewport();

  this->YTitlePosition = VTK_XYPLOT_Y_AXIS_HCENTER;
  this->YTitleDelta = 0;

  this->XValues = VTK_XYPLOT_INDEX;

  this->NumberOfXLabels = 5;
  this->NumberOfYLabels = 5;

  this->TitleTextProperty = vtkTextProperty::New();
  this->TitleTextProperty->SetBold(1);
  this->TitleTextProperty->SetItalic(1);
  this->TitleTextProperty->SetShadow(1);
  this->TitleTextProperty->SetFontFamilyToArial();

  this->AxisLabelTextProperty = vtkTextProperty::New();
  this->AxisLabelTextProperty->SetBold(0);
  this->AxisLabelTextProperty->SetItalic(1);
  this->AxisLabelTextProperty->SetShadow(1);
  this->AxisLabelTextProperty->SetFontFamilyToArial();

  this->AxisTitleTextProperty = vtkTextProperty::New();
  this->AxisTitleTextProperty->SetBold(0);
  this->AxisTitleTextProperty->SetItalic(1);
  this->AxisTitleTextProperty->SetShadow(1);
  this->AxisTitleTextProperty->SetFontFamilyToArial();

  this->XLabelFormat = new char[10];
  result = vtk::format_to_n(this->XLabelFormat, 10, "{:s}", "{:<#6.3g}");
  *result.out = '\0';

  this->YLabelFormat = new char[10];
  result = vtk::format_to_n(this->YLabelFormat, 10, "{:s}", "{:<#6.3g}");
  *result.out = '\0';

  this->Logx = 0;

  this->XRange[0] = 0.;
  this->XRange[1] = 0.;
  this->YRange[0] = 0.;
  this->YRange[1] = 0.;

  this->Border = 5;
  this->PlotLines = 1;
  this->PlotPoints = 0;
  this->PlotCurveLines = 0;
  this->PlotCurvePoints = 0;
  this->ExchangeAxes = 0;
  this->ReverseXAxis = 0;
  this->ReverseYAxis = 0;

  this->TitleMapper = vtkTextMapper::New();
  this->TitleActor = vtkActor2D::New();
  this->TitleActor->SetMapper(this->TitleMapper);
  this->TitleActor->GetPositionCoordinate()->SetCoordinateSystemToViewport();

  this->XAxis->GetPositionCoordinate()->SetCoordinateSystemToViewport();
  this->XAxis->GetPosition2Coordinate()->SetCoordinateSystemToViewport();
  this->XAxis->SetProperty(this->GetProperty());

  this->YAxis->GetPositionCoordinate()->SetCoordinateSystemToViewport();
  this->YAxis->GetPosition2Coordinate()->SetCoordinateSystemToViewport();
  this->YAxis->SetProperty(this->GetProperty());

  this->NumberOfInputs = 0;
  this->PlotData = nullptr;
  this->PlotGlyph = nullptr;
  this->PlotAppend = nullptr;
  this->PlotMapper = nullptr;
  this->PlotActor = nullptr;

  this->ViewportCoordinate[0] = 0.;
  this->ViewportCoordinate[1] = 0.;
  this->PlotCoordinate[0] = 0.;
  this->PlotCoordinate[1] = 0.;

  this->DataObjectPlotMode = VTK_XYPLOT_COLUMN;
  this->XComponent = vtkIntArray::New();
  this->XComponent->SetNumberOfValues(VTK_MAX_PLOTS);
  this->YComponent = vtkIntArray::New();
  this->YComponent->SetNumberOfValues(VTK_MAX_PLOTS);

  this->LinesOn = vtkIntArray::New();
  this->LinesOn->SetNumberOfValues(VTK_MAX_PLOTS);
  this->PointsOn = vtkIntArray::New();
  this->PointsOn->SetNumberOfValues(VTK_MAX_PLOTS);
  for (int i = 0; i < VTK_MAX_PLOTS; i++)
  {
    this->XComponent->SetValue(i, 0);
    this->YComponent->SetValue(i, 0);
    this->LinesOn->SetValue(i, this->PlotLines);
    this->PointsOn->SetValue(i, this->PlotPoints);
  }

  this->Legend = 0;
  this->LegendPosition[0] = .85;
  this->LegendPosition[1] = .75;
  this->LegendPosition2[0] = .15;
  this->LegendPosition2[1] = .20;
  this->LegendActor->GetPositionCoordinate()->SetCoordinateSystemToViewport();
  this->LegendActor->GetPosition2Coordinate()->SetCoordinateSystemToViewport();
  this->LegendActor->GetPosition2Coordinate()->SetReferenceCoordinate(nullptr);
  this->LegendActor->BorderOff();
  this->LegendActor->SetNumberOfEntries(VTK_MAX_PLOTS); // initial allocation
  this->GlyphSource = vtkGlyphSource2D::New();
  this->GlyphSource->SetGlyphTypeToNone();
  this->GlyphSource->DashOn();
  this->GlyphSource->FilledOff();
  this->GlyphSource->Update();
  this->GlyphSize = 0.020;

  this->ClipPlanes = vtkPlanes::New();
  vtkPoints* pts = vtkPoints::New();
  pts->SetNumberOfPoints(4);
  this->ClipPlanes->SetPoints(pts);
  pts->Delete();
  vtkDoubleArray* n = vtkDoubleArray::New();
  n->SetNumberOfComponents(3);
  n->SetNumberOfTuples(4);
  this->ClipPlanes->SetNormals(n);
  n->Delete();

  // Construct the box
  this->ChartBox = 0;
  this->ChartBoxPolyData = vtkPolyData::New();
  vtkPoints* points = vtkPoints::New();
  points->SetNumberOfPoints(4);
  this->ChartBoxPolyData->SetPoints(points);
  points->Delete();
  vtkCellArray* polys = vtkCellArray::New();
  polys->InsertNextCell(4);
  polys->InsertCellPoint(0);
  polys->InsertCellPoint(1);
  polys->InsertCellPoint(2);
  polys->InsertCellPoint(3);
  this->ChartBoxPolyData->SetPolys(polys);
  polys->Delete();
  this->ChartBoxMapper = vtkPolyDataMapper2D::New();
  this->ChartBoxMapper->SetInputData(this->ChartBoxPolyData);
  this->ChartBoxActor = vtkActor2D::New();
  this->ChartBoxActor->SetMapper(this->ChartBoxMapper);

  // Box border
  this->ChartBorder = 0;
  this->ChartBorderPolyData = vtkPolyData::New();
  this->ChartBorderPolyData->SetPoints(points);
  vtkCellArray* lines = vtkCellArray::New();
  lines->InsertNextCell(5);
  lines->InsertCellPoint(0);
  lines->InsertCellPoint(1);
  lines->InsertCellPoint(2);
  lines->InsertCellPoint(3);
  lines->InsertCellPoint(0);
  this->ChartBorderPolyData->SetLines(lines);
  lines->Delete();
  this->ChartBorderMapper = vtkPolyDataMapper2D::New();
  this->ChartBorderMapper->SetInputData(this->ChartBorderPolyData);
  this->ChartBorderActor = vtkActor2D::New();
  this->ChartBorderActor->SetMapper(this->ChartBorderMapper);

  // Reference lines
  this->ShowReferenceXLine = 0;
  this->ShowReferenceYLine = 0;
  this->ReferenceXValue = 0.;
  this->ReferenceYValue = 0.;
  points = vtkPoints::New();
  points->SetNumberOfPoints(4);
  lines = vtkCellArray::New();
  lines->InsertNextCell(2);
  lines->InsertCellPoint(0);
  lines->InsertCellPoint(1);
  lines->InsertNextCell(2);
  lines->InsertCellPoint(2);
  lines->InsertCellPoint(3);
  this->ReferenceLinesPolyData = vtkPolyData::New();
  this->ReferenceLinesPolyData->SetPoints(points);
  this->ReferenceLinesPolyData->SetLines(lines);
  points->Delete();
  lines->Delete();
  this->ReferenceLinesMapper = vtkPolyDataMapper2D::New();
  this->ReferenceLinesMapper->SetInputData(this->ReferenceLinesPolyData);
  this->ReferenceLinesActor = vtkActor2D::New();
  this->ReferenceLinesActor->SetMapper(this->ReferenceLinesMapper);

  this->CachedSize[0] = 0;
  this->CachedSize[1] = 0;

  this->AdjustXLabels = 1;
  this->AdjustYLabels = 1;
  this->AdjustTitlePosition = 1;
  this->TitlePosition[0] = .5;
  this->TitlePosition[1] = .9;
  this->AdjustTitlePositionMode = vtkXYPlotActor::AlignHCenter | vtkXYPlotActor::AlignTop |
    vtkXYPlotActor::AlignAxisHCenter | vtkXYPlotActor::AlignAxisVCenter;
}

//------------------------------------------------------------------------------
vtkXYPlotActor::~vtkXYPlotActor()
{
  // Get rid of the list of array names.
  int num = this->InputConnectionHolder->GetNumberOfInputConnections(0);
  if (this->SelectedInputScalars)
  {
    for (int i = 0; i < num; ++i)
    {
      delete[] this->SelectedInputScalars[i];
      this->SelectedInputScalars[i] = nullptr;
    }
    delete[] this->SelectedInputScalars;
    this->SelectedInputScalars = nullptr;
  }
  this->SelectedInputScalarsComponent->Delete();
  this->SelectedInputScalarsComponent = nullptr;

  //  Now we can get rid of the inputs.
  this->InputConnectionHolder->Delete();
  this->InputConnectionHolder = nullptr;

  this->DataObjectInputConnectionHolder->Delete();

  this->TitleMapper->Delete();
  this->TitleMapper = nullptr;
  this->TitleActor->Delete();
  this->TitleActor = nullptr;

  this->SetTitle(nullptr);
  this->SetXTitle(nullptr);
  this->SetXLabelFormat(nullptr);
  this->SetYLabelFormat(nullptr);

  this->InitializeEntries();

  this->GlyphSource->Delete();
  this->ClipPlanes->Delete();

  this->ChartBoxActor->Delete();
  this->ChartBoxMapper->Delete();
  this->ChartBoxPolyData->Delete();

  this->ChartBorderActor->Delete();
  this->ChartBorderMapper->Delete();
  this->ChartBorderPolyData->Delete();

  this->ReferenceLinesActor->Delete();
  this->ReferenceLinesMapper->Delete();
  this->ReferenceLinesPolyData->Delete();

  this->XComponent->Delete();
  this->YComponent->Delete();

  this->LinesOn->Delete();
  this->PointsOn->Delete();

  this->TitleTextProperty->Delete();
  this->TitleTextProperty = nullptr;
  this->AxisLabelTextProperty->Delete();
  this->AxisLabelTextProperty = nullptr;
  this->AxisTitleTextProperty->Delete();
  this->AxisTitleTextProperty = nullptr;

  this->YTitleActor->Delete();
  this->YTitleActor = nullptr;
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::InitializeEntries()
{
  if (this->NumberOfInputs > 0)
  {
    for (int i = 0; i < this->NumberOfInputs; i++)
    {
      this->PlotData[i]->Delete();
      this->PlotGlyph[i]->Delete();
      this->PlotAppend[i]->Delete();
      this->PlotMapper[i]->Delete();
      this->PlotActor[i]->Delete();
    } // for all entries

    delete[] this->PlotData;
    this->PlotData = nullptr;

    delete[] this->PlotGlyph;
    this->PlotGlyph = nullptr;

    delete[] this->PlotAppend;
    this->PlotAppend = nullptr;

    delete[] this->PlotMapper;
    this->PlotMapper = nullptr;

    delete[] this->PlotActor;
    this->PlotActor = nullptr;
    this->NumberOfInputs = 0;
  } // if entries have been defined
}

//------------------------------------------------------------------------------
bool vtkXYPlotActor::DoesConnectionMatch(int i, vtkAlgorithmOutput* in)
{
  vtkAlgorithmOutput* conn = this->InputConnectionHolder->GetInputConnection(0, i);
  return conn->GetProducer() == in->GetProducer() && conn->GetIndex() == in->GetIndex();
}

//------------------------------------------------------------------------------
int vtkXYPlotActor::IsInputPresent(vtkAlgorithmOutput* in, const char* arrayName, int component)
{
  int numConns = this->InputConnectionHolder->GetNumberOfInputConnections(0);
  for (int idx = 0; idx < numConns; idx++)
  {
    if (this->DoesConnectionMatch(idx, in))
    {
      if (arrayName == nullptr && this->SelectedInputScalars[idx] == nullptr &&
        component == this->SelectedInputScalarsComponent->GetValue(idx))
      {
        return idx + 1;
      }
      if (arrayName != nullptr && this->SelectedInputScalars[idx] != nullptr &&
        strcmp(arrayName, this->SelectedInputScalars[idx]) == 0 &&
        component == this->SelectedInputScalarsComponent->GetValue(idx))
      {
        return idx + 1;
      }
    }
  }
  return 0;
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::AddDataSetInput(vtkDataSet* ds, const char* arrayName, int component)
{
  vtkTrivialProducer* tp = vtkTrivialProducer::New();
  tp->SetOutput(ds);
  this->AddDataSetInputConnection(tp->GetOutputPort(), arrayName, component);
  tp->Delete();
}

//------------------------------------------------------------------------------
// Add a dataset and array to the list of data to plot.
void vtkXYPlotActor::AddDataSetInputConnection(
  vtkAlgorithmOutput* in, const char* arrayName, int component)
{
  int idx, num;
  char** newNames;

  // I cannot change the input list, because the user has direct
  // access to the collection.  I cannot store the index of the array,
  // because the index might change from render to render ...
  // I have to store the list of string array names.

  idx = this->IsInputPresent(in, arrayName, component);
  // idx starts at 1 and goes to "NumberOfItems".
  if (idx != 0)
  {
    return;
  }

  // The input/array/component must be a unique combination.  Add it to our input list.

  // Now reallocate the list of strings and add the new value.
  num = this->InputConnectionHolder->GetNumberOfInputConnections(0);
  newNames = new char*[num + 1];
  for (idx = 0; idx < num; ++idx)
  {
    newNames[idx] = this->SelectedInputScalars[idx];
  }
  if (arrayName == nullptr)
  {
    newNames[num] = nullptr;
  }
  else
  {
    newNames[num] = new char[strlen(arrayName) + 1];
    strcpy(newNames[num], arrayName);
  }
  delete[] this->SelectedInputScalars;
  this->SelectedInputScalars = newNames;

  // Save the component in the int array.
  this->SelectedInputScalarsComponent->InsertValue(num, component);

  // Add the data set to the collection
  this->InputConnectionHolder->AddInputConnection(0, in);

  // In case of multiple use of a XYPlotActor the NumberOfEntries could be set
  // to n. Then when a call to SetEntryString( n+1, bla ) was done the string was lost
  // Need to update the number of entries for the legend actor
  this->LegendActor->SetNumberOfEntries(this->LegendActor->GetNumberOfEntries() + 1);

  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::RemoveAllDataSetInputConnections()
{
  int idx, num;

  num = this->InputConnectionHolder->GetNumberOfInputConnections(0);
  this->InputConnectionHolder->RemoveAllInputs();

  for (idx = 0; idx < num; ++idx)
  {
    delete[] this->SelectedInputScalars[idx];
    this->SelectedInputScalars[idx] = nullptr;
  }
  this->SelectedInputScalarsComponent->Reset();

  this->DataObjectInputConnectionHolder->RemoveAllInputs();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::RemoveDataSetInput(vtkDataSet* ds, const char* arrayName, int component)
{
  int numConns = this->InputConnectionHolder->GetNumberOfInputConnections(0);
  for (int idx = 0; idx < numConns; idx++)
  {
    vtkAlgorithmOutput* aout = this->InputConnectionHolder->GetInputConnection(0, idx);
    vtkAlgorithm* alg = aout ? aout->GetProducer() : nullptr;
    if (alg)
    {
      if (ds == alg->GetOutputDataObject(aout->GetIndex()))
      {
        this->RemoveDataSetInputConnection(aout, arrayName, component);
        return;
      }
    }
  }
}

//------------------------------------------------------------------------------
// Remove a dataset from the list of data to plot.
void vtkXYPlotActor::RemoveDataSetInputConnection(
  vtkAlgorithmOutput* in, const char* arrayName, int component)
{
  // IsInputPresent returns 0 on failure, index+1 on success.
  // Subtract 1 for the actual index.
  int found = this->IsInputPresent(in, arrayName, component) - 1;
  if (found == -1)
  {
    return;
  }

  this->Modified();

  int num = this->InputConnectionHolder->GetNumberOfInputConnections(0);

  this->InputConnectionHolder->RemoveInputConnection(0, found);

  // Do not bother reallocating the SelectedInputScalars
  // string array to make it smaller.
  delete[] this->SelectedInputScalars[found];
  this->SelectedInputScalars[found] = nullptr;
  for (int idx = found + 1; idx < num; ++idx)
  {
    this->SelectedInputScalars[idx - 1] = this->SelectedInputScalars[idx];
    this->SelectedInputScalarsComponent->SetValue(
      idx - 1, this->SelectedInputScalarsComponent->GetValue(idx));
  }
  // Resetting the last item is not really necessary,
  // but to be clean we do it anyway.
  this->SelectedInputScalarsComponent->SetValue(num - 1, -1);
  this->SelectedInputScalars[num - 1] = nullptr;
}

//------------------------------------------------------------------------------
vtkAlgorithmOutput* vtkXYPlotActor::GetDataSetInputConnection(unsigned int idx)
{
  if (static_cast<int>(idx) <= this->InputConnectionHolder->GetNumberOfInputConnections(0))
  {
    return this->InputConnectionHolder->GetInputConnection(0, idx);
  }
  else
  {
    return nullptr;
  }
}

//------------------------------------------------------------------------------
unsigned int vtkXYPlotActor::GetNumberOfDataSetInputConnections()
{
  return this->InputConnectionHolder->GetNumberOfInputConnections(0);
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::AddDataObjectInputConnection(vtkAlgorithmOutput* aout)
{
  // Return if the connection already exists
  int numDO = this->DataObjectInputConnectionHolder->GetNumberOfInputConnections(0);
  for (int i = 0; i < numDO; i++)
  {
    vtkAlgorithmOutput* port = this->DataObjectInputConnectionHolder->GetInputConnection(0, i);
    if (port == aout)
    {
      return;
    }
  }

  this->DataObjectInputConnectionHolder->AddInputConnection(aout);
}

//------------------------------------------------------------------------------
// Add a data object to the list of data to plot.
void vtkXYPlotActor::AddDataObjectInput(vtkDataObject* in)
{
  vtkTrivialProducer* tp = vtkTrivialProducer::New();
  tp->SetOutput(in);
  this->AddDataObjectInputConnection(tp->GetOutputPort());
  tp->Delete();
}

//------------------------------------------------------------------------------
// Remove a data object from the list of data to plot.
void vtkXYPlotActor::RemoveDataObjectInputConnection(vtkAlgorithmOutput* aout)
{
  int numDO = this->DataObjectInputConnectionHolder->GetNumberOfInputConnections(0);
  for (int i = 0; i < numDO; i++)
  {
    vtkAlgorithmOutput* port = this->DataObjectInputConnectionHolder->GetInputConnection(0, i);
    if (port == aout)
    {
      this->DataObjectInputConnectionHolder->RemoveInputConnection(0, i);
      break;
    }
  }
}

//------------------------------------------------------------------------------
// Remove a data object from the list of data to plot.
void vtkXYPlotActor::RemoveDataObjectInput(vtkDataObject* in)
{
  int numDO = this->DataObjectInputConnectionHolder->GetNumberOfInputConnections(0);
  for (int i = 0; i < numDO; i++)
  {
    vtkAlgorithmOutput* port = this->DataObjectInputConnectionHolder->GetInputConnection(0, i);
    vtkAlgorithm* alg = port->GetProducer();
    int portIdx = port->GetIndex();
    if (alg->GetOutputDataObject(portIdx) == in)
    {
      this->DataObjectInputConnectionHolder->RemoveInputConnection(0, i);
      break;
    }
  }
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::RemoveAllDataObjectInputConnections()
{
  int idx, num;

  num = this->DataObjectInputConnectionHolder->GetNumberOfInputConnections(0);
  this->DataObjectInputConnectionHolder->RemoveAllInputs();

  for (idx = 0; idx < num; ++idx)
  {
    delete[] this->SelectedInputScalars[idx];
    this->SelectedInputScalars[idx] = nullptr;
  }
  this->SelectedInputScalarsComponent->Reset();
}

//------------------------------------------------------------------------------
vtkAlgorithmOutput* vtkXYPlotActor::GetDataObjectInputConnection(unsigned int idx)
{
  if (static_cast<int>(idx) <=
    this->DataObjectInputConnectionHolder->GetNumberOfInputConnections(0))
  {
    return this->DataObjectInputConnectionHolder->GetInputConnection(0, idx);
  }
  else
  {
    return nullptr;
  }
}

//------------------------------------------------------------------------------
unsigned int vtkXYPlotActor::GetNumberOfDataObjectInputConnections()
{
  return this->DataObjectInputConnectionHolder->GetNumberOfInputConnections(0);
}

//------------------------------------------------------------------------------
// Plot scalar data for each input dataset.
int vtkXYPlotActor::RenderOverlay(vtkViewport* viewport)
{
  int renderedSomething = 0;

  // Make sure input is up to date.
  if (this->InputConnectionHolder->GetNumberOfInputConnections(0) < 1 &&
    this->DataObjectInputConnectionHolder->GetNumberOfInputConnections(0) < 1)
  {
    vtkErrorMacro(<< "Nothing to plot!");
    return 0;
  }

  if (this->ChartBox)
  {
    renderedSomething += this->ChartBoxActor->RenderOverlay(viewport);
  }
  if (this->ChartBorder)
  {
    renderedSomething += this->ChartBorderActor->RenderOverlay(viewport);
  }

  renderedSomething += this->XAxis->RenderOverlay(viewport);
  renderedSomething += this->YAxis->RenderOverlay(viewport);
  if (this->Title)
  {
    renderedSomething += this->TitleActor->RenderOverlay(viewport);
  }
  for (int i = 0; i < this->NumberOfInputs; i++)
  {
    renderedSomething += this->PlotActor[i]->RenderOverlay(viewport);
  }
  if (this->ShowReferenceXLine || this->ShowReferenceYLine)
  {
    renderedSomething += this->ReferenceLinesActor->RenderOverlay(viewport);
  }
  if (this->Legend)
  {
    renderedSomething += this->LegendActor->RenderOverlay(viewport);
  }
  if (this->YTitleActor)
  {
    renderedSomething += this->YTitleActor->RenderOverlay(viewport);
  }

  return renderedSomething;
}

//------------------------------------------------------------------------------
// Plot scalar data for each input dataset.
int vtkXYPlotActor::RenderOpaqueGeometry(vtkViewport* viewport)
{
  vtkMTimeType mtime, dsMtime;
  vtkDataObject* dobj;
  int numDS, numDO, renderedSomething = 0;

  // Initialize
  // Make sure input is up to date.
  numDS = this->InputConnectionHolder->GetNumberOfInputConnections(0);
  numDO = this->DataObjectInputConnectionHolder->GetNumberOfInputConnections(0);
  if (numDS > 0)
  {
    vtkDebugMacro(<< "Plotting input data sets");
    mtime = 0;
    for (int i = 0; i < numDS; i++)
    {
      vtkAlgorithmOutput* port = this->InputConnectionHolder->GetInputConnection(0, i);
      vtkAlgorithm* alg = port->GetProducer();
      int portIdx = port->GetIndex();
      alg->Update(portIdx);
      dobj = alg->GetOutputDataObject(portIdx);
      dsMtime = dobj->GetMTime();
      mtime = std::max(dsMtime, mtime);
    }
  }
  else if (numDO > 0)
  {
    vtkDebugMacro(<< "Plotting input data objects");
    mtime = 0;
    for (int i = 0; i < numDO; i++)
    {
      vtkAlgorithmOutput* port = this->DataObjectInputConnectionHolder->GetInputConnection(0, i);
      vtkAlgorithm* alg = port->GetProducer();
      int portIdx = port->GetIndex();
      alg->Update(portIdx);
      dobj = alg->GetOutputDataObject(portIdx);
      dsMtime = dobj->GetMTime();
      mtime = std::max(dsMtime, mtime);
    }
  }
  else
  {
    vtkErrorMacro(<< "Nothing to plot!");
    return 0;
  }

  if (this->Title && this->Title[0] && !this->TitleTextProperty)
  {
    vtkErrorMacro(<< "Need a title text property to render plot title");
    return 0;
  }

  // Check modified time to see whether we have to rebuild.
  // Pay attention that GetMTime() has been redefined ( see below )

  const int* size = viewport->GetSize();
  if (mtime > this->BuildTime || size[0] != this->CachedSize[0] || size[1] != this->CachedSize[1] ||
    this->GetMTime() > this->BuildTime ||
    (this->Title && this->Title[0] && this->TitleTextProperty->GetMTime() > this->BuildTime) ||
    (this->AxisLabelTextProperty && this->AxisLabelTextProperty->GetMTime() > this->BuildTime) ||
    (this->AxisTitleTextProperty && this->AxisTitleTextProperty->GetMTime() > this->BuildTime))
  {
    double range[2], yrange[2], xRange[2], yRange[2], interval, *lengths = nullptr;
    int pos[2], pos2[2], numTicks;
    int stringSize[2];
    int num = (numDS > 0 ? numDS : numDO);

    vtkDebugMacro(<< "Rebuilding plot");
    this->CachedSize[0] = size[0];
    this->CachedSize[1] = size[1];

    // manage legend
    vtkDebugMacro(<< "Rebuilding legend");
    if (this->Legend)
    {
      int legPos[2], legPos2[2];
      int* p1 = this->PositionCoordinate->GetComputedViewportValue(viewport);
      int* p2 = this->Position2Coordinate->GetComputedViewportValue(viewport);
      legPos[0] = (int)(p1[0] + this->LegendPosition[0] * (p2[0] - p1[0]));
      legPos2[0] = (int)(legPos[0] + this->LegendPosition2[0] * (p2[0] - p1[0]));
      legPos[1] = (int)(p1[1] + this->LegendPosition[1] * (p2[1] - p1[1]));
      legPos2[1] = (int)(legPos[1] + this->LegendPosition2[1] * (p2[1] - p1[1]));

      this->LegendActor->GetPositionCoordinate()->SetValue((double)legPos[0], (double)legPos[1]);
      this->LegendActor->GetPosition2Coordinate()->SetValue((double)legPos2[0], (double)legPos2[1]);
      this->LegendActor->SetNumberOfEntries(num);
      for (int i = 0; i < num; i++)
      {
        if (!this->LegendActor->GetEntrySymbol(i))
        {
          this->LegendActor->SetEntrySymbol(i, this->GlyphSource->GetOutput());
        }
        if (!this->LegendActor->GetEntryString(i))
        {
          char legendString[18];
          auto result =
            vtk::format_to_n(legendString, sizeof(legendString), "{:s}{:d}", "Curve ", i);
          *result.out = '\0';
          this->LegendActor->SetEntryString(i, legendString);
        }
      }

      this->LegendActor->SetPadding(2);
      this->LegendActor->GetProperty()->DeepCopy(this->GetProperty());
      this->LegendActor->ScalarVisibilityOff();
    }

    // Rebuild text props
    // Perform shallow copy here since each individual axis can be
    // accessed through the class API ( i.e. each individual axis text prop
    // can be changed ). Therefore, we can not just assign pointers otherwise
    // each individual axis text prop would point to the same text prop.

    if (this->AxisLabelTextProperty && this->AxisLabelTextProperty->GetMTime() > this->BuildTime)
    {
      if (this->XAxis->GetLabelTextProperty())
      {
        this->XAxis->GetLabelTextProperty()->ShallowCopy(this->AxisLabelTextProperty);
      }
      if (this->YAxis->GetLabelTextProperty())
      {
        this->YAxis->GetLabelTextProperty()->ShallowCopy(this->AxisLabelTextProperty);
      }
    }

    if (this->AxisTitleTextProperty && this->AxisTitleTextProperty->GetMTime() > this->BuildTime)
    {
      if (this->XAxis->GetTitleTextProperty())
      {
        this->XAxis->GetTitleTextProperty()->ShallowCopy(this->AxisTitleTextProperty);
      }
      if (this->YAxis->GetTitleTextProperty())
      {
        this->YAxis->GetTitleTextProperty()->ShallowCopy(this->AxisTitleTextProperty);
      }
      if (this->YTitleActor->GetTextProperty())
      {
        this->YTitleActor->GetTextProperty()->ShallowCopy(this->AxisTitleTextProperty);
      }
    }

    // setup x-axis
    vtkDebugMacro(<< "Rebuilding x-axis");

    this->XAxis->SetTitle(this->XTitle);
    this->XAxis->SetNumberOfLabels(this->NumberOfXLabels);
    this->XAxis->SetProperty(this->GetProperty());

    lengths = new double[num];
    if (numDS > 0) // plotting data sets
    {
      this->ComputeXRange(range, lengths);
    }
    else
    {
      this->ComputeDORange(range, yrange, lengths);
    }
    if (this->XRange[0] < this->XRange[1])
    {
      range[0] = this->XRange[0];
      range[1] = this->XRange[1];
    }

    if (this->AdjustXLabels)
    {
      vtkAxisActor2D::ComputeRange(range, xRange, this->NumberOfXLabels, numTicks, interval);
    }
    else
    {
      xRange[0] = range[0];
      xRange[1] = range[1];
    }

    if (!this->ExchangeAxes)
    {
      this->XComputedRange[0] = xRange[0];
      this->XComputedRange[1] = xRange[1];
      if (this->ReverseXAxis)
      {
        this->XAxis->SetRange(range[1], range[0]);
      }
      else
      {
        this->XAxis->SetRange(range[0], range[1]);
      }
    }
    else
    {
      this->XComputedRange[1] = xRange[0];
      this->XComputedRange[0] = xRange[1];
      if (this->ReverseYAxis)
      {
        this->XAxis->SetRange(range[0], range[1]);
      }
      else
      {
        this->XAxis->SetRange(range[1], range[0]);
      }
    }

    // setup y-axis
    vtkDebugMacro(<< "Rebuilding y-axis");
    this->YAxis->SetNumberOfLabels(this->NumberOfYLabels);

    if (this->YRange[0] >= this->YRange[1])
    {
      if (numDS > 0) // plotting data sets
      {
        this->ComputeYRange(yrange);
      }
    }
    else
    {
      yrange[0] = this->YRange[0];
      yrange[1] = this->YRange[1];
    }

    if (this->AdjustYLabels)
    {
      vtkAxisActor2D::ComputeRange(yrange, yRange, this->NumberOfYLabels, numTicks, interval);
    }
    else
    {
      yRange[0] = yrange[0];
      yRange[1] = yrange[1];
    }

    if (!this->ExchangeAxes)
    {
      this->YComputedRange[0] = yRange[0];
      this->YComputedRange[1] = yRange[1];
      if (this->ReverseYAxis)
      {
        this->YAxis->SetRange(yrange[0], yrange[1]);
      }
      else
      {
        this->YAxis->SetRange(yrange[1], yrange[0]);
      }
    }
    else
    {
      this->YComputedRange[1] = yRange[0];
      this->YComputedRange[0] = yRange[1];
      if (this->ReverseXAxis)
      {
        this->YAxis->SetRange(yrange[1], yrange[0]);
      }
      else
      {
        this->YAxis->SetRange(yrange[0], yrange[1]);
      }
    }

    this->PlaceAxes(viewport, size, pos, pos2);

    // Update y axis title position
    // NB: Must be done after call to PlaceAxes() which calculates YTitleSize and YAxisTitleSize
    if (strcmp(this->YTitleActor->GetInput(), "") != 0)
    {
      this->YTitleActor->GetTextProperty()->SetFontSize(this->YAxisTitleSize);

      int* p1 = this->PositionCoordinate->GetComputedViewportValue(viewport);

      // Retrieve lower endpoint of Y axis
      int* yaxis_p1 = this->YAxis->GetPositionCoordinate()->GetComputedViewportValue(viewport);

      // Retrieve upper endpoint of Y axis
      int* yaxis_p2 = this->YAxis->GetPosition2Coordinate()->GetComputedViewportValue(viewport);

      int yaxis = yaxis_p1[1] - yaxis_p2[1];
      int yaxis_ymiddle = (int)(yaxis * .5);
      int ytitle_half_height = (int)(this->YTitleSize[1] * .5);
      int ytitle_width = this->YTitleSize[0];
      int ytitlePos[2];
      ytitlePos[0] = 0;
      ytitlePos[1] = 0;
      switch (this->YTitlePosition)
      {
        case VTK_XYPLOT_Y_AXIS_TOP:
        {
          this->YTitleActor->SetOrientation(0.);
          // Make sure that title does not exceed actor bounds
          int val = yaxis_p1[0] - this->YTitleDelta - ytitle_width;
          ytitlePos[0] = val < p1[0] ? p1[0] : val;
          ytitlePos[1] = yaxis_p1[1] + 10;
          break;
        }
        case VTK_XYPLOT_Y_AXIS_HCENTER:
        {
          this->YTitleActor->SetOrientation(0.);
          // YTitleActor might exceed actor bounds
          ytitlePos[0] = yaxis_p1[0] - this->YTitleDelta - this->YTitleSize[0];
          ytitlePos[1] = yaxis_p2[1] + yaxis_ymiddle - ytitle_half_height;
          break;
        }
        case VTK_XYPLOT_Y_AXIS_VCENTER:
        {
          this->YTitleActor->SetOrientation(90.);
          int val = (int)((yaxis - ytitle_width) * .4);
          ytitlePos[0] = yaxis_p1[0] - this->YTitleDelta;
          ytitlePos[1] = ytitle_width > yaxis ? yaxis_p2[1] : yaxis_p2[1] + val;
          break;
        }
      }
      this->YTitleActor->GetPositionCoordinate()->SetValue(
        (double)ytitlePos[0], (double)ytitlePos[1]);
    }

    // manage title
    if (this->Title != nullptr && this->Title[0])
    {
      this->TitleMapper->SetInput(this->Title);
      if (this->TitleTextProperty->GetMTime() > this->BuildTime)
      {
        this->TitleMapper->GetTextProperty()->ShallowCopy(this->TitleTextProperty);
      }

      vtkTextMapper::SetRelativeFontSize(this->TitleMapper, viewport, size, stringSize, 0.015);

      if (this->AdjustTitlePosition)
      {
        this->TitleActor->GetPositionCoordinate()->SetCoordinateSystemToViewport();
        double titlePos[2];
        switch (this->AdjustTitlePositionMode & (AlignLeft | AlignRight | AlignHCenter))
        {
          default:
          case AlignLeft:
            titlePos[0] = pos[0];
            break;
          case AlignRight:
            titlePos[0] = pos2[0];
            break;
          case AlignHCenter:
            titlePos[0] = pos[0] + .5 * (pos2[0] - pos[0]);
            break;
        }
        switch (this->AdjustTitlePositionMode & (AlignAxisLeft | AlignAxisRight | AlignAxisHCenter))
        {
          case AlignAxisLeft:
            titlePos[0] -= stringSize[0];
            break;
          case AlignAxisRight:
            break;
          case AlignAxisHCenter:
            titlePos[0] -= stringSize[0] / 2;
            break;
          default:
            titlePos[0] -= (this->AdjustTitlePositionMode & AlignLeft) ? stringSize[0] : 0;
            break;
        }
        switch (this->AdjustTitlePositionMode & (AlignTop | AlignBottom | AlignVCenter))
        {
          default:
          case AlignTop:
            titlePos[1] = pos2[1];
            break;
          case AlignBottom:
            titlePos[1] = pos[1];
            break;
          case AlignVCenter:
            titlePos[1] = pos[1] + .5 * (pos2[1] - pos[1]);
        }

        switch (this->AdjustTitlePositionMode & (AlignAxisTop | AlignAxisBottom | AlignAxisVCenter))
        {
          case AlignAxisTop:
            titlePos[1] +=
              (this->AdjustTitlePositionMode & AlignTop) ? this->Border : -this->Border;
            break;
          case AlignAxisBottom:
            titlePos[1] -= stringSize[1];
            break;
          case AlignAxisVCenter:
            titlePos[1] -= stringSize[1] / 2;
            break;
          default:
            titlePos[1] += (this->AdjustTitlePositionMode & AlignTop) ? stringSize[1] : 0;
            break;
        }
        this->TitleActor->GetPositionCoordinate()->SetValue(titlePos[0], titlePos[1]);
        // this->TitleActor->GetPositionCoordinate()->SetValue(
        //  pos[0] + .5 * ( pos2[0] - pos[0] ) - stringSize[0] / 2.0,
        //  pos2[1] - stringSize[1] / 2.0 );
      }
      else
      {
        this->TitleActor->GetPositionCoordinate()->SetCoordinateSystemToNormalizedViewport();
        this->TitleActor->GetPositionCoordinate()->SetValue(
          this->TitlePosition[0], this->TitlePosition[1]);
      }
    }

    // Border and box - may adjust spacing based on font size relationship
    // to the proportions relative to the border
    //
    if (this->ChartBox || this->ChartBorder)
    {
      double doubleP1[3], doubleP2[3];

      doubleP1[0] = static_cast<double>(pos[0]);
      doubleP1[1] = static_cast<double>(pos[1]);
      doubleP1[2] = 0.;
      doubleP2[0] = static_cast<double>(pos2[0]);
      doubleP2[1] = static_cast<double>(pos2[1]);
      doubleP2[2] = 0.;

      vtkPoints* pts = this->ChartBoxPolyData->GetPoints();
      pts->SetPoint(0, doubleP1);
      pts->SetPoint(1, doubleP2[0], doubleP1[1], 0.0);
      pts->SetPoint(2, doubleP2);
      pts->SetPoint(3, doubleP1[0], doubleP2[1], 0.0);

      this->ChartBorderActor->SetProperty(this->GetProperty());
    }
    // Reference lines
    if (this->ShowReferenceXLine || this->ShowReferenceYLine)
    {
      double doubleP1[3], doubleP2[3];

      doubleP1[0] = static_cast<double>(pos[0]);
      doubleP1[1] = static_cast<double>(pos[1]);
      doubleP1[2] = 0.;
      doubleP2[0] = static_cast<double>(pos2[0]);
      doubleP2[1] = static_cast<double>(pos2[1]);
      doubleP2[2] = 0.;

      vtkPoints* pts = this->ReferenceLinesPolyData->GetPoints();
      if (this->ShowReferenceXLine && this->ReferenceXValue >= xRange[0] &&
        this->ReferenceXValue < xRange[1])
      {
        double xRefPos = doubleP1[0] +
          (this->ReferenceXValue - xRange[0]) / (xRange[1] - xRange[0]) *
            (doubleP2[0] - doubleP1[0]);
        pts->SetPoint(0, xRefPos, doubleP1[1], 0.0);
        pts->SetPoint(1, xRefPos, doubleP2[1], 0.0);
      }
      else
      {
        pts->SetPoint(0, doubleP1);
        pts->SetPoint(1, doubleP1);
      }
      if (this->ShowReferenceYLine && this->ReferenceYValue >= yRange[0] &&
        this->ReferenceYValue < yRange[1])
      {
        double yRefPos = doubleP1[1] +
          (this->ReferenceYValue - yRange[0]) / (yRange[1] - yRange[0]) *
            (doubleP2[1] - doubleP1[1]);
        pts->SetPoint(2, doubleP1[0], yRefPos, 0.);
        pts->SetPoint(3, doubleP2[0], yRefPos, 0.);
      }
      else
      {
        pts->SetPoint(2, doubleP1);
        pts->SetPoint(3, doubleP1);
      }
      // copy the color/linewidth/opacity...
      this->ReferenceLinesActor->SetProperty(this->GetProperty());
    }
    vtkDebugMacro(<< "Creating Plot Data");
    // Okay, now create the plot data and set up the pipeline
    this->CreatePlotData(pos, pos2, xRange, yRange, lengths, numDS, numDO);
    delete[] lengths;

    this->BuildTime.Modified();

  } // if need to rebuild the plot

  vtkDebugMacro(<< "Rendering Box");
  if (this->ChartBox)
  {
    renderedSomething += this->ChartBoxActor->RenderOpaqueGeometry(viewport);
  }
  if (this->ChartBorder)
  {
    renderedSomething += this->ChartBorderActor->RenderOpaqueGeometry(viewport);
  }
  if (this->ShowReferenceXLine || this->ShowReferenceYLine)
  {
    renderedSomething += this->ReferenceLinesActor->RenderOpaqueGeometry(viewport);
  }
  vtkDebugMacro(<< "Rendering Axes");
  renderedSomething += this->XAxis->RenderOpaqueGeometry(viewport);
  renderedSomething += this->YAxis->RenderOpaqueGeometry(viewport);
  if (this->YTitleActor)
  {
    vtkDebugMacro(<< "Rendering ytitleactor");
    renderedSomething += this->YTitleActor->RenderOpaqueGeometry(viewport);
  }
  for (int i = 0; i < this->NumberOfInputs; ++i)
  {
    vtkDebugMacro(<< "Rendering plotactors");
    renderedSomething += this->PlotActor[i]->RenderOpaqueGeometry(viewport);
  }
  if (this->Title)
  {
    vtkDebugMacro(<< "Rendering titleactors");
    renderedSomething += this->TitleActor->RenderOpaqueGeometry(viewport);
  }
  if (this->Legend)
  {
    vtkDebugMacro(<< "Rendering legendeactors");
    renderedSomething += this->LegendActor->RenderOpaqueGeometry(viewport);
  }

  return renderedSomething;
}

//------------------------------------------------------------------------------
// Description:
// Does this prop have some translucent polygonal geometry?
vtkTypeBool vtkXYPlotActor::HasTranslucentPolygonalGeometry()
{
  return 0;
}

//------------------------------------------------------------------------------
const char* vtkXYPlotActor::GetXValuesAsString()
{
  switch (this->XValues)
  {
    case VTK_XYPLOT_INDEX:
      return "Index";
    case VTK_XYPLOT_ARC_LENGTH:
      return "ArcLength";
    case VTK_XYPLOT_NORMALIZED_ARC_LENGTH:
      return "NormalizedArcLength";
    default:
      return "Value";
  }
}

//------------------------------------------------------------------------------
const char* vtkXYPlotActor::GetDataObjectPlotModeAsString()
{
  if (this->DataObjectPlotMode == VTK_XYPLOT_ROW)
  {
    return "Plot Rows";
  }
  else
  {
    return "Plot Columns";
  }
}

//------------------------------------------------------------------------------
// Release any graphics resources that are being consumed by this actor.
// The parameter window could be used to determine which graphic
// resources to release.
void vtkXYPlotActor::ReleaseGraphicsResources(vtkWindow* win)
{
  this->TitleActor->ReleaseGraphicsResources(win);
  this->XAxis->ReleaseGraphicsResources(win);
  this->YAxis->ReleaseGraphicsResources(win);
  for (int i = 0; i < this->NumberOfInputs; i++)
  {
    this->PlotActor[i]->ReleaseGraphicsResources(win);
  }
  this->LegendActor->ReleaseGraphicsResources(win);
  if (this->ChartBoxActor)
  {
    this->ChartBoxActor->ReleaseGraphicsResources(win);
  }
  if (this->ChartBorderActor)
  {
    this->ChartBorderActor->ReleaseGraphicsResources(win);
  }
  if (this->ReferenceLinesActor)
  {
    this->ReferenceLinesActor->ReleaseGraphicsResources(win);
  }
  if (this->YTitleActor)
  {
    this->YTitleActor->ReleaseGraphicsResources(win);
  }
}

//------------------------------------------------------------------------------
vtkMTimeType vtkXYPlotActor::GetMTime()
{
  vtkMTimeType mtime, mtime2;
  mtime = this->vtkActor2D::GetMTime();

  if (this->Legend)
  {
    mtime2 = this->LegendActor->GetMTime();
    mtime = std::max(mtime2, mtime);
  }

  return mtime;
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::PrintSelf(ostream& os, vtkIndent indent)
{
  vtkIndent i2 = indent.GetNextIndent();
  vtkAlgorithmOutput* input;
  char* array;
  int component;
  int idx, num;

  this->Superclass::PrintSelf(os, indent);

  num = this->InputConnectionHolder->GetNumberOfInputConnections(0);
  os << indent << "DataSetInputs: " << endl;
  for (idx = 0; idx < num; ++idx)
  {
    input = this->InputConnectionHolder->GetInputConnection(0, idx);
    array = this->SelectedInputScalars[idx];
    component = this->SelectedInputScalarsComponent->GetValue((vtkIdType)idx);
    if (array == nullptr)
    {
      os << i2 << "(" << input << ") Default Scalars,  Component = " << component << endl;
    }
    else
    {
      os << i2 << "(" << input << ") " << array << ",  Component = " << component << endl;
    }
  }

  os << indent << "Input DataObjects:\n";
  num = this->DataObjectInputConnectionHolder->GetNumberOfInputConnections(0);
  for (idx = 0; idx < num; ++idx)
  {
    input = this->DataObjectInputConnectionHolder->GetInputConnection(0, idx);
    os << i2 << input << endl;
  }

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

  if (this->AxisTitleTextProperty)
  {
    os << indent << "Axis Title Text Property:\n";
    this->AxisTitleTextProperty->PrintSelf(os, indent.GetNextIndent());
  }
  else
  {
    os << indent << "Axis Title Text Property: ( none )\n";
  }

  if (this->AxisLabelTextProperty)
  {
    os << indent << "Axis Label Text Property:\n";
    this->AxisLabelTextProperty->PrintSelf(os, indent.GetNextIndent());
  }
  else
  {
    os << indent << "Axis Label Text Property: ( none )\n";
  }

  os << indent << "Data Object Plot Mode: " << this->GetDataObjectPlotModeAsString() << endl;

  os << indent << "Title: " << (this->Title ? this->Title : "( none )") << "\n";
  os << indent << "X Title: " << (this->XTitle ? this->XTitle : "( none )") << "\n";

  os << indent << "X Values: " << this->GetXValuesAsString() << endl;
  os << indent << "Log X Values: " << (this->Logx ? "On\n" : "Off\n");

  os << indent << "Plot global-points: " << (this->PlotPoints ? "On\n" : "Off\n");
  os << indent << "Plot global-lines: " << (this->PlotLines ? "On\n" : "Off\n");
  os << indent << "Plot per-curve points: " << (this->PlotCurvePoints ? "On\n" : "Off\n");
  os << indent << "Plot per-curve lines: " << (this->PlotCurveLines ? "On\n" : "Off\n");
  os << indent << "Exchange Axes: " << (this->ExchangeAxes ? "On\n" : "Off\n");
  os << indent << "Reverse X Axis: " << (this->ReverseXAxis ? "On\n" : "Off\n");
  os << indent << "Reverse Y Axis: " << (this->ReverseYAxis ? "On\n" : "Off\n");

  os << indent << "Number Of X Labels: " << this->NumberOfXLabels << "\n";
  os << indent << "Number Of Y Labels: " << this->NumberOfYLabels << "\n";

  os << indent << "X Label Format: " << this->XLabelFormat << "\n";
  os << indent << "Y Label Format: " << this->YLabelFormat << "\n";
  os << indent << "Border: " << this->Border << "\n";

  os << indent << "X Range: ";
  if (this->XRange[0] >= this->XRange[1])
  {
    os << indent << "( Automatically Computed )\n";
  }
  else
  {
    os << "(" << this->XRange[0] << ", " << this->XRange[1] << ")\n";
  }

  os << indent << "Y Range: ";
  if (this->XRange[0] >= this->YRange[1])
  {
    os << indent << "( Automatically Computed )\n";
  }
  else
  {
    os << "(" << this->YRange[0] << ", " << this->YRange[1] << ")\n";
  }

  os << indent << "Viewport Coordinate: (" << this->ViewportCoordinate[0] << ", "
     << this->ViewportCoordinate[1] << ")\n";

  os << indent << "Plot Coordinate: (" << this->PlotCoordinate[0] << ", " << this->PlotCoordinate[1]
     << ")\n";

  os << indent << "Legend: " << (this->Legend ? "On\n" : "Off\n");
  os << indent << "Legend Position: (" << this->LegendPosition[0] << ", " << this->LegendPosition[1]
     << ")\n";
  os << indent << "Legend Position2: (" << this->LegendPosition2[0] << ", "
     << this->LegendPosition2[1] << ")\n";

  os << indent << "Glyph Size: " << this->GlyphSize << endl;

  os << indent << "Legend Actor:";
  this->LegendActor->PrintSelf(os << endl, i2);
  os << indent << "Glyph Source:";
  this->GlyphSource->PrintSelf(os << endl, i2);

  os << indent << "AdjustXLabels: " << this->AdjustXLabels << endl;
  os << indent << "AdjustYLabels: " << this->AdjustYLabels << endl;
  os << indent << "AdjustTitlePosition: " << this->AdjustTitlePosition << endl;
  os << indent << "TitlePosition: " << this->TitlePosition[0] << " " << this->TitlePosition[1]
     << " " << endl;
  os << indent << "AdjustTitlePositionMode: " << this->AdjustTitlePositionMode << endl;
  os << indent << "ChartBox: " << (this->ChartBox ? "On\n" : "Off\n");
  os << indent << "ChartBorder: " << (this->ChartBorder ? "On\n" : "Off\n");
  os << indent << "ShowReferenceXLine: " << (this->ShowReferenceXLine ? "On\n" : "Off\n");
  os << indent << "ReferenceXValue: " << this->ReferenceXValue << endl;
  os << indent << "ShowReferenceYLine: " << (this->ShowReferenceYLine ? "On\n" : "Off\n");
  os << indent << "ReferenceYValue: " << this->ReferenceYValue << endl;
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::ComputeXRange(double range[2], double* lengths)
{
  int dsNum;
  vtkIdType numPts, ptId, maxNum;
  double maxLength = 0.0, xPrev[3], x[3];
  vtkDataSet* ds;

  range[0] = VTK_DOUBLE_MAX;
  range[1] = VTK_DOUBLE_MIN;

  int numDS = this->InputConnectionHolder->GetNumberOfInputConnections(0);
  for (dsNum = 0, maxNum = 0; dsNum < numDS; dsNum++)
  {
    vtkAlgorithmOutput* port = this->InputConnectionHolder->GetInputConnection(0, dsNum);
    vtkAlgorithm* alg = port->GetProducer();
    int portIndex = port->GetIndex();
    ds = vtkDataSet::SafeDownCast(alg->GetOutputDataObject(portIndex));
    numPts = ds->GetNumberOfPoints();
    if (numPts == 0)
    {
      vtkErrorMacro(<< "No scalar data to plot!");
      continue;
    }

    if (this->XValues != VTK_XYPLOT_INDEX)
    {
      ds->GetPoint(0, xPrev);
      for (lengths[dsNum] = 0.0, ptId = 0; ptId < numPts; ptId++)
      {
        ds->GetPoint(ptId, x);
        switch (this->XValues)
        {
          case VTK_XYPLOT_VALUE:
            if (this->GetLogx() == 0)
            {
              range[0] = std::min(x[this->XComponent->GetValue(dsNum)], range[0]);
              range[1] = std::max(x[this->XComponent->GetValue(dsNum)], range[1]);
            }
            else
            {
              // ensure range strictly > 0 for log
              if ((x[this->XComponent->GetValue(dsNum)]) < range[0] &&
                (x[this->XComponent->GetValue(dsNum)] > 0))
              {
                range[0] = x[this->XComponent->GetValue(dsNum)];
              }
              if ((x[this->XComponent->GetValue(dsNum)] > range[1]) &&
                (x[this->XComponent->GetValue(dsNum)] > 0))
              {
                range[1] = x[this->XComponent->GetValue(dsNum)];
              }
            }
            break;
          default:
            lengths[dsNum] += sqrt(vtkMath::Distance2BetweenPoints(x, xPrev));
            xPrev[0] = x[0];
            xPrev[1] = x[1];
            xPrev[2] = x[2];
        }
      } // for all points
      maxLength = std::max(lengths[dsNum], maxLength);
    } // if need to visit all points

    else // if ( this->XValues == VTK_XYPLOT_INDEX )
    {
      maxNum = std::max(numPts, maxNum);
    }
  } // over all datasets

  // determine the range
  switch (this->XValues)
  {
    case VTK_XYPLOT_ARC_LENGTH:
      range[0] = 0.;
      range[1] = maxLength;
      break;
    case VTK_XYPLOT_NORMALIZED_ARC_LENGTH:
      range[0] = 0.;
      range[1] = 1.;
      break;
    case VTK_XYPLOT_INDEX:
      range[0] = 0.;
      range[1] = (double)(maxNum - 1);
      break;
    case VTK_XYPLOT_VALUE:
      if (this->GetLogx() == 1)
      {
        if (range[0] > range[1])
        {
          range[0] = 0;
          range[1] = 0;
        }
        else
        {
          range[0] = log10(range[0]);
          range[1] = log10(range[1]);
        }
      }
      break; // range computed in for loop above
    default:
      vtkErrorMacro(<< "Unknown X-Value option.");
      return;
  }
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::ComputeYRange(double range[2])
{
  vtkDataSet* ds;
  vtkDataArray* scalars;
  double sRange[2];
  int component;

  range[0] = VTK_DOUBLE_MAX;
  range[1] = VTK_DOUBLE_MIN;

  int numDS = this->InputConnectionHolder->GetNumberOfInputConnections(0);
  for (int dsNum = 0, count = 0; dsNum < numDS; dsNum++, count++)
  {
    vtkAlgorithmOutput* port = this->InputConnectionHolder->GetInputConnection(0, dsNum);
    vtkAlgorithm* alg = port->GetProducer();
    int portIndex = port->GetIndex();
    ds = vtkDataSet::SafeDownCast(alg->GetOutputDataObject(portIndex));
    scalars = ds->GetPointData()->GetScalars(this->SelectedInputScalars[count]);
    component = this->SelectedInputScalarsComponent->GetValue(count);
    if (!scalars)
    {
      vtkErrorMacro(<< "No scalar data to plot!");
      continue;
    }
    if (component < 0 || component >= scalars->GetNumberOfComponents())
    {
      vtkErrorMacro(<< "Bad component!");
      continue;
    }

    scalars->GetRange(sRange, component);
    range[0] = std::min(sRange[0], range[0]);

    range[1] = std::max(sRange[1], range[1]);
  } // over all datasets
}

//------------------------------------------------------------------------------
static inline int vtkXYPlotActorGetComponent(
  vtkFieldData* field, vtkIdType tuple, int component, double* val)
{
  int array_comp;
  int array_index = field->GetArrayContainingComponent(component, array_comp);
  if (array_index < 0)
  {
    return 0;
  }
  vtkDataArray* da = field->GetArray(array_index);
  if (!da)
  {
    // non-numeric array.
    return 0;
  }
  *val = da->GetComponent(tuple, array_comp);
  return 1;
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::ComputeDORange(double xrange[2], double yrange[2], double* lengths)
{
  int i;
  vtkDataObject* dobj;
  vtkFieldData* field;
  int doNum, numColumns;
  vtkIdType numTuples, numRows, num, ptId, maxNum;
  double maxLength = 0.;
  double x = 0.;
  double y = 0.;
  double xPrev = 0.;
  vtkDataArray* array;

  // NOTE: FieldData can have non-numeric arrays. However, XY plot can only
  // work on numeric arrays ( or vtkDataArray subclasses ).

  xrange[0] = yrange[0] = VTK_DOUBLE_MAX;
  xrange[1] = yrange[1] = -VTK_DOUBLE_MAX;
  maxNum = 0;
  int numDOs = this->DataObjectInputConnectionHolder->GetNumberOfInputConnections(0);
  for (doNum = 0; doNum < numDOs; doNum++)
  {
    vtkAlgorithmOutput* port = this->DataObjectInputConnectionHolder->GetInputConnection(0, doNum);
    vtkAlgorithm* alg = port->GetProducer();
    int portIdx = port->GetIndex();
    dobj = alg->GetOutputDataObject(portIdx);

    lengths[doNum] = 0.;
    field = dobj->GetFieldData();
    numColumns = field->GetNumberOfComponents(); // number of "columns"
    // numColumns includes the components for non-numeric arrays as well.
    for (numRows = VTK_ID_MAX, i = 0; i < field->GetNumberOfArrays(); i++)
    {
      array = field->GetArray(i);
      if (!array)
      {
        // non-numeric array, skip.
        continue;
      }
      numTuples = array->GetNumberOfTuples();
      numRows = std::min(numTuples, numRows);
    }

    num = (this->DataObjectPlotMode == VTK_XYPLOT_ROW ? numColumns : numRows);

    if (this->XValues != VTK_XYPLOT_INDEX)
    {
      // gather the information to form a plot
      for (ptId = 0; ptId < num; ptId++)
      {
        int status = 0;

        if (this->DataObjectPlotMode == VTK_XYPLOT_ROW)
        {
          // x = field->GetComponent( this->XComponent->GetValue( doNum ), ptId );
          status = ::vtkXYPlotActorGetComponent(field, this->XComponent->GetValue(doNum), ptId, &x);
        }
        else // if ( this->DataObjectPlotMode == VTK_XYPLOT_COLUMN )
        {
          // x = field->GetComponent( ptId, this->XComponent->GetValue( doNum ) );
          status = ::vtkXYPlotActorGetComponent(field, ptId, this->XComponent->GetValue(doNum), &x);
        }
        if (!status)
        {
          // requested component falls in a non-numeric array, skip it.
          continue;
        }
        if (ptId == 0)
        {
          xPrev = x;
        }

        switch (this->XValues)
        {
          case VTK_XYPLOT_VALUE:
            if (this->GetLogx() == 0)
            {
              xrange[0] = std::min(x, xrange[0]);
              xrange[1] = std::max(x, xrange[1]);
            }
            else // ensure positive values
            {
              if ((x < xrange[0]) && (x > 0))
              {
                xrange[0] = x;
              }
              if (x > xrange[1] && (x > 0))
              {
                xrange[1] = x;
              }
            }
            break;
          default:
            lengths[doNum] += fabs(x - xPrev);
            xPrev = x;
        }
      } // for all points
      maxLength = std::max(lengths[doNum], maxLength);
    } // if all data has to be visited

    else // if ( this->XValues == VTK_XYPLOT_INDEX )
    {
      maxNum = std::max(num, maxNum);
    }

    // Get the y-values
    for (ptId = 0; ptId < num; ptId++)
    {
      int status = 0;
      if (this->DataObjectPlotMode == VTK_XYPLOT_ROW)
      {
        // y = field->GetComponent( this->YComponent->GetValue( doNum ), ptId );
        status = ::vtkXYPlotActorGetComponent(field, this->YComponent->GetValue(doNum), ptId, &y);
      }
      else // if ( this->DataObjectPlotMode == VTK_XYPLOT_COLUMN )
      {
        // y = field->GetComponent( ptId, this->YComponent->GetValue( doNum ) );
        status = ::vtkXYPlotActorGetComponent(field, ptId, this->YComponent->GetValue(doNum), &y);
      }
      if (!status)
      {
        // requested component falls in non-numeric array.
        // skip.
        continue;
      }
      yrange[0] = std::min(y, yrange[0]);
      yrange[1] = std::max(y, yrange[1]);
    } // over all y values
  }   // over all dataobjects

  // determine the range
  switch (this->XValues)
  {
    case VTK_XYPLOT_ARC_LENGTH:
      xrange[0] = 0.;
      xrange[1] = maxLength;
      break;
    case VTK_XYPLOT_NORMALIZED_ARC_LENGTH:
      xrange[0] = 0.;
      xrange[1] = 1.;
      break;
    case VTK_XYPLOT_INDEX:
      xrange[0] = 0.;
      xrange[1] = (double)(maxNum - 1);
      break;
    case VTK_XYPLOT_VALUE:
      if (this->GetLogx() == 1)
      {
        xrange[0] = log10(xrange[0]);
        xrange[1] = log10(xrange[1]);
      }
      break;
    default:
      vtkErrorMacro(<< "Unknown X-Value option");
      return;
  }
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::CreatePlotData(
  int* pos, int* pos2, double xRange[2], double yRange[2], double* lengths, int numDS, int numDO)
{
  double xyz[3];
  xyz[2] = 0.;
  int i, numLinePts, doNum, num;
  vtkIdType numPts, ptId, id;
  double length, x[3], xPrev[3];
  vtkDataArray* scalars;
  int component;
  vtkDataSet* ds;
  vtkCellArray* lines;
  vtkPoints* pts;
  int clippingRequired = 0;

  // Allocate resources for the polygonal plots
  //
  num = (numDS > numDO ? numDS : numDO);
  this->InitializeEntries();
  this->NumberOfInputs = num;
  this->PlotData = new vtkPolyData*[num];
  this->PlotGlyph = new vtkGlyph2D*[num];
  this->PlotAppend = new vtkAppendPolyData*[num];
  this->PlotMapper = new vtkPolyDataMapper2D*[num];
  this->PlotActor = new vtkActor2D*[num];
  for (i = 0; i < num; i++)
  {
    this->PlotData[i] = vtkPolyData::New();
    this->PlotGlyph[i] = vtkGlyph2D::New();
    this->PlotGlyph[i]->SetInputData(this->PlotData[i]);
    this->PlotGlyph[i]->SetScaleModeToDataScalingOff();
    this->PlotAppend[i] = vtkAppendPolyData::New();
    this->PlotAppend[i]->AddInputData(this->PlotData[i]);
    if (this->LegendActor->GetEntrySymbol(i) != nullptr &&
      this->LegendActor->GetEntrySymbol(i) != this->GlyphSource->GetOutput())
    {
      this->PlotGlyph[i]->SetSourceData(this->LegendActor->GetEntrySymbol(i));
      this->PlotGlyph[i]->SetScaleFactor(this->ComputeGlyphScale(i, pos, pos2));
      this->PlotAppend[i]->AddInputConnection(this->PlotGlyph[i]->GetOutputPort());
    }
    this->PlotMapper[i] = vtkPolyDataMapper2D::New();
    this->PlotMapper[i]->SetInputConnection(this->PlotAppend[i]->GetOutputPort());
    this->PlotMapper[i]->ScalarVisibilityOff();
    this->PlotActor[i] = vtkActor2D::New();
    this->PlotActor[i]->SetMapper(this->PlotMapper[i]);
    this->PlotActor[i]->GetProperty()->DeepCopy(this->GetProperty());
    if (this->LegendActor->GetEntryColor(i)[0] < 0.0)
    {
      this->PlotActor[i]->GetProperty()->SetColor(this->GetProperty()->GetColor());
    }
    else
    {
      this->PlotActor[i]->GetProperty()->SetColor(this->LegendActor->GetEntryColor(i));
    }
  }

  // Prepare to receive data
  this->GenerateClipPlanes(pos, pos2);
  for (i = 0; i < this->NumberOfInputs; i++)
  {
    lines = vtkCellArray::New();
    pts = vtkPoints::New();

    lines->AllocateEstimate(10, 10);
    pts->Allocate(10, 10);
    this->PlotData[i]->SetPoints(pts);
    this->PlotData[i]->SetVerts(lines);
    this->PlotData[i]->SetLines(lines);

    pts->Delete();
    lines->Delete();
  }

  // Okay, for each input generate plot data. Depending on the input
  // we use either dataset or data object.
  //
  if (numDS > 0)
  {
    for (int dsNum = 0; dsNum < numDS; dsNum++)
    {
      vtkAlgorithmOutput* port = this->InputConnectionHolder->GetInputConnection(0, dsNum);
      vtkAlgorithm* alg = port->GetProducer();
      int portIndex = port->GetIndex();
      ds = vtkDataSet::SafeDownCast(alg->GetOutputDataObject(portIndex));
      clippingRequired = 0;
      numPts = ds->GetNumberOfPoints();
      scalars = ds->GetPointData()->GetScalars(this->SelectedInputScalars[dsNum]);
      if (!scalars)
      {
        continue;
      }
      if (scalars->GetNumberOfTuples() < numPts)
      {
        vtkErrorMacro("Number of points: " << numPts << " exceeds number of scalar tuples: "
                                           << scalars->GetNumberOfTuples());
        continue;
      }
      component = this->SelectedInputScalarsComponent->GetValue(dsNum);
      if (component < 0 || component >= scalars->GetNumberOfComponents())
      {
        continue;
      }

      pts = this->PlotData[dsNum]->GetPoints();
      lines = this->PlotData[dsNum]->GetLines();
      lines->InsertNextCell(0); // update the count later

      ds->GetPoint(0, xPrev);
      for (numLinePts = 0, length = 0.0, ptId = 0; ptId < numPts; ptId++)
      {
        xyz[1] = scalars->GetComponent(ptId, component);
        ds->GetPoint(ptId, x);
        switch (this->XValues)
        {
          case VTK_XYPLOT_NORMALIZED_ARC_LENGTH:
            length += sqrt(vtkMath::Distance2BetweenPoints(x, xPrev));
            xyz[0] = length / lengths[dsNum];
            xPrev[0] = x[0];
            xPrev[1] = x[1];
            xPrev[2] = x[2];
            break;
          case VTK_XYPLOT_INDEX:
            xyz[0] = (double)ptId;
            break;
          case VTK_XYPLOT_ARC_LENGTH:
            length += sqrt(vtkMath::Distance2BetweenPoints(x, xPrev));
            xyz[0] = length;
            xPrev[0] = x[0];
            xPrev[1] = x[1];
            xPrev[2] = x[2];
            break;
          case VTK_XYPLOT_VALUE:
            xyz[0] = x[this->XComponent->GetValue(dsNum)];
            break;
          default:
            vtkErrorMacro(<< "Unknown X-Component option");
        }

        if (this->GetLogx() == 1)
        {
          if (xyz[0] > 0)
          {
            xyz[0] = log10(xyz[0]);
            // normalize and position
            if (xyz[0] < xRange[0] || xyz[0] > xRange[1] || xyz[1] < yRange[0] ||
              xyz[1] > yRange[1])
            {
              clippingRequired = 1;
            }

            numLinePts++;
            xyz[0] = pos[0] + (xyz[0] - xRange[0]) / (xRange[1] - xRange[0]) * (pos2[0] - pos[0]);
            xyz[1] = pos[1] + (xyz[1] - yRange[0]) / (yRange[1] - yRange[0]) * (pos2[1] - pos[1]);
            id = pts->InsertNextPoint(xyz);
            lines->InsertCellPoint(id);
          }
        }
        else
        {
          // normalize and position
          if (xyz[0] < xRange[0] || xyz[0] > xRange[1] || xyz[1] < yRange[0] || xyz[1] > yRange[1])
          {
            clippingRequired = 1;
          }

          numLinePts++;
          xyz[0] = pos[0] + (xyz[0] - xRange[0]) / (xRange[1] - xRange[0]) * (pos2[0] - pos[0]);
          xyz[1] = pos[1] + (xyz[1] - yRange[0]) / (yRange[1] - yRange[0]) * (pos2[1] - pos[1]);
          id = pts->InsertNextPoint(xyz);
          lines->InsertCellPoint(id);
        }
      } // for all input points

      lines->UpdateCellCount(numLinePts);
      if (clippingRequired)
      {
        this->ClipPlotData(pos, pos2, this->PlotData[dsNum]);
      }
    } // loop over all input data sets
  }   // if plotting datasets

  else // plot data from data objects
  {
    vtkDataObject* dobj;
    int numColumns;
    vtkIdType numRows, numTuples;
    vtkDataArray* array;
    vtkFieldData* field;
    int numDOs = this->DataObjectInputConnectionHolder->GetNumberOfInputConnections(0);
    for (doNum = 0; doNum < numDOs; doNum++)
    {
      vtkAlgorithmOutput* port =
        this->DataObjectInputConnectionHolder->GetInputConnection(0, doNum);
      vtkAlgorithm* alg = port->GetProducer();
      int portIdx = port->GetIndex();
      dobj = alg->GetOutputDataObject(portIdx);

      // determine the shape of the field
      field = dobj->GetFieldData();
      numColumns = field->GetNumberOfComponents(); // number of "columns"
      // numColumns also includes non-numeric array components.
      for (numRows = VTK_ID_MAX, i = 0; i < field->GetNumberOfArrays(); i++)
      {
        array = field->GetArray(i);
        if (!array)
        {
          // skip non-numeric arrays.
          continue;
        }
        numTuples = array->GetNumberOfTuples();
        numRows = std::min(numTuples, numRows);
      }

      pts = this->PlotData[doNum]->GetPoints();
      lines = this->PlotData[doNum]->GetLines();
      lines->InsertNextCell(0); // update the count later

      numPts = (this->DataObjectPlotMode == VTK_XYPLOT_ROW ? numColumns : numRows);

      // gather the information to form a plot
      for (numLinePts = 0, length = 0.0, ptId = 0; ptId < numPts; ptId++)
      {
        int status1, status2;
        if (this->DataObjectPlotMode == VTK_XYPLOT_ROW)
        {
          // x[0] = field->GetComponent( this->XComponent->GetValue( doNum ),ptId );
          // xyz[1] = field->GetComponent( this->YComponent->GetValue( doNum ),ptId );
          status1 =
            ::vtkXYPlotActorGetComponent(field, this->XComponent->GetValue(doNum), ptId, &x[0]);
          status2 =
            ::vtkXYPlotActorGetComponent(field, this->YComponent->GetValue(doNum), ptId, &xyz[1]);
        }
        else // if ( this->DataObjectPlotMode == VTK_XYPLOT_COLUMN )
        {
          // x[0] = field->GetComponent( ptId, this->XComponent->GetValue( doNum ) );
          // xyz[1] = field->GetComponent( ptId, this->YComponent->GetValue( doNum ) );

          status1 =
            ::vtkXYPlotActorGetComponent(field, ptId, this->XComponent->GetValue(doNum), &x[0]);

          if (!status1)
          {
            vtkWarningMacro(<< this->XComponent->GetValue(doNum) << " is a non-numeric component.");
          }

          status2 =
            ::vtkXYPlotActorGetComponent(field, ptId, this->YComponent->GetValue(doNum), &xyz[1]);

          if (!status2)
          {
            vtkWarningMacro(<< this->YComponent->GetValue(doNum) << " is a non-numeric component.");
          }
        }
        if (!status1 || !status2)
        {
          // component is non-numeric.
          // Skip it.
          continue;
        }

        switch (this->XValues)
        {
          case VTK_XYPLOT_NORMALIZED_ARC_LENGTH:
            length += fabs(x[0] - xPrev[0]);
            xyz[0] = length / lengths[doNum];
            xPrev[0] = x[0];
            break;
          case VTK_XYPLOT_INDEX:
            xyz[0] = (double)ptId;
            break;
          case VTK_XYPLOT_ARC_LENGTH:
            length += fabs(x[0] - xPrev[0]);
            xyz[0] = length;
            xPrev[0] = x[0];
            break;
          case VTK_XYPLOT_VALUE:
            xyz[0] = x[0];
            break;
          default:
            vtkErrorMacro(<< "Unknown X-Value option");
        }

        if (this->GetLogx() == 1)
        {
          if (xyz[0] > 0)
          {
            xyz[0] = log10(xyz[0]);
            // normalize and position
            if (xyz[0] < xRange[0] || xyz[0] > xRange[1] || xyz[1] < yRange[0] ||
              xyz[1] > yRange[1])
            {
              clippingRequired = 1;
            }
            numLinePts++;
            xyz[0] = pos[0] + (xyz[0] - xRange[0]) / (xRange[1] - xRange[0]) * (pos2[0] - pos[0]);
            xyz[1] = pos[1] + (xyz[1] - yRange[0]) / (yRange[1] - yRange[0]) * (pos2[1] - pos[1]);
            id = pts->InsertNextPoint(xyz);
            lines->InsertCellPoint(id);
          }
        }
        else
        {
          // normalize and position
          if (xyz[0] < xRange[0] || xyz[0] > xRange[1] || xyz[1] < yRange[0] || xyz[1] > yRange[1])
          {
            clippingRequired = 1;
          }
          numLinePts++;
          xyz[0] = pos[0] + (xyz[0] - xRange[0]) / (xRange[1] - xRange[0]) * (pos2[0] - pos[0]);
          xyz[1] = pos[1] + (xyz[1] - yRange[0]) / (yRange[1] - yRange[0]) * (pos2[1] - pos[1]);
          id = pts->InsertNextPoint(xyz);
          lines->InsertCellPoint(id);
        }
      } // for all input points

      lines->UpdateCellCount(numLinePts);
      if (clippingRequired)
      {
        this->ClipPlotData(pos, pos2, this->PlotData[doNum]);
      }
    } // loop over all input data sets
  }

  // Remove points/lines as directed by the user
  for (i = 0; i < num; i++)
  {
    if (!this->PlotCurveLines)
    {
      if (!this->PlotLines)
      {
        this->PlotData[i]->SetLines(nullptr);
      }
    }
    else
    {
      if (this->GetPlotLines(i) == 0)
      {
        this->PlotData[i]->SetLines(nullptr);
      }
    }

    if (!this->PlotCurvePoints)
    {
      if (!this->PlotPoints ||
        (this->LegendActor->GetEntrySymbol(i) &&
          this->LegendActor->GetEntrySymbol(i) != this->GlyphSource->GetOutput()))
      {
        this->PlotData[i]->SetVerts(nullptr);
      }
    }
    else
    {
      if (this->GetPlotPoints(i) == 0 ||
        (this->LegendActor->GetEntrySymbol(i) &&
          this->LegendActor->GetEntrySymbol(i) != this->GlyphSource->GetOutput()))
      {
        this->PlotData[i]->SetVerts(nullptr);
      }
    }
  }
}

//------------------------------------------------------------------------------
// Position the axes taking into account the expected padding due to labels
// and titles. We want the result to fit in the box specified. This method
// knows something about how the vtkAxisActor2D functions, so it may have
// to change if that class changes dramatically.
//
void vtkXYPlotActor::PlaceAxes(vtkViewport* viewport, const int* size, int pos[2], int pos2[2])
{
  int titleSizeX[2], titleSizeY[2], labelSizeX[2], labelSizeY[2];
  double labelFactorX, labelFactorY;
  double fontFactorX, fontFactorY;
  double tickOffsetX, tickOffsetY;
  double tickLengthX, tickLengthY;

  vtkAxisActor2D* axisX;
  vtkAxisActor2D* axisY;

  char str1[512], str2[512];

  if (this->ExchangeAxes)
  {
    axisX = this->YAxis;
    axisY = this->XAxis;
  }
  else
  {
    axisX = this->XAxis;
    axisY = this->YAxis;
  }

  fontFactorY = axisY->GetFontFactor();
  fontFactorX = axisX->GetFontFactor();

  labelFactorY = axisY->GetLabelFactor();
  labelFactorX = axisX->GetLabelFactor();

  // Create a dummy text mapper for getting font sizes
  vtkTextMapper* textMapper = vtkTextMapper::New();
  vtkTextProperty* tprop = textMapper->GetTextProperty();

  // Get the location of the corners of the box
  int* p1 = this->PositionCoordinate->GetComputedViewportValue(viewport);
  int* p2 = this->Position2Coordinate->GetComputedViewportValue(viewport);

  // Estimate the padding around the X and Y axes
  tprop->ShallowCopy(axisX->GetTitleTextProperty());
  textMapper->SetInput(axisX->GetTitle());
  vtkTextMapper::SetRelativeFontSize(textMapper, viewport, size, titleSizeX, 0.015 * fontFactorX);

  tprop->ShallowCopy(axisY->GetTitleTextProperty());
  textMapper->SetInput(axisY->GetTitle());
  vtkTextMapper::SetRelativeFontSize(textMapper, viewport, size, titleSizeY, 0.015 * fontFactorY);

  // Retrieve X axis title font
  tprop->ShallowCopy(axisX->GetTitleTextProperty());
  // Calculate string length from YTitleActor,
  //  + 1 for the case where there is only one character
  //  + 1 for the final \0
  int len = int((strlen(this->YTitleActor->GetInput()) + 1) * .5) + 1;
  char* tmp = new char[len];
  switch (this->YTitlePosition)
  {
    case VTK_XYPLOT_Y_AXIS_TOP:
    {
      auto result = vtk::format_to_n(tmp, len, "{:s}", this->YTitleActor->GetInput());
      *result.out = '\0';
      textMapper->SetInput(tmp);
    }
    break;
    case VTK_XYPLOT_Y_AXIS_HCENTER:
      textMapper->SetInput(this->YTitleActor->GetInput());
      break;
    case VTK_XYPLOT_Y_AXIS_VCENTER:
      // Create a dummy title to ensure that the added YTitleActor is visible
      textMapper->SetInput("AABB");
      break;
  }
  delete[] tmp;
  this->YAxisTitleSize =
    vtkTextMapper::SetRelativeFontSize(textMapper, viewport, size, titleSizeY, 0.015 * fontFactorY);

  this->YTitleSize[0] = titleSizeY[0];
  this->YTitleSize[1] = titleSizeY[1];

  // At this point the thing to do would be to actually ask the Y axis
  // actor to return the largest label.
  // In the meantime, let's try with the min and max
  std::string axisYLabelFormat =
    axisY->GetLabelFormat() ? vtk::to_std_format(axisY->GetLabelFormat()) : "";
  VTK_FORMAT_IF_ERROR_RETURN(auto result = vtk::format_to_n(
                               str1, sizeof(str1), axisYLabelFormat, axisY->GetAdjustedRange()[0]);
                             *result.out = '\0', );
  VTK_FORMAT_IF_ERROR_RETURN(auto result = vtk::format_to_n(
                               str2, sizeof(str2), axisYLabelFormat, axisY->GetAdjustedRange()[1]);
                             *result.out = '\0', );
  tprop->ShallowCopy(axisY->GetLabelTextProperty());
  textMapper->SetInput(strlen(str1) > strlen(str2) ? str1 : str2);
  vtkTextMapper::SetRelativeFontSize(
    textMapper, viewport, size, labelSizeY, 0.015 * labelFactorY * fontFactorY);

  // We do only care of the height of the label in the X axis, so let's
  // use the min for example
  std::string axisXLabelFormat =
    axisX->GetLabelFormat() ? vtk::to_std_format(axisX->GetLabelFormat()) : "";
  VTK_FORMAT_IF_ERROR_RETURN(auto result = vtk::format_to_n(
                               str1, sizeof(str1), axisXLabelFormat, axisX->GetAdjustedRange()[0]);
                             *result.out = '\0', );
  tprop->ShallowCopy(axisX->GetLabelTextProperty());
  textMapper->SetInput(str1);
  vtkTextMapper::SetRelativeFontSize(
    textMapper, viewport, size, labelSizeX, 0.015 * labelFactorX * fontFactorX);

  tickOffsetX = axisX->GetTickOffset();
  tickOffsetY = axisY->GetTickOffset();
  tickLengthX = axisX->GetTickLength();
  tickLengthY = axisY->GetTickLength();

  // Okay, estimate the size
  pos[0] =
    (int)(p1[0] + titleSizeY[0] + 2.0 * tickOffsetY + tickLengthY + labelSizeY[0] + this->Border);

  pos[1] =
    (int)(p1[1] + titleSizeX[1] + 2.0 * tickOffsetX + tickLengthX + labelSizeX[1] + this->Border);

  pos2[0] = (int)(p2[0] - labelSizeY[0] / 2 - tickOffsetY - this->Border);

  pos2[1] = (int)(p2[1] - labelSizeX[1] / 2 - tickOffsetX - this->Border);

  // Save estimated axis size to avoid recomputing of YTitleActor displacement
  if (this->YTitlePosition == VTK_XYPLOT_Y_AXIS_TOP)
  {
    this->YTitleDelta = (int)(2 * tickOffsetY + tickLengthY + this->Border);
  }
  else
  {
    this->YTitleDelta = (int)(2 * tickOffsetY + tickLengthY + .75 * labelSizeY[0] + this->Border);
  }

  // Now specify the location of the axes
  axisX->GetPositionCoordinate()->SetValue((double)pos[0], (double)pos[1]);
  axisX->GetPosition2Coordinate()->SetValue((double)pos2[0], (double)pos[1]);
  axisY->GetPositionCoordinate()->SetValue((double)pos[0], (double)pos2[1]);
  axisY->GetPosition2Coordinate()->SetValue((double)pos[0], (double)pos[1]);

  textMapper->Delete();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::ViewportToPlotCoordinate(vtkViewport* viewport, double& u, double& v)
{
  int *p0, *p1, *p2;

  // XAxis, YAxis are in viewport coordinates already
  p0 = this->XAxis->GetPositionCoordinate()->GetComputedViewportValue(viewport);
  p1 = this->XAxis->GetPosition2Coordinate()->GetComputedViewportValue(viewport);
  p2 = this->YAxis->GetPositionCoordinate()->GetComputedViewportValue(viewport);

  u =
    ((u - p0[0]) / (double)(p1[0] - p0[0])) * (this->XComputedRange[1] - this->XComputedRange[0]) +
    this->XComputedRange[0];
  v =
    ((v - p0[1]) / (double)(p2[1] - p0[1])) * (this->YComputedRange[1] - this->YComputedRange[0]) +
    this->YComputedRange[0];
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::PlotToViewportCoordinate(vtkViewport* viewport, double& u, double& v)
{
  int *p0, *p1, *p2;

  // XAxis, YAxis are in viewport coordinates already
  p0 = this->XAxis->GetPositionCoordinate()->GetComputedViewportValue(viewport);
  p1 = this->XAxis->GetPosition2Coordinate()->GetComputedViewportValue(viewport);
  p2 = this->YAxis->GetPositionCoordinate()->GetComputedViewportValue(viewport);

  u = (((u - this->XComputedRange[0]) / (this->XComputedRange[1] - this->XComputedRange[0])) *
        (double)(p1[0] - p0[0])) +
    p0[0];
  v = (((v - this->YComputedRange[0]) / (this->YComputedRange[1] - this->YComputedRange[0])) *
        (double)(p2[1] - p0[1])) +
    p0[1];
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::ViewportToPlotCoordinate(vtkViewport* viewport)
{
  this->ViewportToPlotCoordinate(
    viewport, this->ViewportCoordinate[0], this->ViewportCoordinate[1]);
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::PlotToViewportCoordinate(vtkViewport* viewport)
{
  this->PlotToViewportCoordinate(viewport, this->PlotCoordinate[0], this->PlotCoordinate[1]);
}

//------------------------------------------------------------------------------
int vtkXYPlotActor::IsInPlot(vtkViewport* viewport, double u, double v)
{
  int *p0, *p1, *p2;

  // Bounds of the plot are based on the axes...
  p0 = this->XAxis->GetPositionCoordinate()->GetComputedViewportValue(viewport);
  p1 = this->XAxis->GetPosition2Coordinate()->GetComputedViewportValue(viewport);
  p2 = this->YAxis->GetPositionCoordinate()->GetComputedViewportValue(viewport);

  if (u >= p0[0] && u <= p1[0] && v >= p0[1] && v <= p2[1])
  {
    return 1;
  }

  return 0;
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetPlotLines(int i, int isOn)
{
  i = std::min(std::max(i, 0), VTK_MAX_PLOTS - 1);
  int val = this->LinesOn->GetValue(i);
  if (val != isOn)
  {
    this->Modified();
    this->LinesOn->SetValue(i, isOn);
  }
}

//------------------------------------------------------------------------------
int vtkXYPlotActor::GetPlotLines(int i)
{
  i = std::min(std::max(i, 0), VTK_MAX_PLOTS - 1);
  return this->LinesOn->GetValue(i);
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetPlotPoints(int i, int isOn)
{
  i = std::min(std::max(i, 0), VTK_MAX_PLOTS - 1);
  int val = this->PointsOn->GetValue(i);
  if (val != isOn)
  {
    this->Modified();
    this->PointsOn->SetValue(i, isOn);
  }
}

//------------------------------------------------------------------------------
int vtkXYPlotActor::GetPlotPoints(int i)
{
  i = std::min(std::max(i, 0), VTK_MAX_PLOTS - 1);
  return this->PointsOn->GetValue(i);
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetPlotColor(int i, double r, double g, double b)
{
  this->LegendActor->SetEntryColor(i, r, g, b);
}

//------------------------------------------------------------------------------
double* vtkXYPlotActor::GetPlotColor(int i)
{
  return this->LegendActor->GetEntryColor(i);
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetPlotSymbol(int i, vtkPolyData* input)
{
  this->LegendActor->SetEntrySymbol(i, input);
}

//------------------------------------------------------------------------------
vtkPolyData* vtkXYPlotActor::GetPlotSymbol(int i)
{
  return this->LegendActor->GetEntrySymbol(i);
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetPlotLabel(int i, const char* label)
{
  this->LegendActor->SetEntryString(i, label);
}

//------------------------------------------------------------------------------
const char* vtkXYPlotActor::GetPlotLabel(int i)
{
  return this->LegendActor->GetEntryString(i);
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::GenerateClipPlanes(int* pos, int* pos2)
{
  double n[3], x[3];
  vtkPoints* pts = this->ClipPlanes->GetPoints();
  vtkDataArray* normals = this->ClipPlanes->GetNormals();

  n[2] = x[2] = 0.;

  // first
  n[0] = 0.;
  n[1] = -1.;
  normals->SetTuple(0, n);
  x[0] = .5 * (pos[0] + pos2[0]);
  x[1] = (double)pos[1];
  pts->SetPoint(0, x);

  // second
  n[0] = 1.;
  n[1] = 0.;
  normals->SetTuple(1, n);
  x[0] = (double)pos2[0];
  x[1] = .5 * (pos[1] + pos2[1]);
  pts->SetPoint(1, x);

  // third
  n[0] = 0.;
  n[1] = 1.;
  normals->SetTuple(2, n);
  x[0] = .5 * (pos[0] + pos2[0]);
  x[1] = (double)pos2[1];
  pts->SetPoint(2, x);

  // fourth
  n[0] = -1.;
  n[1] = 0.;
  normals->SetTuple(3, n);
  x[0] = (double)pos[0];
  x[1] = .5 * (pos[1] + pos2[1]);
  pts->SetPoint(3, x);
}

//------------------------------------------------------------------------------
double vtkXYPlotActor::ComputeGlyphScale(int i, int* pos, int* pos2)
{
  vtkPolyData* pd = this->LegendActor->GetEntrySymbol(i);
  // pd->Update();
  double length = pd->GetLength();
  double sf = this->GlyphSize *
    sqrt(
      (double)(pos[0] - pos2[0]) * (pos[0] - pos2[0]) + (pos[1] - pos2[1]) * (pos[1] - pos2[1])) /
    length;

  return sf;
}

//------------------------------------------------------------------------------
// This assumes that there are multiple polylines
void vtkXYPlotActor::ClipPlotData(int* pos, int* pos2, vtkPolyData* pd)
{
  vtkPoints* points = pd->GetPoints();
  vtkPoints* newPoints;
  vtkCellArray* lines = pd->GetLines();
  vtkCellArray *newLines, *newVerts;
  vtkIdType numPts = pd->GetNumberOfPoints();
  vtkIdType npts = 0;
  vtkIdType newPts[2];
  const vtkIdType* pts = nullptr;
  vtkIdType i, id;
  int j;
  double x1[3], x2[3], px[3], n[3], xint[3], t;
  double p1[2], p2[2];

  p1[0] = (double)pos[0];
  p1[1] = (double)pos[1];
  p2[0] = (double)pos2[0];
  p2[1] = (double)pos2[1];

  newPoints = vtkPoints::New();
  newPoints->Allocate(numPts);
  newVerts = vtkCellArray::New();
  newVerts->AllocateCopy(lines);
  newLines = vtkCellArray::New();
  newLines->AllocateCopy(lines);
  int* pointMap = new int[numPts];
  for (i = 0; i < numPts; i++)
  {
    pointMap[i] = -1;
  }

  // Loop over polyverts eliminating those that are outside
  for (lines->InitTraversal(); lines->GetNextCell(npts, pts);)
  {
    // loop over verts keeping only those that are not clipped
    for (i = 0; i < npts; i++)
    {
      points->GetPoint(pts[i], x1);

      if (x1[0] >= p1[0] && x1[0] <= p2[0] && x1[1] >= p1[1] && x1[1] <= p2[1])
      {
        id = newPoints->InsertNextPoint(x1);
        pointMap[i] = id;
        newPts[0] = id;
        newVerts->InsertNextCell(1, newPts);
      }
    }
  }

  // Loop over polylines clipping each line segment
  for (lines->InitTraversal(); lines->GetNextCell(npts, pts);)
  {
    // loop over line segment making up the polyline
    for (i = 0; i < (npts - 1); i++)
    {
      points->GetPoint(pts[i], x1);
      points->GetPoint(pts[i + 1], x2);

      // intersect each segment with the four planes
      if ((x1[0] < p1[0] && x2[0] < p1[0]) || (x1[0] > p2[0] && x2[0] > p2[0]) ||
        (x1[1] < p1[1] && x2[1] < p1[1]) || (x1[1] > p2[1] && x2[1] > p2[1]))
      {
        // trivial rejection
      }
      else if (x1[0] >= p1[0] && x2[0] >= p1[0] && x1[0] <= p2[0] && x2[0] <= p2[0] &&
        x1[1] >= p1[1] && x2[1] >= p1[1] && x1[1] <= p2[1] && x2[1] <= p2[1])
      { // trivial acceptance
        newPts[0] = pointMap[pts[i]];
        newPts[1] = pointMap[pts[i + 1]];
        newLines->InsertNextCell(2, newPts);
      }
      else
      {
        newPts[0] = -1;
        newPts[1] = -1;
        if (x1[0] >= p1[0] && x1[0] <= p2[0] && x1[1] >= p1[1] && x1[1] <= p2[1])
        { // first point in
          newPts[0] = pointMap[pts[i]];
        }
        else if (x2[0] >= p1[0] && x2[0] <= p2[0] && x2[1] >= p1[1] && x2[1] <= p2[1])
        { // second point in
          newPts[0] = pointMap[pts[i + 1]];
        }

        // only create cell if either x1 or x2 is inside the range
        if (newPts[0] >= 0)
        {
          for (j = 0; j < 4; j++)
          {
            this->ClipPlanes->GetPoints()->GetPoint(j, px);
            this->ClipPlanes->GetNormals()->GetTuple(j, n);
            if (vtkPlane::IntersectWithLine(x1, x2, n, px, t, xint) && t >= 0 && t <= 1.0)
            {
              newPts[1] = newPoints->InsertNextPoint(xint);
              break;
            }
          }
          if (newPts[1] >= 0)
          {
            newLines->InsertNextCell(2, newPts);
          }
        }
      }
    }
  }
  delete[] pointMap;

  // Update the lines
  pd->SetPoints(newPoints);
  pd->SetVerts(newVerts);
  pd->SetLines(newLines);

  newPoints->Delete();
  newVerts->Delete();
  newLines->Delete();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetDataObjectXComponent(int i, int comp)
{
  i = std::min(std::max(i, 0), VTK_MAX_PLOTS - 1);
  int val = this->XComponent->GetValue(i);
  if (val != comp)
  {
    this->Modified();
    this->XComponent->SetValue(i, comp);
  }
}

//------------------------------------------------------------------------------
int vtkXYPlotActor::GetDataObjectXComponent(int i)
{
  i = std::min(std::max(i, 0), VTK_MAX_PLOTS - 1);
  return this->XComponent->GetValue(i);
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetDataObjectYComponent(int i, int comp)
{
  i = std::min(std::max(i, 0), VTK_MAX_PLOTS - 1);
  int val = this->YComponent->GetValue(i);
  if (val != comp)
  {
    this->Modified();
    this->YComponent->SetValue(i, comp);
  }
}

//------------------------------------------------------------------------------
int vtkXYPlotActor::GetDataObjectYComponent(int i)
{
  i = std::min(std::max(i, 0), VTK_MAX_PLOTS - 1);
  return this->YComponent->GetValue(i);
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetPointComponent(int i, int comp)
{
  i = std::min(std::max(i, 0), VTK_MAX_PLOTS - 1);
  int val = this->XComponent->GetValue(i);
  if (val != comp)
  {
    this->Modified();
    this->XComponent->SetValue(i, comp);
  }
}

//------------------------------------------------------------------------------
int vtkXYPlotActor::GetPointComponent(int i)
{
  i = std::min(std::max(i, 0), VTK_MAX_PLOTS - 1);
  return this->XComponent->GetValue(i);
}

//------------------------------------------------------------------------------
double* vtkXYPlotActor::TransformPoint(int pos[2], int pos2[2], double x[3], double xNew[3])
{
  // First worry about exchanging axes
  if (this->ExchangeAxes)
  {
    double sx = (x[0] - pos[0]) / (pos2[0] - pos[0]);
    double sy = (x[1] - pos[1]) / (pos2[1] - pos[1]);
    xNew[0] = sy * (pos2[0] - pos[0]) + pos[0];
    xNew[1] = sx * (pos2[1] - pos[1]) + pos[1];
    xNew[2] = x[2];
  }
  else
  {
    xNew[0] = x[0];
    xNew[1] = x[1];
    xNew[2] = x[2];
  }

  // Okay, now swap the axes around if reverse is on
  if (this->ReverseXAxis)
  {
    xNew[0] = pos[0] + (pos2[0] - xNew[0]);
  }
  if (this->ReverseYAxis)
  {
    xNew[1] = pos[1] + (pos2[1] - xNew[1]);
  }

  return xNew;
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetYTitle(const char* ytitle)
{
  this->YTitleActor->SetInput(ytitle);
  this->Modified();
}

//------------------------------------------------------------------------------
char* vtkXYPlotActor::GetYTitle()
{
  return this->YTitleActor->GetInput();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetXTitlePosition(double position)
{
  this->XAxis->SetTitlePosition(position);
  this->Modified();
}

//------------------------------------------------------------------------------
double vtkXYPlotActor::GetXTitlePosition()
{
  return this->XAxis->GetTitlePosition();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetAdjustXLabels(int adjust)
{
  this->AdjustXLabels = adjust;
  this->XAxis->SetAdjustLabels(adjust);
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetAdjustYLabels(int adjust)
{
  this->AdjustYLabels = adjust;
  this->YAxis->SetAdjustLabels(adjust);
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetLabelFormat(const char* _arg)
{
  this->SetXLabelFormat(_arg);
  this->SetYLabelFormat(_arg);
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetXLabelFormat(const char* _arg)
{
  if (this->XLabelFormat == nullptr && _arg == nullptr)
  {
    return;
  }

  if (this->XLabelFormat && _arg && (!strcmp(this->XLabelFormat, _arg)))
  {
    return;
  }

  delete[] this->XLabelFormat;

  if (_arg)
  {
    this->XLabelFormat = new char[strlen(_arg) + 1];
    strcpy(this->XLabelFormat, _arg);
  }
  else
  {
    this->XLabelFormat = nullptr;
  }

  this->XAxis->SetLabelFormat(this->XLabelFormat);

  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetYLabelFormat(const char* _arg)
{
  if (this->YLabelFormat == nullptr && _arg == nullptr)
  {
    return;
  }

  if (this->YLabelFormat && _arg && (!strcmp(this->YLabelFormat, _arg)))
  {
    return;
  }

  delete[] this->YLabelFormat;

  if (_arg)
  {
    this->YLabelFormat = new char[strlen(_arg) + 1];
    strcpy(this->YLabelFormat, _arg);
  }
  else
  {
    this->YLabelFormat = nullptr;
  }

  this->YAxis->SetLabelFormat(this->YLabelFormat);

  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetNumberOfXMinorTicks(int num)
{
  this->XAxis->SetNumberOfMinorTicks(num);
  this->Modified();
}

//------------------------------------------------------------------------------
int vtkXYPlotActor::GetNumberOfXMinorTicks()
{
  return this->XAxis->GetNumberOfMinorTicks();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetNumberOfYMinorTicks(int num)
{
  this->YAxis->SetNumberOfMinorTicks(num);
  this->Modified();
}

//------------------------------------------------------------------------------
int vtkXYPlotActor::GetNumberOfYMinorTicks()
{
  return this->YAxis->GetNumberOfMinorTicks();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::PrintAsCSV(ostream& os)
{
  vtkDataArray* scalars;
  vtkDataSet* ds;
  double s;
  int component;
  int numDS = this->InputConnectionHolder->GetNumberOfInputConnections(0);
  for (int dsNum = 0; dsNum < numDS; dsNum++)
  {
    vtkAlgorithmOutput* port = this->InputConnectionHolder->GetInputConnection(0, dsNum);
    vtkAlgorithm* alg = port->GetProducer();
    int portIndex = port->GetIndex();
    ds = vtkDataSet::SafeDownCast(alg->GetOutputDataObject(portIndex));
    vtkIdType numPts = ds->GetNumberOfPoints();
    scalars = ds->GetPointData()->GetScalars(this->SelectedInputScalars[dsNum]);
    os << this->SelectedInputScalars[dsNum] << ",";

    component = this->SelectedInputScalarsComponent->GetValue(dsNum);
    for (vtkIdType ptId = 0; ptId < numPts; ptId++)
    {
      s = scalars->GetComponent(ptId, component);
      if (ptId == 0)
      {
        os << s;
      }
      else
      {
        os << "," << s;
      }
    }
    os << endl;

    if (dsNum == numDS - 1)
    {
      os << "X or T,";
      for (vtkIdType ptId = 0; ptId < numPts; ptId++)
      {
        double* x = ds->GetPoint(ptId);
        if (ptId == 0)
        {
          os << x[0];
        }
        else
        {
          os << "," << x[0];
        }
      }
      os << endl;
    }
  }
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::AddUserCurvesPoint(double c_dbl, double x, double y)
{
  int c = static_cast<int>(c_dbl);
  if (this->ActiveCurveIndex != c)
  {
    vtkPolyData* dataObj = vtkPolyData::New();
    dataObj->GetFieldData()->AddArray(this->ActiveCurve);
    this->AddDataObjectInput(dataObj);
    this->SetDataObjectXComponent(this->ActiveCurveIndex, 0);
    this->SetDataObjectYComponent(this->ActiveCurveIndex, 1);
    dataObj->Delete();
    this->ActiveCurve = vtkSmartPointer<vtkDoubleArray>::New();
    this->ActiveCurve->SetNumberOfComponents(2);
    this->ActiveCurveIndex = c;
  }
  this->ActiveCurve->InsertNextTuple2(x, y);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::RemoveAllActiveCurves()
{
  this->ActiveCurveIndex = 0;
  this->ActiveCurve = vtkSmartPointer<vtkDoubleArray>::New();
  this->ActiveCurve->SetNumberOfComponents(2);
  this->Modified();
}

//------------------------------------------------------------------------------
//  Glyph type
//  \li 0 : nothing
//  \li 1 : vertex - not visible
//  \li 2 : line
//  \li 3 : cross
//  \li 4 : thick cross
//  \li 5 : triangle
//  \li 6 : square
//  \li 7 : circle
//  \li 8 : diamond
//  \li 9 : arrow
//  \li 10 : thick arrow
//  \li 11 : curved arrow
//  \li 12 : arrow
//  \li 13 : nothing
//  \li 14 : nothing
//  \li 15 : 2 + fillOff
//  \li 16 : nothing
//  \li 17 : 4 + fillOff
//  \li 18 : 5 + fillOff
//  \li 19 : 6 + fillOff
//  \li 20 : 7 + fillOff
//  \li 21 : 8 + fillOff
//  \li 22 : nothing
//  \li 23 : 10 + fillOff
//  \li 24 : 11 + fillOff
//  \li 25 : 12 + fillOff*
void vtkXYPlotActor::SetPlotGlyphType(int curve, int i)
{
  static const int good_glyph_type[26] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 2, 0, 4,
    5, 6, 7, 8, 0, 10, 11, 12 };
  vtkSmartPointer<vtkGlyphSource2D> source = vtkSmartPointer<vtkGlyphSource2D>::New();
  source->SetGlyphType(good_glyph_type[i]);
  source->SetFilled((i > 12) ? 0 : 1);
  source->Update();

  vtkPolyData* glyph = vtkPolyData::SafeDownCast(source->GetOutputDataObject(0));
  this->SetPlotSymbol(curve, glyph);
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetXAxisColor(double r, double g, double b)
{
  this->XAxis->GetProperty()->SetColor(r, g, b);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetYAxisColor(double r, double g, double b)
{
  this->YAxis->GetProperty()->SetColor(r, g, b);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetLegendBorder(int b)
{
  this->LegendActor->SetBorder(b);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetLegendBox(int b)
{
  this->LegendActor->SetBox(b);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetLegendUseBackground(int b)
{
  this->LegendActor->SetUseBackground(b);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetLegendBackgroundColor(double r, double g, double b)
{
  this->LegendActor->SetBackgroundColor(r, g, b);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetLineWidth(double w)
{
  this->GetProperty()->SetLineWidth(w);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetTitleColor(double r, double g, double b)
{
  this->GetTitleTextProperty()->SetColor(r, g, b);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetTitleFontFamily(int x)
{
  this->GetTitleTextProperty()->SetFontFamily(x);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetTitleBold(int x)
{
  this->GetTitleTextProperty()->SetBold(x);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetTitleItalic(int x)
{
  this->GetTitleTextProperty()->SetItalic(x);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetTitleShadow(int x)
{
  this->GetTitleTextProperty()->SetShadow(x);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetTitleFontSize(int x)
{
  this->GetTitleTextProperty()->SetFontSize(x);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetTitleJustification(int x)
{
  this->GetTitleTextProperty()->SetJustification(x);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetTitleVerticalJustification(int x)
{
  this->GetTitleTextProperty()->SetVerticalJustification(x);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetAxisTitleColor(double r, double g, double b)
{
  this->GetAxisTitleTextProperty()->SetColor(r, g, b);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetAxisTitleFontFamily(int x)
{
  this->GetAxisTitleTextProperty()->SetFontFamily(x);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetAxisTitleBold(int x)
{
  this->GetAxisTitleTextProperty()->SetBold(x);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetAxisTitleItalic(int x)
{
  this->GetAxisTitleTextProperty()->SetItalic(x);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetAxisTitleShadow(int x)
{
  this->GetAxisTitleTextProperty()->SetShadow(x);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetAxisTitleFontSize(int x)
{
  this->GetAxisTitleTextProperty()->SetFontSize(x);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetAxisTitleJustification(int x)
{
  this->GetAxisTitleTextProperty()->SetJustification(x);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetAxisTitleVerticalJustification(int x)
{
  this->GetAxisTitleTextProperty()->SetVerticalJustification(x);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetAxisTitleTextProperty(vtkTextProperty* p)
{
  // NB: Perform shallow copy here since each individual axis can be
  // accessed through the class API ( i.e. each individual axis text prop
  // can be changed ). Therefore, we can not just assign pointers otherwise
  // each individual axis text prop would point to the same text prop.
  this->AxisTitleTextProperty->ShallowCopy(p);
  this->YTitleActor->GetTextProperty()->ShallowCopy(p);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetAxisLabelColor(double r, double g, double b)
{
  this->GetAxisLabelTextProperty()->SetColor(r, g, b);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetAxisLabelFontFamily(int x)
{
  this->GetAxisLabelTextProperty()->SetFontFamily(x);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetAxisLabelBold(int x)
{
  this->GetAxisLabelTextProperty()->SetBold(x);
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetAxisLabelItalic(int x)
{
  this->GetAxisLabelTextProperty()->SetItalic(x);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetAxisLabelShadow(int x)
{
  this->GetAxisLabelTextProperty()->SetShadow(x);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetAxisLabelFontSize(int x)
{
  this->GetAxisLabelTextProperty()->SetFontSize(x);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetAxisLabelJustification(int x)
{
  this->GetAxisLabelTextProperty()->SetJustification(x);
  this->Modified();
}

//------------------------------------------------------------------------------
void vtkXYPlotActor::SetAxisLabelVerticalJustification(int x)
{
  this->GetAxisLabelTextProperty()->SetVerticalJustification(x);
  this->Modified();
}
VTK_ABI_NAMESPACE_END
