// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#include "vtkOrientedGlyphContourRepresentation.h"
#include "vtkActor.h"
#include "vtkAssemblyPath.h"
#include "vtkBezierContourLineInterpolator.h"
#include "vtkCamera.h"
#include "vtkCellArray.h"
#include "vtkCleanPolyData.h"
#include "vtkCoordinate.h"
#include "vtkCursor2D.h"
#include "vtkCylinderSource.h"
#include "vtkDoubleArray.h"
#include "vtkFocalPlanePointPlacer.h"
#include "vtkGlyph3D.h"
#include "vtkInteractorObserver.h"
#include "vtkLine.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkPoints.h"
#include "vtkPolyData.h"
#include "vtkPolyDataMapper.h"
#include "vtkProperty.h"
#include "vtkRenderWindow.h"
#include "vtkRenderer.h"
#include "vtkSphereSource.h"
#include "vtkTransform.h"
#include "vtkTransformPolyDataFilter.h"

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkOrientedGlyphContourRepresentation);

//------------------------------------------------------------------------------
vtkOrientedGlyphContourRepresentation::vtkOrientedGlyphContourRepresentation()
{
  // Initialize state
  this->InteractionState = vtkContourRepresentation::Outside;

  this->CursorShape = nullptr;
  this->ActiveCursorShape = nullptr;

  this->HandleSize = 0.01;

  this->PointPlacer = vtkFocalPlanePointPlacer::New();
  this->LineInterpolator = vtkBezierContourLineInterpolator::New();

  // Represent the position of the cursor
  this->FocalPoint = vtkPoints::New();
  this->FocalPoint->SetNumberOfPoints(100);
  this->FocalPoint->SetNumberOfPoints(1);
  this->FocalPoint->SetPoint(0, 0.0, 0.0, 0.0);

  vtkDoubleArray* normals = vtkDoubleArray::New();
  normals->SetNumberOfComponents(3);
  normals->SetNumberOfTuples(100);
  normals->SetNumberOfTuples(1);
  double n[3] = { 0, 0, 0 };
  normals->SetTuple(0, n);

  // Represent the position of the cursor
  this->ActiveFocalPoint = vtkPoints::New();
  this->ActiveFocalPoint->SetNumberOfPoints(100);
  this->ActiveFocalPoint->SetNumberOfPoints(1);
  this->ActiveFocalPoint->SetPoint(0, 0.0, 0.0, 0.0);

  vtkDoubleArray* activeNormals = vtkDoubleArray::New();
  activeNormals->SetNumberOfComponents(3);
  activeNormals->SetNumberOfTuples(100);
  activeNormals->SetNumberOfTuples(1);
  activeNormals->SetTuple(0, n);

  this->FocalData = vtkPolyData::New();
  this->FocalData->SetPoints(this->FocalPoint);
  this->FocalData->GetPointData()->SetNormals(normals);
  normals->Delete();

  this->ActiveFocalData = vtkPolyData::New();
  this->ActiveFocalData->SetPoints(this->ActiveFocalPoint);
  this->ActiveFocalData->GetPointData()->SetNormals(activeNormals);
  activeNormals->Delete();

  this->Glypher = vtkGlyph3D::New();
  this->Glypher->SetInputData(this->FocalData);
  this->Glypher->SetVectorModeToUseNormal();
  this->Glypher->OrientOn();
  this->Glypher->ScalingOn();
  this->Glypher->SetScaleModeToDataScalingOff();
  this->Glypher->SetScaleFactor(1.0);

  this->ActiveGlypher = vtkGlyph3D::New();
  this->ActiveGlypher->SetInputData(this->ActiveFocalData);
  this->ActiveGlypher->SetVectorModeToUseNormal();
  this->ActiveGlypher->OrientOn();
  this->ActiveGlypher->ScalingOn();
  this->ActiveGlypher->SetScaleModeToDataScalingOff();
  this->ActiveGlypher->SetScaleFactor(1.0);

  // The transformation of the cursor will be done via vtkGlyph3D
  // By default a vtkCursor2D will be used to define the cursor shape
  vtkCursor2D* cursor2D = vtkCursor2D::New();
  cursor2D->AllOff();
  cursor2D->PointOn();
  cursor2D->Update();
  this->SetCursorShape(cursor2D->GetOutput());
  cursor2D->Delete();

  vtkCylinderSource* cylinder = vtkCylinderSource::New();
  cylinder->SetResolution(64);
  cylinder->SetRadius(0.5);
  cylinder->SetHeight(0.0);
  cylinder->CappingOff();
  cylinder->SetCenter(0, 0, 0);

  vtkCleanPolyData* clean = vtkCleanPolyData::New();
  clean->PointMergingOn();
  clean->CreateDefaultLocator();
  clean->SetInputConnection(cylinder->GetOutputPort());

  vtkTransform* t = vtkTransform::New();
  t->RotateZ(90.0);

  vtkTransformPolyDataFilter* tpd = vtkTransformPolyDataFilter::New();
  tpd->SetInputConnection(clean->GetOutputPort());
  tpd->SetTransform(t);
  clean->Delete();
  cylinder->Delete();

  tpd->Update();
  this->SetActiveCursorShape(tpd->GetOutput());
  tpd->Delete();
  t->Delete();

  this->Glypher->SetSourceData(this->CursorShape);
  this->ActiveGlypher->SetSourceData(this->ActiveCursorShape);

  this->Mapper = vtkPolyDataMapper::New();
  this->Mapper->SetInputConnection(this->Glypher->GetOutputPort());

  // This turns on resolve coincident topology for everything
  // as it is a class static on the mapper
  vtkPolyDataMapper::SetResolveCoincidentTopologyToPolygonOffset();
  this->Mapper->ScalarVisibilityOff();
  // Put this on top of other objects
  this->Mapper->SetRelativeCoincidentTopologyLineOffsetParameters(-1, -1);
  this->Mapper->SetRelativeCoincidentTopologyPolygonOffsetParameters(-1, -1);
  this->Mapper->SetRelativeCoincidentTopologyPointOffsetParameter(-1);

  this->ActiveMapper = vtkPolyDataMapper::New();
  this->ActiveMapper->SetInputConnection(this->ActiveGlypher->GetOutputPort());
  this->ActiveMapper->ScalarVisibilityOff();
  this->ActiveMapper->SetRelativeCoincidentTopologyLineOffsetParameters(-1, -1);
  this->ActiveMapper->SetRelativeCoincidentTopologyPolygonOffsetParameters(-1, -1);
  this->ActiveMapper->SetRelativeCoincidentTopologyPointOffsetParameter(-1);

  // Set up the initial properties
  this->CreateDefaultProperties();

  this->Actor = vtkActor::New();
  this->Actor->SetMapper(this->Mapper);
  this->Actor->SetProperty(this->Property);

  this->ActiveActor = vtkActor::New();
  this->ActiveActor->SetMapper(this->ActiveMapper);
  this->ActiveActor->SetProperty(this->ActiveProperty);

  this->Lines = vtkPolyData::New();
  this->LinesMapper = vtkPolyDataMapper::New();
  this->LinesMapper->SetInputData(this->Lines);
  vtkPolyDataMapper::SetResolveCoincidentTopologyToPolygonOffset();
  this->LinesMapper->SetRelativeCoincidentTopologyLineOffsetParameters(-1, -1);
  this->LinesMapper->SetRelativeCoincidentTopologyPolygonOffsetParameters(-1, -1);
  this->LinesMapper->SetRelativeCoincidentTopologyPointOffsetParameter(-1);

  this->LinesActor = vtkActor::New();
  this->LinesActor->SetMapper(this->LinesMapper);
  this->LinesActor->SetProperty(this->LinesProperty);

  this->InteractionOffset[0] = 0.0;
  this->InteractionOffset[1] = 0.0;

  this->AlwaysOnTop = 0;

  this->SelectedNodesPoints = nullptr;
  this->SelectedNodesData = nullptr;
  this->SelectedNodesCursorShape = nullptr;
  this->SelectedNodesGlypher = nullptr;
  this->SelectedNodesMapper = nullptr;
  this->SelectedNodesActor = nullptr;
}

