text: /*=========================================================================
  Program:   Visualization Toolkit
  Module:    vtkParallelCoordinatesView.cxx
  
  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
  
  This software is distributed WITHOUT ANY WARRANTY; without even
  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  PURPOSE.  See the above copyright notice for more information.
  
  =========================================================================*/
  /*-------------------------------------------------------------------------
    Copyright 2009 Sandia Corporation.
    Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
    the U.S. Government retains certain rights in this software.
    -------------------------------------------------------------------------*/
  #include "vtkParallelCoordinatesView.h"
  
  #include "vtkActor2D.h"
  #include "vtkAlgorithmOutput.h"
  #include "vtkBalloonRepresentation.h"
  #include "vtkCellArray.h"
  #include "vtkCellArrayIterator.h"
  #include "vtkCommand.h"
  #include "vtkDataSetAttributes.h"
  #include "vtkDoubleArray.h"
  #include "vtkIdList.h"
  #include "vtkObjectFactory.h"
  #include "vtkOutlineSource.h"
  #include "vtkParallelCoordinatesHistogramRepresentation.h"
  #include "vtkParallelCoordinatesInteractorStyle.h"
  #include "vtkParallelCoordinatesRepresentation.h"
  #include "vtkPoints.h"
  #include "vtkPolyData.h"
  #include "vtkPolyDataMapper2D.h"
  #include "vtkProperty2D.h"
  #include "vtkRenderWindow.h"
  #include "vtkRenderWindowInteractor.h"
  #include "vtkRendererCollection.h"
  #include "vtkSCurveSpline.h"
  #include "vtkSelection.h"
  #include "vtkSelectionNode.h"
  #include "vtkTable.h"
  #include "vtkTextProperty.h"
  #include "vtkViewTheme.h"
  
  #include <cassert>
  #include <sstream>
  
  vtkStandardNewMacro(vtkParallelCoordinatesView);
  
  //------------------------------------------------------------------------------
  
  vtkParallelCoordinatesView::vtkParallelCoordinatesView()
  {
    vtkParallelCoordinatesInteractorStyle* istyle = vtkParallelCoordinatesInteractorStyle::New();
    this->SetInteractorStyle(istyle);
    istyle->Delete();
  
    this->ReuseSingleRepresentationOn();
  
    istyle->AddObserver(vtkCommand::StartInteractionEvent, this->GetObserver());
    istyle->AddObserver(vtkCommand::InteractionEvent, this->GetObserver());
    istyle->AddObserver(vtkCommand::EndInteractionEvent, this->GetObserver());
    istyle->AddObserver(vtkCommand::UpdateEvent, this->GetObserver());
  
    this->BrushData = vtkSmartPointer<vtkPolyData>::New();
    this->BrushMapper = vtkSmartPointer<vtkPolyDataMapper2D>::New();
    this->BrushActor = vtkSmartPointer<vtkActor2D>::New();
  
    vtkSmartPointer<vtkCoordinate> dummycoord = vtkSmartPointer<vtkCoordinate>::New();
    dummycoord->SetCoordinateSystemToNormalizedViewport();
    this->BrushMapper->SetInputData(this->BrushData);
    this->BrushMapper->SetTransformCoordinate(dummycoord);
    this->BrushActor->SetMapper(this->BrushMapper);
    this->BrushActor->GetProperty()->SetColor(.1, 1.0, 1.0);
  
    this->InspectMode = VTK_INSPECT_MANIPULATE_AXES;
    this->BrushMode = VTK_BRUSH_LASSO;
    this->BrushOperator = VTK_BRUSHOPERATOR_ADD;
    this->MaximumNumberOfBrushPoints = -1;
    this->NumberOfBrushPoints = 0;
    this->SetMaximumNumberOfBrushPoints(100);
    this->ClearBrushPoints();
    this->FirstFunctionBrushLineDrawn = 0;
    this->CurrentBrushClass = 0;
    this->AxisHighlightPosition = VTK_HIGHLIGHT_CENTER;
  
    this->SelectedAxisPosition = -1;
    this->HighlightSource = vtkSmartPointer<vtkOutlineSource>::New();
    this->HighlightMapper = vtkSmartPointer<vtkPolyDataMapper2D>::New();
    this->HighlightActor = vtkSmartPointer<vtkActor2D>::New();
  
    this->HighlightSource->SetBounds(-1, -1, -1, -1, -1, -1);
    this->HighlightMapper->SetInputConnection(this->HighlightSource->GetOutputPort());
    this->HighlightMapper->SetTransformCoordinate(dummycoord);
    this->HighlightActor->SetMapper(this->HighlightMapper);
    this->HighlightActor->GetProperty()->SetColor(.1, 1.0, .1);
    this->HighlightActor->VisibilityOff();
  
    //  vtkSmartPointer<vtkViewTheme> theme = vtkSmartPointer<vtkViewTheme>::New();
    //  theme->SetBackgroundColor(0,0,0);
    //  theme->SetBackgroundColor2(0,0,0);
    //  this->ApplyViewTheme(theme);
  }
  
  //------------------------------------------------------------------------------
  vtkParallelCoordinatesView::~vtkParallelCoordinatesView()
  {
    // nothing to do
  }
  
  void vtkParallelCoordinatesView::PrepareForRendering()
  {
    vtkDebugMacro(<< "*** PrepareForRendering called");
  
    vtkParallelCoordinatesRepresentation* rep =
      vtkParallelCoordinatesRepresentation::SafeDownCast(this->GetRepresentation());
  
    if (rep)
    {
      vtkRenderer* ren = this->GetRenderer();
  
      this->Superclass::PrepareForRendering();
  
      if (!ren->HasViewProp(this->HighlightActor))
      {
        ren->AddActor(this->HighlightActor);
      }
      if (!ren->HasViewProp(this->BrushActor))
      {
        ren->AddActor(this->BrushActor);
      }
  
      // this is a hack to make sure that the balloon hover text is sitting on top
      if (ren->HasViewProp(this->Balloon))
      {
        this->Renderer->RemoveViewProp(this->Balloon);
        this->Renderer->AddViewProp(this->Balloon);
      }
    }
  }
  
  //------------------------------------------------------------------------------
  void vtkParallelCoordinatesView::PrintSelf(ostream& os, vtkIndent indent)
  {
    this->Superclass::PrintSelf(os, indent);
    os << "MaximumNumberOfBrushPoints: " << this->MaximumNumberOfBrushPoints << endl;
    os << "BrushOperator: " << this->BrushOperator << endl;
    os << "BrushMode: " << this->BrushMode << endl;
    os << "InspectMode: " << this->InspectMode << endl;
    os << "CurrentBrushClass: " << this->CurrentBrushClass << endl;
  }
  
  //------------------------------------------------------------------------------
  // The frustum selection code is borrowed from vtkRenderView.
  void vtkParallelCoordinatesView::ProcessEvents(
    vtkObject* caller, unsigned long eventId, void* callData)
  {
  
    //  if (caller == this->GetInteractor() || caller == this->GetInteractorStyle())
    if (caller == this->GetInteractorStyle())
    {
      vtkParallelCoordinatesInteractorStyle* style =
        vtkParallelCoordinatesInteractorStyle::SafeDownCast(this->GetInteractorStyle());
      vtkParallelCoordinatesRepresentation* rep =
        vtkParallelCoordinatesRepresentation::SafeDownCast(this->GetRepresentation());
  
      if (style && rep)
      {
        int state = style->GetState();
  
        if (eventId == vtkCommand::UpdateEvent)
        {
          rep->ResetAxes();
        }
        else
        {
          switch (state)
          {
            case vtkParallelCoordinatesInteractorStyle::INTERACT_HOVER:
              this->Hover(eventId);
              break;
            case vtkParallelCoordinatesInteractorStyle::INTERACT_INSPECT:
              if (this->InspectMode == VTK_INSPECT_MANIPULATE_AXES)
              {
                this->ManipulateAxes(eventId);
              }
              else if (this->InspectMode == VTK_INSPECT_SELECT_DATA)
              {
                this->SelectData(eventId);
              }
              break;
            case vtkParallelCoordinatesInteractorStyle::INTERACT_ZOOM:
              this->Zoom(eventId);
              break;
            case vtkParallelCoordinatesInteractorStyle::INTERACT_PAN:
              this->Pan(eventId);
              break;
          }
        }
  
        this->Render();
      }
    }
  
    this->Superclass::ProcessEvents(caller, eventId, callData);
  }
  
  vtkDataRepresentation* vtkParallelCoordinatesView::CreateDefaultRepresentation(
    vtkAlgorithmOutput* conn)
  {
    vtkParallelCoordinatesHistogramRepresentation* rep =
      vtkParallelCoordinatesHistogramRepresentation::New();
    rep->SetInputConnection(conn);
    vtkDataObject* data = conn->GetProducer()->GetOutputDataObject(0);
    vtkTable* td = vtkTable::SafeDownCast(data);
  
    if (!td)
    {
      rep->SetInputArrayToProcess(
        0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS_THEN_CELLS, vtkDataSetAttributes::SCALARS);
    }
    else
    {
      int ncols = td->GetNumberOfColumns();
      for (int i = 0; i < ncols; i++)
      {
        vtkAbstractArray* a = td->GetColumn(i);
        rep->SetInputArrayToProcess(i, 0, 0, vtkDataObject::FIELD_ASSOCIATION_ROWS, a->GetName());
      }
    }
  
    return rep;
  }
  
  int vtkParallelCoordinatesView::SetAxisHighlightPosition(
    vtkParallelCoordinatesRepresentation* rep, int position)
  {
    int numAxes = rep->GetNumberOfAxes();
  
    if (numAxes <= 0)
      return -1;
  
    double p1[2], p2[2];
    rep->GetPositionAndSize(p1, p2);
    double xpos = rep->GetXCoordinateOfPosition(position);
  
    if (xpos < 0 || position < 0 || position >= numAxes)
    {
      this->HighlightSource->SetBounds(-1, -1, -1, -1, -1, -1);
      this->HighlightActor->VisibilityOff();
      return -1;
    }
    else
    {
      double xmargin = .3 * p2[0] / static_cast<double>(numAxes);
      double ymargin = .05 * p2[1];
      if (this->AxisHighlightPosition == VTK_HIGHLIGHT_CENTER)
      {
        this->HighlightSource->SetBounds(
          xpos - xmargin, xpos + xmargin, p1[1] + ymargin, p1[1] + p2[1] - ymargin, 0, 0);
      }
      else if (this->AxisHighlightPosition == VTK_HIGHLIGHT_MIN)
      {
        this->HighlightSource->SetBounds(
          xpos - xmargin, xpos + xmargin, p1[1] - ymargin, p1[1] + ymargin, 0, 0);
      }
      else if (this->AxisHighlightPosition == VTK_HIGHLIGHT_MAX)
      {
        this->HighlightSource->SetBounds(
          xpos - xmargin, xpos + xmargin, p1[1] + p2[1] - ymargin, p1[1] + p2[1] + ymargin, 0, 0);
      }
      this->HighlightSource->Update();
      this->HighlightActor->VisibilityOn();
    }
  
    return position;
  }
  
  int vtkParallelCoordinatesView::SetAxisHighlightPosition(
    vtkParallelCoordinatesRepresentation* rep, double position)
  {
    int nearestPosition = rep->GetPositionNearXCoordinate(position);
  
    return this->SetAxisHighlightPosition(rep, nearestPosition);
  }
  
  void vtkParallelCoordinatesView::SetBrushMode(int mode)
  {
    if (mode < 0 || mode >= VTK_BRUSH_MODECOUNT)
    {
      return;
    }
    else
    {
      this->BrushMode = mode;
  
      // if we made it into function mode but left early, clear the lines
      if (this->FirstFunctionBrushLineDrawn && this->BrushMode != VTK_BRUSH_FUNCTION)
      {
        this->FirstFunctionBrushLineDrawn = 0;
        this->ClearBrushPoints();
        this->Render();
      }
    }
  }
  
  void vtkParallelCoordinatesView::SetBrushOperator(int op)
  {
    if (op < 0 || op >= VTK_BRUSHOPERATOR_MODECOUNT)
    {
      return;
    }
    else
    {
      this->BrushOperator = op;
    }
  }
  
  void vtkParallelCoordinatesView::SetInspectMode(int mode)
  {
    if (mode < 0 || mode >= VTK_INSPECT_MODECOUNT)
    {
      return;
    }
    else
    {
      this->InspectMode = mode;
  
      if (this->InspectMode != VTK_INSPECT_MANIPULATE_AXES)
      {
        this->HighlightActor->VisibilityOff();
      }
    }
  }
  
  void vtkParallelCoordinatesView::SetMaximumNumberOfBrushPoints(int num)
  {
    if (num >= 2 && num != this->MaximumNumberOfBrushPoints)
    {
      this->MaximumNumberOfBrushPoints = num;
  
      vtkSmartPointer<vtkPoints> pts = vtkSmartPointer<vtkPoints>::New();
      pts->SetNumberOfPoints(4 * this->MaximumNumberOfBrushPoints);
  
      for (int i = 0; i < 4 * this->MaximumNumberOfBrushPoints; i++)
        pts->InsertPoint(i, -1, -1, 0);
  
      vtkSmartPointer<vtkCellArray> lines = vtkSmartPointer<vtkCellArray>::New();
      lines->AllocateEstimate(4, this->MaximumNumberOfBrushPoints);
  
      // first line is for a manually drawn curve, for selecting lines
      // second line is for the spline used for angular brushing
      // third and fourth lines are for the splines used for function brushing
      for (int i = 0; i < 4; i++)
      {
        lines->InsertNextCell(this->MaximumNumberOfBrushPoints);
        for (int j = 0; j < this->MaximumNumberOfBrushPoints; j++)
          lines->InsertCellPoint(0);
      }
  
      this->BrushData->SetPoints(pts);
      this->BrushData->SetLines(lines);
    }
  }
  
  void vtkParallelCoordinatesView::ClearBrushPoints()
  {
    this->NumberOfBrushPoints = 0;
  
    vtkIdType npts = this->BrushData->GetPoints()->GetNumberOfPoints();
    for (vtkIdType i = 0; i < npts; i++)
      this->BrushData->GetPoints()->SetPoint(i, -1, -1, 0);
  
    // clear them for all of the lines
    vtkNew<vtkIdList> cell;
    auto cellIter = vtk::TakeSmartPointer(this->BrushData->GetLines()->NewIterator());
    for (cellIter->GoToFirstCell(); !cellIter->IsDoneWithTraversal(); cellIter->GoToNextCell())
    {
      const vtkIdType cellNum = cellIter->GetCurrentCellId();
      cellIter->GetCurrentCell(cell);
  
      for (vtkIdType j = 0; j < cell->GetNumberOfIds(); j++)
      {
        cell->SetId(j, cellNum * this->MaximumNumberOfBrushPoints);
      }
  
      cellIter->ReplaceCurrentCell(cell);
    }
  
    this->BrushData->Modified();
  }
  
  //------------------------------------------------------------------------------
  int vtkParallelCoordinatesView::AddLassoBrushPoint(double* p)
  {
    if (this->NumberOfBrushPoints >= this->MaximumNumberOfBrushPoints)
      return 0;
  
    vtkIdType ptid = this->NumberOfBrushPoints;
    this->BrushData->GetPoints()->SetPoint(ptid, p[0], p[1], 0);
  
    const vtkIdType npts = this->BrushData->GetLines()->GetCellSize(0);
    std::vector<vtkIdType> ptids(static_cast<vtkIdType>(npts));
  
    for (vtkIdType i = ptid; i < npts; i++)
    {
      ptids[static_cast<size_t>(i)] = ptid;
    }
  
    this->BrushData->GetLines()->ReplaceCellAtId(0, npts, ptids.data());
  
    this->NumberOfBrushPoints++;
    this->BrushData->Modified();
  
    return 1;
  }
  
  //------------------------------------------------------------------------------
  int vtkParallelCoordinatesView::SetAngleBrushLine(double* p1, double* p2)
  {
    this->SetBrushLine(1, p1, p2);
    return 1;
  }
  
  //------------------------------------------------------------------------------
  int vtkParallelCoordinatesView::SetFunctionBrushLine1(double* p1, double* p2)
  {
    this->SetBrushLine(2, p1, p2);
    return 1;
  }
  //------------------------------------------------------------------------------
  int vtkParallelCoordinatesView::SetFunctionBrushLine2(double* p1, double* p2)
  {
    this->SetBrushLine(3, p1, p2);
    return 1;
  }
  
  //------------------------------------------------------------------------------
  int vtkParallelCoordinatesView::SetBrushLine(int line, double* p1, double* p2)
  {
    double p1x = p1[0], p1y = p1[1];
    double p2x = p2[0], p2y = p2[1];
    vtkParallelCoordinatesRepresentation* rep =
      vtkParallelCoordinatesRepresentation::SafeDownCast(this->GetRepresentation());
    int numAxes = rep->GetNumberOfAxes();
    double* xs = new double[numAxes];
    rep->GetXCoordinatesOfPositions(xs);
  
    if (p1x == p2x)
    {
      delete[] xs;
      return 0;
    }
  
    // swap, if necessary...I don't think the splines like being out of order
    if (p1x > p2x)
    {
      double tmp;
      tmp = p1x;
      p1x = p2x;
      p2x = tmp;
      tmp = p1y;
      p1y = p2y;
      p2y = tmp;
    }
  
    // find the axis to the left of p1
    int left = numAxes - 1;
    for (int i = 0; i < numAxes; i++)
    {
      if (p1x > xs[i])
      {
        left = i;
      }
      else
      {
        break;
      }
    }
  
    int right = left + 1;
  
    // sanity check
    if (right >= numAxes || left < 0)
    {
      delete[] xs;
      return 0;
    }
  
    // find the points that line (p1-p2) intersects on the left/right axes
    double m = (double)(p2y - p1y) / (double)(p2x - p1x);
    double lefty = p1y - m * (p1x - xs[left]);
    double righty = p1y - m * (p1x - xs[right]);
  
    p1x = xs[left];
    p2x = xs[right];
    p1y = lefty;
    p2y = righty;
    // p1y = std::max<int>(std::min<int>(lefty,this->YMax),this->YMin);
    // p2y = std::max<int>(std::min<int>(righty,this->YMax),this->YMin);
  
    // sanity check
    if (p1x >= p2x)
    {
      delete[] xs;
      return 0;
    }
  
    int pointOffset = line * this->MaximumNumberOfBrushPoints;
  
    double dx = (double)(p2x - p1x) / (this->MaximumNumberOfBrushPoints - 1);
  
    if (!rep->GetUseCurves())
    {
      double dy = (double)(p2y - p1y) / (this->MaximumNumberOfBrushPoints - 1);
  
      for (int i = 0; i < this->MaximumNumberOfBrushPoints; i++)
      {
        this->BrushData->GetPoints()->SetPoint(pointOffset + i, p1x + i * dx, p1y + i * dy, 0);
      }
    }
    else
    {
      vtkSmartPointer<vtkSCurveSpline> spline = vtkSmartPointer<vtkSCurveSpline>::New();
      spline->SetParametricRange(p1x, p2x);
      spline->AddPoint(p1x, p1y);
      spline->AddPoint(p2x, p2y);
  
      for (int i = 0; i < this->MaximumNumberOfBrushPoints; i++)
      {
        this->BrushData->GetPoints()->SetPoint(
          pointOffset + i, p1x + (double)i * dx, spline->Evaluate(p1x + (double)i * dx), 0);
      }
    }
  
    vtkNew<vtkIdList> cell;
    this->BrushData->GetLines()->GetCellAtId(line, cell);
  
    for (vtkIdType j = 0; j < cell->GetNumberOfIds(); j++)
    {
      cell->SetId(j, pointOffset + j);
    }
  
    this->BrushData->GetLines()->ReplaceCellAtId(line, cell);
    this->BrushData->Modified();
  
    delete[] xs;
    return 1;
  }
  
  //------------------------------------------------------------------------------
  void vtkParallelCoordinatesView::GetBrushLine(int line, vtkIdType& npts, vtkIdType const*& ptids)
  {
    int cellNum = 0;
    for (this->BrushData->GetLines()->InitTraversal();
         this->BrushData->GetLines()->GetNextCell(npts, ptids); cellNum++)
    {
      if (cellNum == line)
      {
        return;
      }
    }
  }
  
  void vtkParallelCoordinatesView::Hover(unsigned long eventId)
  {
    vtkParallelCoordinatesInteractorStyle* style =
      vtkParallelCoordinatesInteractorStyle::SafeDownCast(this->GetInteractorStyle());
    vtkParallelCoordinatesRepresentation* rep =
      vtkParallelCoordinatesRepresentation::SafeDownCast(this->GetRepresentation());
  
    double p1[2], p2[2];
    if (!rep->GetPositionAndSize(p1, p2))
      return;
  
    double cursorPosition[2] = { 0, 0 };
    style->GetCursorCurrentPosition(this->GetRenderer(), cursorPosition);
  
    // deal with hovering
    if (this->InspectMode == VTK_INSPECT_MANIPULATE_AXES && eventId == vtkCommand::InteractionEvent)
    {
      // if we're close to the representation
      if (cursorPosition[0] >= 0.0 && cursorPosition[0] <= 1.0 && cursorPosition[1] >= 0.0 &&
        cursorPosition[1] <= 1.0 && cursorPosition[0] > p1[0] - .05 * p2[0] &&
        cursorPosition[0] < p1[0] + 1.05 * p2[0])
      {
  
        this->SelectedAxisPosition = rep->GetPositionNearXCoordinate(cursorPosition[0]);
        double xpos = rep->GetXCoordinateOfPosition(this->SelectedAxisPosition);
  
        if (fabs(xpos - cursorPosition[0]) > .05)
        {
          this->SelectedAxisPosition = -1;
        }
        else if (cursorPosition[1] < p1[1] + .05 * p2[1])
        {
          this->AxisHighlightPosition = VTK_HIGHLIGHT_MIN;
        }
        else if (cursorPosition[1] > p1[1] + .95 * p2[1])
        {
          this->AxisHighlightPosition = VTK_HIGHLIGHT_MAX;
        }
        else
        {
          this->AxisHighlightPosition = VTK_HIGHLIGHT_CENTER;
        }
        this->SetAxisHighlightPosition(rep, this->SelectedAxisPosition);
      }
      else
      {
        this->SelectedAxisPosition = -1;
        this->SetAxisHighlightPosition(rep, this->SelectedAxisPosition);
      }
    }
  }
  
  void vtkParallelCoordinatesView::ManipulateAxes(unsigned long eventId)
  {
    vtkParallelCoordinatesInteractorStyle* style =
      vtkParallelCoordinatesInteractorStyle::SafeDownCast(this->GetInteractorStyle());
    vtkParallelCoordinatesRepresentation* rep =
      vtkParallelCoordinatesRepresentation::SafeDownCast(this->GetRepresentation());
  
    double cursorPosition[2], cursorLastPosition[2], cursorStartPosition[2];
    style->GetCursorCurrentPosition(this->GetRenderer(), cursorPosition);
    style->GetCursorLastPosition(this->GetRenderer(), cursorLastPosition);
    style->GetCursorStartPosition(this->GetRenderer(), cursorStartPosition);
  
    double dx = cursorPosition[0] - cursorLastPosition[0];
    double dy = cursorPosition[1] - cursorLastPosition[1];
  
    if (eventId == vtkCommand::StartInteractionEvent)
    {
    }
    else if (eventId == vtkCommand::InteractionEvent)
    {
      if (this->SelectedAxisPosition >= 0)
      {
        if (AxisHighlightPosition == VTK_HIGHLIGHT_CENTER)
        {
          double xpos = rep->GetXCoordinateOfPosition(this->SelectedAxisPosition);
          this->SelectedAxisPosition =
            rep->SetXCoordinateOfPosition(this->SelectedAxisPosition, xpos + dx);
          this->SetAxisHighlightPosition(rep, this->SelectedAxisPosition);
        }
        else
        {
          double range[] = { 0.0, 0.0 };
          rep->GetRangeAtPosition(this->SelectedAxisPosition, range);
  
          if (AxisHighlightPosition == VTK_HIGHLIGHT_MAX)
          {
            range[1] += dy * (range[1] - range[0]);
          }
          else if (AxisHighlightPosition == VTK_HIGHLIGHT_MIN)
          {
            range[0] += dy * (range[1] - range[0]);
          }
          rep->SetRangeAtPosition(this->SelectedAxisPosition, range);
        }
      }
    }
    else if (eventId == vtkCommand::EndInteractionEvent)
    {
      this->SelectedAxisPosition = -1;
    }
  }
  
  void vtkParallelCoordinatesView::SelectData(unsigned long eventId)
  {
    vtkParallelCoordinatesInteractorStyle* style =
      vtkParallelCoordinatesInteractorStyle::SafeDownCast(this->GetInteractorStyle());
    vtkParallelCoordinatesRepresentation* rep =
      vtkParallelCoordinatesRepresentation::SafeDownCast(this->GetRepresentation());
  
    double cursorPosition[2], cursorStartPosition[2];
    style->GetCursorCurrentPosition(this->GetRenderer(), cursorPosition);
    style->GetCursorStartPosition(this->GetRenderer(), cursorStartPosition);
  
    // in lasso mode, the user sketches a curve. Lines that are
    // near that curve are selected.
    if (this->BrushMode == VTK_BRUSH_LASSO)
    {
      if (eventId == vtkCommand::StartInteractionEvent)
      {
        this->AddLassoBrushPoint(cursorPosition);
      }
      else if (eventId == vtkCommand::InteractionEvent)
      {
        this->AddLassoBrushPoint(cursorPosition);
      }
      else if (eventId == vtkCommand::EndInteractionEvent)
      {
        const vtkIdType* ptids;
        vtkIdType npts;
        this->BrushData->GetLines()->GetCellAtId(0, npts, ptids);
  
        vtkSmartPointer<vtkPoints> pts = vtkSmartPointer<vtkPoints>::New();
        for (int i = 0; i < npts; i++)
        {
          pts->InsertNextPoint(this->BrushData->GetPoints()->GetPoint(ptids[i]));
        }
  
        rep->LassoSelect(this->CurrentBrushClass, this->BrushOperator, pts);
        /*
          vtkSmartPointer<vtkDoubleArray> locs = vtkSmartPointer<vtkDoubleArray>::New();
          locs->SetNumberOfComponents(2);
          locs->SetNumberOfTuples(this->NumberOfBrushPoints);
          for (int i=0; i<this->NumberOfBrushPoints; i++)
          {
          locs->SetTuple(i,this->BrushData->GetPoints()->GetPoint(ptids[i]));
          }
  
          // generate a selection based on the lasso points
          vtkSmartPointer<vtkSelection> sel = vtkSmartPointer<vtkSelection>::New();
          vtkSmartPointer<vtkParallelCoordinatesSelectionNode> node =
          vtkSmartPointer<vtkParallelCoordinatesSelectionNode>::New();
          node->SetSelectionOperator(this->BrushOperator);
          node->SetSelectionIndex(this->CurrentBrushClass);
          node->SetContentType(vtkSelectionNode::LOCATIONS);
          node->SetFieldType(vtkSelectionNode::ROW);
          node->SetSelectionList(locs);
  
          sel->AddNode(node);
          rep->Select(this,sel);
        */
        this->ClearBrushPoints();
      }
    }
    // in angle brush mode, the user clicks one point to start the
    // line.  the cursor position is the second endpoint of the line.
    else if (this->BrushMode == VTK_BRUSH_ANGLE)
    {
      if (eventId == vtkCommand::StartInteractionEvent || eventId == vtkCommand::InteractionEvent)
      {
        this->SetAngleBrushLine(cursorStartPosition, cursorPosition);
      }
      else if (eventId == vtkCommand::EndInteractionEvent)
      {
        const vtkIdType* ptids = nullptr;
        vtkIdType npts = 0;
        this->GetBrushLine(1, npts, ptids);
  
        double p1[3] = { 0, 0, 0 };
        double p2[3] = { 0, 0, 0 };
        this->BrushData->GetPoints()->GetPoint(ptids[0], p1);
        this->BrushData->GetPoints()->GetPoint(ptids[npts - 1], p2);
  
        rep->AngleSelect(this->CurrentBrushClass, this->BrushOperator, p1, p2);
        /*
          vtkSmartPointer<vtkDoubleArray> locs = vtkSmartPointer<vtkDoubleArray>::New();
          locs->SetNumberOfComponents(2);
          locs->SetNumberOfTuples(2);
          locs->SetTuple(0,p1);
          locs->SetTuple(1,p2);
  
          // generate a selection based on the angle brush points
          vtkSmartPointer<vtkSelection> sel = vtkSmartPointer<vtkSelection>::New();
          vtkSmartPointer<vtkParallelCoordinatesSelectionNode> node =
          vtkSmartPointer<vtkParallelCoordinatesSelectionNode>::New();
          node->SetSelectionOperator(this->BrushOperator);
          node->SetSelectionIndex(this->CurrentBrushClass);
          node->SetContentType(vtkParallelCoordinatesSelectionNode::ANGLE_LOCATIONS);
          node->SetFieldType(vtkSelectionNode::ROW);
          node->SetSelectionList(locs);
  
          sel->AddNode(node);
          rep->Select(this,sel);
        */
        this->ClearBrushPoints();
      }
    }
    // same as angle mode, but do two of them.
    else if (this->BrushMode == VTK_BRUSH_FUNCTION)
    {
      if (eventId == vtkCommand::StartInteractionEvent || eventId == vtkCommand::InteractionEvent)
      {
        if (!this->FirstFunctionBrushLineDrawn)
        {
          this->SetFunctionBrushLine1(cursorStartPosition, cursorPosition);
        }
        else
        {
          this->SetFunctionBrushLine2(cursorStartPosition, cursorPosition);
        }
      }
      else if (eventId == vtkCommand::EndInteractionEvent)
      {
        // if the first line isn't finished, keep going with the second.
        if (!this->FirstFunctionBrushLineDrawn)
        {
          this->FirstFunctionBrushLineDrawn = 1;
        }
        // the first line is finished, so do the selection
        else
        {
          const vtkIdType* ptids = nullptr;
          vtkIdType npts = 0;
  
          double p1[3] = { 0, 0, 0 };
          double p2[3] = { 0, 0, 0 };
          this->GetBrushLine(2, npts, ptids);
          this->BrushData->GetPoints()->GetPoint(ptids[0], p1);
          this->BrushData->GetPoints()->GetPoint(ptids[npts - 1], p2);
  
          double q1[3] = { 0, 0, 0 };
          double q2[3] = { 0, 0, 0 };
          this->GetBrushLine(3, npts, ptids);
          this->BrushData->GetPoints()->GetPoint(ptids[0], q1);
          this->BrushData->GetPoints()->GetPoint(ptids[npts - 1], q2);
  
          rep->FunctionSelect(this->CurrentBrushClass, this->BrushOperator, p1, p2, q1, q2);
  
          /*        vtkSmartPointer<vtkDoubleArray> locs = vtkSmartPointer<vtkDoubleArray>::New();
                    locs->SetNumberOfComponents(2);
                    locs->SetNumberOfTuples(4);
                    locs->SetTuple(0,p1);
                    locs->SetTuple(1,p2);
                    locs->SetTuple(2,q1);
                    locs->SetTuple(3,q2);
  
  
                    // generate a selection based on the angle brush points
                    vtkSmartPointer<vtkSelection> sel = vtkSmartPointer<vtkSelection>::New();
                    vtkSmartPointer<vtkParallelCoordinatesSelectionNode> node =
             vtkSmartPointer<vtkParallelCoordinatesSelectionNode>::New();
                    node->SetSelectionOperator(this->BrushOperator);
                    node->SetSelectionIndex(this->CurrentBrushClass);
                    node->SetContentType(vtkParallelCoordinatesSelectionNode::FUNCTION_LOCATIONS);
                    node->SetFieldType(vtkSelectionNode::ROW);
                    node->SetSelectionList(locs);
                    sel->AddNode(node);
                    rep->Select(this,sel);
          */
  
          this->FirstFunctionBrushLineDrawn = 0;
          this->ClearBrushPoints();
        }
      }
    }
    else if (this->BrushMode == VTK_BRUSH_AXISTHRESHOLD)
    {
    }
  }
  
  void vtkParallelCoordinatesView::Zoom(unsigned long eventId)
  {
    vtkParallelCoordinatesInteractorStyle* style =
      vtkParallelCoordinatesInteractorStyle::SafeDownCast(this->GetInteractorStyle());
    vtkParallelCoordinatesRepresentation* rep =
      vtkParallelCoordinatesRepresentation::SafeDownCast(this->GetRepresentation());
  
    double p1[2], size[2], p2[2];
    rep->GetPositionAndSize(p1, size);
    p2[0] = p1[0] + size[0];
    p2[1] = p1[1] + size[1];
  
    double cursorPosition[2], cursorLastPosition[2], cursorStartPosition[2];
    style->GetCursorCurrentPosition(this->GetRenderer(), cursorPosition);
    style->GetCursorLastPosition(this->GetRenderer(), cursorLastPosition);
    style->GetCursorStartPosition(this->GetRenderer(), cursorStartPosition);
  
    double v1[2] = { cursorStartPosition[0] - p1[0], cursorStartPosition[1] - p1[1] };
    double v2[2] = { cursorStartPosition[0] - p2[0], cursorStartPosition[1] - p2[1] };
  
    double dy = -(cursorPosition[1] - cursorLastPosition[1]);
  
    if (eventId == vtkCommand::StartInteractionEvent)
    {
    }
    else if (eventId == vtkCommand::InteractionEvent)
    {
      double p1new[2] = { p1[0] + dy * v1[0], p1[1] + dy * v1[1] };
      double p2new[2] = { p2[0] + dy * v2[0], // - p1new[0],
        p2[1] + dy * v2[1] };                 // - p1new[1]};
  
      double sizenew[] = { p2new[0] - p1new[0], p2new[1] - p1new[1] };
  
      rep->SetPositionAndSize(p1new, sizenew);
      this->SetAxisHighlightPosition(rep, this->SelectedAxisPosition);
    }
    else if (eventId == vtkCommand::EndInteractionEvent)
    {
    }
  }
  
  void vtkParallelCoordinatesView::Pan(unsigned long eventId)
  {
    vtkParallelCoordinatesInteractorStyle* style =
      vtkParallelCoordinatesInteractorStyle::SafeDownCast(this->GetInteractorStyle());
    vtkParallelCoordinatesRepresentation* rep =
      vtkParallelCoordinatesRepresentation::SafeDownCast(this->GetRepresentation());
  
    double p1[2], size[2], p2[2];
    rep->GetPositionAndSize(p1, size);
    p2[0] = p1[0] + size[0];
    p2[1] = p1[1] + size[1];
  
    double cursorPosition[2], cursorLastPosition[2], cursorStartPosition[2];
    style->GetCursorCurrentPosition(this->GetRenderer(), cursorPosition);
    style->GetCursorLastPosition(this->GetRenderer(), cursorLastPosition);
    style->GetCursorStartPosition(this->GetRenderer(), cursorStartPosition);
  
    double dx = cursorPosition[0] - cursorLastPosition[0];
    double dy = cursorPosition[1] - cursorLastPosition[1];
  
    if (eventId == vtkCommand::StartInteractionEvent)
    {
    }
    else if (eventId == vtkCommand::InteractionEvent)
    {
      double p1new[2] = { p1[0] + dx, p1[1] + dy };
      double p2new[2] = { p2[0] + dx, p2[1] + dy };
  
      double sizenew[] = { p2new[0] - p1new[0], p2new[1] - p1new[1] };
  
      rep->SetPositionAndSize(p1new, sizenew);
      this->SetAxisHighlightPosition(rep, this->SelectedAxisPosition);
    }
    else if (eventId == vtkCommand::EndInteractionEvent)
    {
    }
  }
  
  void vtkParallelCoordinatesView::ApplyViewTheme(vtkViewTheme* theme)
  {
    this->Superclass::ApplyViewTheme(theme);
    this->Balloon->GetFrameProperty()->SetColor(theme->GetBackgroundColor()); // CellColor());
    this->Balloon->GetTextProperty()->SetColor(theme->GetCellColor());        // BackgroundColor());
  }
  