//------------------------------------------------------------------------------
vtkOrientedGlyphContourRepresentation::~vtkOrientedGlyphContourRepresentation()
{
  this->FocalPoint->Delete();
  this->FocalData->Delete();

  this->ActiveFocalPoint->Delete();
  this->ActiveFocalData->Delete();

  this->SetCursorShape(nullptr);
  this->SetActiveCursorShape(nullptr);

  this->Glypher->Delete();
  this->Mapper->Delete();
  this->Actor->Delete();

  this->ActiveGlypher->Delete();
  this->ActiveMapper->Delete();
  this->ActiveActor->Delete();

  this->Lines->Delete();
  this->LinesMapper->Delete();
  this->LinesActor->Delete();

  this->Property->Delete();
  this->ActiveProperty->Delete();
  this->LinesProperty->Delete();

  // Clear the selected nodes representation
  if (this->SelectedNodesPoints)
  {
    this->SelectedNodesPoints->Delete();
  }
  if (this->SelectedNodesData)
  {
    this->SelectedNodesData->Delete();
  }
  if (this->SelectedNodesCursorShape)
  {
    this->SelectedNodesCursorShape->Delete();
  }
  if (this->SelectedNodesGlypher)
  {
    this->SelectedNodesGlypher->Delete();
  }
  if (this->SelectedNodesMapper)
  {
    this->SelectedNodesMapper->Delete();
  }
  if (this->SelectedNodesActor)
  {
    this->SelectedNodesActor->Delete();
  }
}

//------------------------------------------------------------------------------
void vtkOrientedGlyphContourRepresentation::SetCursorShape(vtkPolyData* shape)
{
  if (shape != this->CursorShape)
  {
    if (this->CursorShape)
    {
      this->CursorShape->Delete();
    }
    this->CursorShape = shape;
    if (this->CursorShape)
    {
      this->CursorShape->Register(this);
    }
    if (this->CursorShape)
    {
      this->Glypher->SetSourceData(this->CursorShape);
    }
    this->Modified();
  }
}

//------------------------------------------------------------------------------
vtkPolyData* vtkOrientedGlyphContourRepresentation::GetCursorShape()
{
  return this->CursorShape;
}

//------------------------------------------------------------------------------
void vtkOrientedGlyphContourRepresentation::SetActiveCursorShape(vtkPolyData* shape)
{
  if (shape != this->ActiveCursorShape)
  {
    if (this->ActiveCursorShape)
    {
      this->ActiveCursorShape->Delete();
    }
    this->ActiveCursorShape = shape;
    if (this->ActiveCursorShape)
    {
      this->ActiveCursorShape->Register(this);
    }
    if (this->ActiveCursorShape)
    {
      this->ActiveGlypher->SetSourceData(this->ActiveCursorShape);
    }
    this->Modified();
  }
}

//------------------------------------------------------------------------------
vtkPolyData* vtkOrientedGlyphContourRepresentation::GetActiveCursorShape()
{
  return this->ActiveCursorShape;
}

//------------------------------------------------------------------------------
void vtkOrientedGlyphContourRepresentation::SetRenderer(vtkRenderer* ren)
{
  //  this->WorldPosition->SetViewport(ren);
  this->Superclass::SetRenderer(ren);
}

//------------------------------------------------------------------------------
int vtkOrientedGlyphContourRepresentation::ComputeInteractionState(
  int X, int Y, int vtkNotUsed(modified))
{

  double pos[4], xyz[3];
  this->FocalPoint->GetPoint(0, pos);
  pos[3] = 1.0;
  this->Renderer->SetWorldPoint(pos);
  this->Renderer->WorldToDisplay();
  this->Renderer->GetDisplayPoint(pos);

  xyz[0] = static_cast<double>(X);
  xyz[1] = static_cast<double>(Y);
  xyz[2] = pos[2];

  this->VisibilityOn();
  double tol2 = this->PixelTolerance * this->PixelTolerance;
  if (vtkMath::Distance2BetweenPoints(xyz, pos) <= tol2)
  {
    this->InteractionState = vtkContourRepresentation::Nearby;
    if (!this->ActiveCursorShape)
    {
      this->VisibilityOff();
    }
  }
  else
  {
    this->InteractionState = vtkContourRepresentation::Outside;
    if (!this->CursorShape)
    {
      this->VisibilityOff();
    }
  }

  return this->InteractionState;
}

//------------------------------------------------------------------------------
// Record the current event position, and the rectilinear wipe position.
void vtkOrientedGlyphContourRepresentation::StartWidgetInteraction(double startEventPos[2])
{
  this->StartEventPosition[0] = startEventPos[0];
  this->StartEventPosition[1] = startEventPos[1];
  this->StartEventPosition[2] = 0.0;

  this->LastEventPosition[0] = startEventPos[0];
  this->LastEventPosition[1] = startEventPos[1];

  // How far is this in pixels from the position of this widget?
  // Maintain this during interaction such as translating (don't
  // force center of widget to snap to mouse position)

  // convert position to display coordinates
  double pos[2];
  this->GetNthNodeDisplayPosition(this->ActiveNode, pos);

  this->InteractionOffset[0] = pos[0] - startEventPos[0];
  this->InteractionOffset[1] = pos[1] - startEventPos[1];
}

//------------------------------------------------------------------------------
// Based on the displacement vector (computed in display coordinates) and
// the cursor state (which corresponds to which part of the widget has been
// selected), the widget points are modified.
// First construct a local coordinate system based on the display coordinates
// of the widget.
void vtkOrientedGlyphContourRepresentation::WidgetInteraction(double eventPos[2])
{
  // Process the motion
  if (this->CurrentOperation == vtkContourRepresentation::Translate)
  {
    this->Translate(eventPos);
  }
  if (this->CurrentOperation == vtkContourRepresentation::Shift)
  {
    this->ShiftContour(eventPos);
  }
  if (this->CurrentOperation == vtkContourRepresentation::Scale)
  {
    this->ScaleContour(eventPos);
  }

  // Book keeping
  this->LastEventPosition[0] = eventPos[0];
  this->LastEventPosition[1] = eventPos[1];
}

//------------------------------------------------------------------------------
// Translate everything
void vtkOrientedGlyphContourRepresentation::Translate(double eventPos[2])
{
  double ref[3];

  if (!this->GetActiveNodeWorldPosition(ref))
  {
    return;
  }

  double displayPos[2];
  displayPos[0] = eventPos[0] + this->InteractionOffset[0];
  displayPos[1] = eventPos[1] + this->InteractionOffset[1];

  double worldPos[3];
  double worldOrient[9] = { 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 };
  if (this->PointPlacer->ComputeWorldPosition(
        this->Renderer, displayPos, ref, worldPos, worldOrient))
  {
    this->SetActiveNodeToWorldPosition(worldPos, worldOrient);
  }
  else
  {
    // I really want to track the closest point here,
    // but I am postponing this at the moment....
  }
}

//------------------------------------------------------------------------------
void vtkOrientedGlyphContourRepresentation::ShiftContour(double eventPos[2])
{
  double ref[3];

  if (!this->GetActiveNodeWorldPosition(ref))
  {
    return;
  }

  double displayPos[2];
  displayPos[0] = eventPos[0] + this->InteractionOffset[0];
  displayPos[1] = eventPos[1] + this->InteractionOffset[1];

  double worldPos[3];
  double worldOrient[9] = { 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 };
  if (this->PointPlacer->ComputeWorldPosition(
        this->Renderer, displayPos, ref, worldPos, worldOrient))
  {

    this->SetActiveNodeToWorldPosition(worldPos, worldOrient);

    double vector[3];
    vector[0] = worldPos[0] - ref[0];
    vector[1] = worldPos[1] - ref[1];
    vector[2] = worldPos[2] - ref[2];

    for (int i = 0; i < this->GetNumberOfNodes(); i++)
    {
      if (i != this->ActiveNode)
      {
        this->GetNthNodeWorldPosition(i, ref);
        worldPos[0] = ref[0] + vector[0];
        worldPos[1] = ref[1] + vector[1];
        worldPos[2] = ref[2] + vector[2];
        this->SetNthNodeWorldPosition(i, worldPos, worldOrient);
      }
    }
  }
}

//------------------------------------------------------------------------------
void vtkOrientedGlyphContourRepresentation::ScaleContour(double eventPos[2])
{
  double ref[3];

  if (!this->GetActiveNodeWorldPosition(ref))
  {
    return;
  }

  double centroid[3];
  ComputeCentroid(centroid);

  double r2 = vtkMath::Distance2BetweenPoints(ref, centroid);

  double displayPos[2];
  displayPos[0] = eventPos[0] + this->InteractionOffset[0];
  displayPos[1] = eventPos[1] + this->InteractionOffset[1];

  double worldPos[3];
  double worldOrient[9] = { 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 };
  if (this->PointPlacer->ComputeWorldPosition(
        this->Renderer, displayPos, ref, worldPos, worldOrient))
  {
    double d2 = vtkMath::Distance2BetweenPoints(worldPos, centroid);
    if (d2 != 0.0)
    {
      double ratio = sqrt(d2 / r2);
      // this->SetActiveNodeToWorldPosition(worldPos, worldOrient);

      for (int i = 0; i < this->GetNumberOfNodes(); i++)
      {
        // if (i != this->ActiveNode)
        {
          this->GetNthNodeWorldPosition(i, ref);
          worldPos[0] = centroid[0] + ratio * (ref[0] - centroid[0]);
          worldPos[1] = centroid[1] + ratio * (ref[1] - centroid[1]);
          worldPos[2] = centroid[2] + ratio * (ref[2] - centroid[2]);
          this->SetNthNodeWorldPosition(i, worldPos, worldOrient);
        }
      }
    }
  }
}

//------------------------------------------------------------------------------
void vtkOrientedGlyphContourRepresentation::ComputeCentroid(double* ioCentroid)
{
  double p[3];
  ioCentroid[0] = 0.;
  ioCentroid[1] = 0.;
  ioCentroid[2] = 0.;

  for (int i = 0; i < this->GetNumberOfNodes(); i++)
  {
    this->GetNthNodeWorldPosition(i, p);
    ioCentroid[0] += p[0];
    ioCentroid[1] += p[1];
    ioCentroid[2] += p[2];
  }
  double inv_N = 1. / static_cast<double>(this->GetNumberOfNodes());
  ioCentroid[0] *= inv_N;
  ioCentroid[1] *= inv_N;
  ioCentroid[2] *= inv_N;
}

//------------------------------------------------------------------------------
void vtkOrientedGlyphContourRepresentation::Scale(double eventPos[2])
{
  // Get the current scale factor
  double sf = this->Glypher->GetScaleFactor();

  // Compute the scale factor
  const int* size = this->Renderer->GetSize();
  double dPos = static_cast<double>(eventPos[1] - this->LastEventPosition[1]);
  sf *= (1.0 + 2.0 * (dPos / size[1])); // scale factor of 2.0 is arbitrary

  // Scale the handle
  this->Glypher->SetScaleFactor(sf);
  if (this->ShowSelectedNodes && this->SelectedNodesGlypher)
  {
    this->SelectedNodesGlypher->SetScaleFactor(sf);
  }
}

//------------------------------------------------------------------------------
void vtkOrientedGlyphContourRepresentation::CreateDefaultProperties()
{
  this->Property = vtkProperty::New();
  this->Property->SetColor(1.0, 1.0, 1.0);
  this->Property->SetLineWidth(0.5);
  this->Property->SetPointSize(3);

  this->ActiveProperty = vtkProperty::New();
  this->ActiveProperty->SetColor(0.0, 1.0, 0.0);
  this->ActiveProperty->SetRepresentationToWireframe();
  this->ActiveProperty->SetAmbient(1.0);
  this->ActiveProperty->SetDiffuse(0.0);
  this->ActiveProperty->SetSpecular(0.0);
  this->ActiveProperty->SetLineWidth(1.0);

  this->LinesProperty = vtkProperty::New();
  this->LinesProperty->SetAmbient(1.0);
  this->LinesProperty->SetDiffuse(0.0);
  this->LinesProperty->SetSpecular(0.0);
  this->LinesProperty->SetColor(1, 1, 1);
  this->LinesProperty->SetLineWidth(1);
}

//------------------------------------------------------------------------------
void vtkOrientedGlyphContourRepresentation::BuildLines()
{
  vtkPoints* points = vtkPoints::New();
  vtkCellArray* lines = vtkCellArray::New();

  int i, j;
  vtkIdType index = 0;

  int count = this->GetNumberOfNodes();
  for (i = 0; i < this->GetNumberOfNodes(); i++)
  {
    count += this->GetNumberOfIntermediatePoints(i);
  }

  points->SetNumberOfPoints(count);
  vtkIdType numLines;

  if (this->ClosedLoop && count > 0)
  {
    numLines = count + 1;
  }
  else
  {
    numLines = count;
  }

  if (numLines > 0)
  {
    vtkIdType* lineIndices = new vtkIdType[numLines];

    double pos[3];
    for (i = 0; i < this->GetNumberOfNodes(); i++)
    {
      // Add the node
      this->GetNthNodeWorldPosition(i, pos);
      points->InsertPoint(index, pos);
      lineIndices[index] = index;
      index++;

      int numIntermediatePoints = this->GetNumberOfIntermediatePoints(i);

      for (j = 0; j < numIntermediatePoints; j++)
      {
        this->GetIntermediatePointWorldPosition(i, j, pos);
        points->InsertPoint(index, pos);
        lineIndices[index] = index;
        index++;
      }
    }

    if (this->ClosedLoop)
    {
      lineIndices[index] = 0;
    }

    lines->InsertNextCell(numLines, lineIndices);
    delete[] lineIndices;
  }

  this->Lines->SetPoints(points);
  this->Lines->SetLines(lines);

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

//------------------------------------------------------------------------------
vtkPolyData* vtkOrientedGlyphContourRepresentation::GetContourRepresentationAsPolyData()
{
  // Get the points in this contour as a vtkPolyData.
  return this->Lines;
}

//------------------------------------------------------------------------------
void vtkOrientedGlyphContourRepresentation::BuildRepresentation()
{
  // Make sure we are up to date with any changes made in the placer
  this->UpdateContour();

  if (this->AlwaysOnTop)
  {
    // max value 65536 so we subtract 66000 to make sure we are
    // zero or negative
    this->LinesMapper->SetRelativeCoincidentTopologyLineOffsetParameters(0, -66000);
    this->LinesMapper->SetRelativeCoincidentTopologyPolygonOffsetParameters(0, -66000);
    this->LinesMapper->SetRelativeCoincidentTopologyPointOffsetParameter(-66000);
    this->Mapper->SetRelativeCoincidentTopologyLineOffsetParameters(0, -66000);
    this->Mapper->SetRelativeCoincidentTopologyPolygonOffsetParameters(0, -66000);
    this->Mapper->SetRelativeCoincidentTopologyPointOffsetParameter(-66000);
    this->ActiveMapper->SetRelativeCoincidentTopologyLineOffsetParameters(0, -66000);
    this->ActiveMapper->SetRelativeCoincidentTopologyPolygonOffsetParameters(0, -66000);
    this->ActiveMapper->SetRelativeCoincidentTopologyPointOffsetParameter(-66000);
  }
  else
  {
    this->LinesMapper->SetRelativeCoincidentTopologyLineOffsetParameters(-1, -1);
    this->LinesMapper->SetRelativeCoincidentTopologyPolygonOffsetParameters(-1, -1);
    this->LinesMapper->SetRelativeCoincidentTopologyPointOffsetParameter(-1);
    this->Mapper->SetRelativeCoincidentTopologyLineOffsetParameters(-1, -1);
    this->Mapper->SetRelativeCoincidentTopologyPolygonOffsetParameters(-1, -1);
    this->Mapper->SetRelativeCoincidentTopologyPointOffsetParameter(-1);
    this->ActiveMapper->SetRelativeCoincidentTopologyLineOffsetParameters(-1, -1);
    this->ActiveMapper->SetRelativeCoincidentTopologyPolygonOffsetParameters(-1, -1);
    this->ActiveMapper->SetRelativeCoincidentTopologyPointOffsetParameter(-1);
  }

  double p1[4], p2[4];
  this->Renderer->GetActiveCamera()->GetFocalPoint(p1);
  p1[3] = 1.0;
  this->Renderer->SetWorldPoint(p1);
  this->Renderer->WorldToView();
  this->Renderer->GetViewPoint(p1);

  double depth = p1[2];
  double aspect[2];
  this->Renderer->ComputeAspect();
  this->Renderer->GetAspect(aspect);

  p1[0] = -aspect[0];
  p1[1] = -aspect[1];
  this->Renderer->SetViewPoint(p1);
  this->Renderer->ViewToWorld();
  this->Renderer->GetWorldPoint(p1);

  p2[0] = aspect[0];
  p2[1] = aspect[1];
  p2[2] = depth;
  p2[3] = 1.0;
  this->Renderer->SetViewPoint(p2);
  this->Renderer->ViewToWorld();
  this->Renderer->GetWorldPoint(p2);

  double distance = sqrt(vtkMath::Distance2BetweenPoints(p1, p2));

  const int* size = this->Renderer->GetRenderWindow()->GetSize();
  double viewport[4];
  this->Renderer->GetViewport(viewport);

  double x, y, scale;

  x = size[0] * (viewport[2] - viewport[0]);
  y = size[1] * (viewport[3] - viewport[1]);

  scale = sqrt(x * x + y * y);

  distance = 1000 * distance / scale;

  this->Glypher->SetScaleFactor(distance * this->HandleSize);
  this->ActiveGlypher->SetScaleFactor(distance * this->HandleSize);
  int numPoints = this->GetNumberOfNodes();
  int i;
  if (this->ShowSelectedNodes && this->SelectedNodesGlypher)
  {
    this->SelectedNodesGlypher->SetScaleFactor(distance * this->HandleSize);
    this->FocalPoint->Reset();
    this->FocalPoint->SetNumberOfPoints(0);
    this->FocalData->GetPointData()->GetNormals()->SetNumberOfTuples(0);
    this->SelectedNodesPoints->Reset();
    this->SelectedNodesPoints->SetNumberOfPoints(0);
    this->SelectedNodesData->GetPointData()->GetNormals()->SetNumberOfTuples(0);
    for (i = 0; i < numPoints; i++)
    {
      if (i != this->ActiveNode)
      {
        double worldPos[3];
        double worldOrient[9];
        this->GetNthNodeWorldPosition(i, worldPos);
        this->GetNthNodeWorldOrientation(i, worldOrient);
        if (this->GetNthNodeSelected(i))
        {
          this->SelectedNodesPoints->InsertNextPoint(worldPos);
          this->SelectedNodesData->GetPointData()->GetNormals()->InsertNextTuple(worldOrient + 6);
        }
        else
        {
          this->FocalPoint->InsertNextPoint(worldPos);
          this->FocalData->GetPointData()->GetNormals()->InsertNextTuple(worldOrient + 6);
        }
      }
    }
    this->SelectedNodesPoints->Modified();
    this->SelectedNodesData->GetPointData()->GetNormals()->Modified();
    this->SelectedNodesData->Modified();
  }
  else
  {
    if (this->ActiveNode >= 0 && this->ActiveNode < this->GetNumberOfNodes())
    {
      this->FocalPoint->SetNumberOfPoints(numPoints - 1);
      this->FocalData->GetPointData()->GetNormals()->SetNumberOfTuples(numPoints - 1);
    }
    else
    {
      this->FocalPoint->SetNumberOfPoints(numPoints);
      this->FocalData->GetPointData()->GetNormals()->SetNumberOfTuples(numPoints);
    }
    int idx = 0;
    for (i = 0; i < numPoints; i++)
    {
      if (i != this->ActiveNode)
      {
        double worldPos[3];
        double worldOrient[9];
        this->GetNthNodeWorldPosition(i, worldPos);
        this->GetNthNodeWorldOrientation(i, worldOrient);
        this->FocalPoint->SetPoint(idx, worldPos);
        this->FocalData->GetPointData()->GetNormals()->SetTuple(idx, worldOrient + 6);
        idx++;
      }
    }
  }

  this->FocalPoint->Modified();
  this->FocalData->GetPointData()->GetNormals()->Modified();
  this->FocalData->Modified();

  if (this->ActiveNode >= 0 && this->ActiveNode < this->GetNumberOfNodes())
  {
    double worldPos[3];
    double worldOrient[9];
    this->GetNthNodeWorldPosition(this->ActiveNode, worldPos);
    this->GetNthNodeWorldOrientation(this->ActiveNode, worldOrient);
    this->ActiveFocalPoint->SetPoint(0, worldPos);
    this->ActiveFocalData->GetPointData()->GetNormals()->SetTuple(0, worldOrient + 6);

    this->ActiveFocalPoint->Modified();
    this->ActiveFocalData->GetPointData()->GetNormals()->Modified();
    this->ActiveFocalData->Modified();
    this->ActiveActor->VisibilityOn();
  }
  else
  {
    this->ActiveActor->VisibilityOff();
  }
}

//------------------------------------------------------------------------------
void vtkOrientedGlyphContourRepresentation::GetActors(vtkPropCollection* pc)
{
  this->Actor->GetActors(pc);
  this->ActiveActor->GetActors(pc);
  this->LinesActor->GetActors(pc);
  if (this->ShowSelectedNodes && this->SelectedNodesActor)
  {
    this->SelectedNodesActor->GetActors(pc);
  }
}

//------------------------------------------------------------------------------
void vtkOrientedGlyphContourRepresentation::ReleaseGraphicsResources(vtkWindow* win)
{
  this->Actor->ReleaseGraphicsResources(win);
  this->ActiveActor->ReleaseGraphicsResources(win);
  this->LinesActor->ReleaseGraphicsResources(win);
}

//------------------------------------------------------------------------------
int vtkOrientedGlyphContourRepresentation::RenderOverlay(vtkViewport* viewport)
{
  int count = 0;
  count += this->LinesActor->RenderOverlay(viewport);
  if (this->Actor->GetVisibility())
  {
    count += this->Actor->RenderOverlay(viewport);
  }
  if (this->ActiveActor->GetVisibility())
  {
    count += this->ActiveActor->RenderOverlay(viewport);
  }
  return count;
}

//------------------------------------------------------------------------------
int vtkOrientedGlyphContourRepresentation::RenderOpaqueGeometry(vtkViewport* viewport)
{
  // Since we know RenderOpaqueGeometry gets called first, will do the
  // build here
  this->BuildRepresentation();

  int count = 0;
  count += this->LinesActor->RenderOpaqueGeometry(viewport);
  if (this->Actor->GetVisibility())
  {
    count += this->Actor->RenderOpaqueGeometry(viewport);
  }
  if (this->ActiveActor->GetVisibility())
  {
    count += this->ActiveActor->RenderOpaqueGeometry(viewport);
  }
  if (this->ShowSelectedNodes && this->SelectedNodesActor &&
    this->SelectedNodesActor->GetVisibility())
  {
    count += this->SelectedNodesActor->RenderOpaqueGeometry(viewport);
  }

  return count;
}

//------------------------------------------------------------------------------
int vtkOrientedGlyphContourRepresentation::RenderTranslucentPolygonalGeometry(vtkViewport* viewport)
{
  int count = 0;
  count += this->LinesActor->RenderTranslucentPolygonalGeometry(viewport);
  if (this->Actor->GetVisibility())
  {
    count += this->Actor->RenderTranslucentPolygonalGeometry(viewport);
  }
  if (this->ActiveActor->GetVisibility())
  {
    count += this->ActiveActor->RenderTranslucentPolygonalGeometry(viewport);
  }
  return count;
}

//------------------------------------------------------------------------------
vtkTypeBool vtkOrientedGlyphContourRepresentation::HasTranslucentPolygonalGeometry()
{
  int result = 0;
  result |= this->LinesActor->HasTranslucentPolygonalGeometry();
  if (this->Actor->GetVisibility())
  {
    result |= this->Actor->HasTranslucentPolygonalGeometry();
  }
  if (this->ActiveActor->GetVisibility())
  {
    result |= this->ActiveActor->HasTranslucentPolygonalGeometry();
  }
  return result;
}

//------------------------------------------------------------------------------
void vtkOrientedGlyphContourRepresentation::SetLineColor(double r, double g, double b)
{
  if (this->GetLinesProperty())
  {
    this->GetLinesProperty()->SetColor(r, g, b);
  }
}

//------------------------------------------------------------------------------
void vtkOrientedGlyphContourRepresentation::SetShowSelectedNodes(vtkTypeBool flag)
{
  vtkDebugMacro(<< this->GetClassName() << " (" << this << "): setting ShowSelectedNodes to "
                << flag);
  if (this->ShowSelectedNodes != flag)
  {
    this->ShowSelectedNodes = flag;
    this->Modified();

    if (this->ShowSelectedNodes)
    {
      if (!this->SelectedNodesActor)
      {
        this->CreateSelectedNodesRepresentation();
      }
      else
      {
        this->SelectedNodesActor->SetVisibility(1);
      }
    }
    else
    {
      if (this->SelectedNodesActor)
      {
        this->SelectedNodesActor->SetVisibility(0);
      }
    }
  }
}

//------------------------------------------------------------------------------
double* vtkOrientedGlyphContourRepresentation::GetBounds()
{
  return this->Lines->GetPoints() ? this->Lines->GetPoints()->GetBounds() : nullptr;
}

//------------------------------------------------------------------------------
void vtkOrientedGlyphContourRepresentation::CreateSelectedNodesRepresentation()
{
  vtkSphereSource* sphere = vtkSphereSource::New();
  sphere->SetThetaResolution(12);
  sphere->SetRadius(0.3);
  this->SelectedNodesCursorShape = sphere->GetOutput();
  this->SelectedNodesCursorShape->Register(this);
  sphere->Delete();

  // Represent the position of the cursor
  this->SelectedNodesPoints = vtkPoints::New();
  this->SelectedNodesPoints->SetNumberOfPoints(100);
  // this->SelectedNodesPoints->SetNumberOfPoints(1);
  // this->SelectedNodesPoints->SetPoint(0, 0.0, 0.0, 0.0);

  vtkDoubleArray* normals = vtkDoubleArray::New();
  normals->SetNumberOfComponents(3);
  normals->SetNumberOfTuples(100);
  normals->SetNumberOfTuples(1);
  double n[3] = { 0, 0, 0 };
  normals->SetTuple(0, n);

  this->SelectedNodesData = vtkPolyData::New();
  this->SelectedNodesData->SetPoints(this->SelectedNodesPoints);
  this->SelectedNodesData->GetPointData()->SetNormals(normals);
  normals->Delete();

  this->SelectedNodesGlypher = vtkGlyph3D::New();
  this->SelectedNodesGlypher->SetInputData(this->SelectedNodesData);
  this->SelectedNodesGlypher->SetVectorModeToUseNormal();
  this->SelectedNodesGlypher->OrientOn();
  this->SelectedNodesGlypher->ScalingOn();
  this->SelectedNodesGlypher->SetScaleModeToDataScalingOff();
  this->SelectedNodesGlypher->SetScaleFactor(1.0);

  this->SelectedNodesGlypher->SetSourceData(this->SelectedNodesCursorShape);

  this->SelectedNodesMapper = vtkPolyDataMapper::New();
  this->SelectedNodesMapper->SetInputData(this->SelectedNodesGlypher->GetOutput());
  vtkPolyDataMapper::SetResolveCoincidentTopologyToPolygonOffset();
  this->SelectedNodesMapper->ScalarVisibilityOff();

  vtkProperty* selProperty = vtkProperty::New();
  selProperty->SetColor(0.0, 1.0, 0.0);
  selProperty->SetLineWidth(0.5);
  selProperty->SetPointSize(3);

  this->SelectedNodesActor = vtkActor::New();
  this->SelectedNodesActor->SetMapper(this->SelectedNodesMapper);
  this->SelectedNodesActor->SetProperty(selProperty);
  selProperty->Delete();
}

//------------------------------------------------------------------------------
void vtkOrientedGlyphContourRepresentation::PrintSelf(ostream& os, vtkIndent indent)
{
  // Superclass typedef defined in vtkTypeMacro() found in vtkSetGet.h
  this->Superclass::PrintSelf(os, indent);

  os << indent << "Always On Top: " << (this->AlwaysOnTop ? "On\n" : "Off\n");
  os << indent << "ShowSelectedNodes: " << this->ShowSelectedNodes << endl;

  if (this->Property)
  {
    os << indent << "Property: " << this->Property << "\n";
  }
  else
  {
    os << indent << "Property: (none)\n";
  }

  if (this->ActiveProperty)
  {
    os << indent << "Active Property: " << this->ActiveProperty << "\n";
  }
  else
  {
    os << indent << "Active Property: (none)\n";
  }

  if (this->LinesProperty)
  {
    os << indent << "Lines Property: " << this->LinesProperty << "\n";
  }
  else
  {
    os << indent << "Lines Property: (none)\n";
  }
}
VTK_ABI_NAMESPACE_END
