// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#include "vtkAbstractPolygonalHandleRepresentation3D.h"
#include "vtkActor.h"
#include "vtkAssemblyPath.h"
#include "vtkCamera.h"
#include "vtkCellPicker.h"
#include "vtkCoordinate.h"
#include "vtkCursor3D.h"
#include "vtkFocalPlanePointPlacer.h"
#include "vtkFollower.h"
#include "vtkInteractorObserver.h"
#include "vtkLine.h"
#include "vtkMath.h"
#include "vtkMatrix4x4.h"
#include "vtkMatrixToLinearTransform.h"
#include "vtkObjectFactory.h"
#include "vtkPickingManager.h"
#include "vtkPolyDataMapper.h"
#include "vtkProperty.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkTransformPolyDataFilter.h"
#include "vtkVectorText.h"

#include <cassert>

VTK_ABI_NAMESPACE_BEGIN
vtkCxxSetObjectMacro(vtkAbstractPolygonalHandleRepresentation3D, Property, vtkProperty);
vtkCxxSetObjectMacro(vtkAbstractPolygonalHandleRepresentation3D, SelectedProperty, vtkProperty);

//------------------------------------------------------------------------------
vtkAbstractPolygonalHandleRepresentation3D ::vtkAbstractPolygonalHandleRepresentation3D()
{
  this->InteractionState = vtkHandleRepresentation::Outside;

  this->HandleTransformFilter = vtkTransformPolyDataFilter::New();
  this->HandleTransform = vtkMatrixToLinearTransform::New();
  this->HandleTransformMatrix = vtkMatrix4x4::New();
  this->HandleTransformMatrix->Identity();
  this->HandleTransform->SetInput(this->HandleTransformMatrix);
  this->HandleTransformFilter->SetTransform(this->HandleTransform);

  // initialized because it is used in PrintSelf
  this->LastPickPosition[0] = 0.0;
  this->LastPickPosition[1] = 0.0;
  this->LastPickPosition[2] = 0.0;

  // initialized because it is used in PrintSelf
  this->LastEventPosition[0] = 0.0;
  this->LastEventPosition[1] = 0.0;

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

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

  this->Actor = nullptr; // Created by subclass.vtkFollower::New();

  // Manage the picking stuff
  this->HandlePicker = vtkCellPicker::New();
  this->HandlePicker->PickFromListOn();
  this->HandlePicker->SetTolerance(0.01); // need some fluff

  // Override superclass'
  this->PlaceFactor = 1.0;
  this->WaitingForMotion = 0;
  this->ConstraintAxis = -1;

  vtkFocalPlanePointPlacer* pointPlacer = vtkFocalPlanePointPlacer::New();
  this->SetPointPlacer(pointPlacer);
  pointPlacer->Delete();

  // Label stuff.
  this->LabelAnnotationTextScaleInitialized = false;
  this->LabelVisibility = 0;
  this->HandleVisibility = 1;
  this->LabelTextInput = vtkVectorText::New();
  this->LabelTextInput->SetText("0");
  this->LabelTextMapper = vtkPolyDataMapper::New();
  this->LabelTextMapper->SetInputConnection(this->LabelTextInput->GetOutputPort());
  this->LabelTextActor = vtkFollower::New();
  this->LabelTextActor->SetMapper(this->LabelTextMapper);
  this->LabelTextActor->GetProperty()->SetColor(1.0, 0.1, 0.0);

  this->SmoothMotion = 1;
}

//------------------------------------------------------------------------------
vtkAbstractPolygonalHandleRepresentation3D ::~vtkAbstractPolygonalHandleRepresentation3D()
{
  this->HandleTransformFilter->Delete();
  this->HandleTransform->Delete();
  this->HandleTransformMatrix->Delete();
  this->HandlePicker->Delete();
  this->Mapper->Delete();
  this->Actor->Delete();
  this->Property->Delete();
  this->SelectedProperty->Delete();
  this->LabelTextInput->Delete();
  this->LabelTextMapper->Delete();
  this->LabelTextActor->Delete();
}

//------------------------------------------------------------------------------
void vtkAbstractPolygonalHandleRepresentation3D::RegisterPickers()
{
  vtkPickingManager* pm = this->GetPickingManager();
  if (!pm)
  {
    return;
  }
  pm->AddPicker(this->HandlePicker, this);
}

//------------------------------------------------------------------------------
void vtkAbstractPolygonalHandleRepresentation3D::SetHandle(vtkPolyData* pd)
{
  this->HandleTransformFilter->SetInputData(pd);
}

//------------------------------------------------------------------------------
vtkPolyData* vtkAbstractPolygonalHandleRepresentation3D::GetHandle()
{
  return vtkPolyData::SafeDownCast(this->HandleTransformFilter->GetInput());
}

//------------------------------------------------------------------------------
void vtkAbstractPolygonalHandleRepresentation3D::SetWorldPosition(double p[3])
{
  if (!this->Renderer || !this->PointPlacer || this->PointPlacer->ValidateWorldPosition(p))
  {
    this->WorldPosition->SetValue(p);
    this->WorldPositionTime.Modified();
    this->Modified();
  }
}

//------------------------------------------------------------------------------
void vtkAbstractPolygonalHandleRepresentation3D::SetDisplayPosition(double p[3])
{
  if (this->Renderer && this->PointPlacer)
  {
    if (this->PointPlacer->ValidateDisplayPosition(this->Renderer, p))
    {
      double worldPos[3], worldOrient[9];
      if (this->PointPlacer->ComputeWorldPosition(this->Renderer, p, worldPos, worldOrient))
      {
        this->DisplayPosition->SetValue(p);
        this->WorldPosition->SetValue(worldPos);
        this->DisplayPositionTime.Modified();
        this->SetWorldPosition(this->WorldPosition->GetValue());
      }
    }
  }
  else
  {
    this->DisplayPosition->SetValue(p);
    this->DisplayPositionTime.Modified();
  }
}

//------------------------------------------------------------------------------
int vtkAbstractPolygonalHandleRepresentation3D ::ComputeInteractionState(
  int X, int Y, int vtkNotUsed(modify))
{
  this->VisibilityOn(); // actor must be on to be picked
  vtkAssemblyPath* path = this->GetAssemblyPath(X, Y, 0., this->HandlePicker);

  if (path != nullptr)
  {
    this->InteractionState = vtkHandleRepresentation::Nearby;
  }
  else
  {
    this->InteractionState = vtkHandleRepresentation::Outside;
    if (this->ActiveRepresentation)
    {
      this->VisibilityOff();
    }
  }

  return this->InteractionState;
}

//------------------------------------------------------------------------------
int vtkAbstractPolygonalHandleRepresentation3D::DetermineConstraintAxis(
  int constraint, double* x, double* startPickPoint)
{
  // Look for trivial cases
  if (!this->Constrained)
  {
    return -1;
  }
  else if (constraint >= 0 && constraint < 3)
  {
    return constraint;
  }

  // Okay, figure out constraint. First see if the choice is
  // outside the hot spot
  if (!x)
  {
    double p[3];
    this->HandlePicker->GetPickPosition(p);
    if (vtkMath::Distance2BetweenPoints(p, this->LastPickPosition) > 0.0)
    {
      this->WaitingForMotion = 0;
      return 0;
    }
    else
    {
      this->WaitingForMotion = 1;
      this->WaitCount = 0;
      return -1;
    }
  }
  else if (x)
  {
    this->WaitingForMotion = 0;
    double v[3];
    v[0] = fabs(x[0] - startPickPoint[0]);
    v[1] = fabs(x[1] - startPickPoint[1]);
    v[2] = fabs(x[2] - startPickPoint[2]);
    // NOLINTNEXTLINE(readability-avoid-nested-conditional-operator)
    return (v[0] > v[1] ? (v[0] > v[2] ? 0 : 2) : (v[1] > v[2] ? 1 : 2));
  }
  else
  {
    return -1;
  }
}

//------------------------------------------------------------------------------
// Record the current event position, and the rectilinear wipe position.
void vtkAbstractPolygonalHandleRepresentation3D::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];

  vtkAssemblyPath* path =
    this->GetAssemblyPath(startEventPos[0], startEventPos[1], 0., this->HandlePicker);

  // Did we pick the handle ?
  if (path)
  {
    this->InteractionState = vtkHandleRepresentation::Nearby;
    this->ConstraintAxis = -1;
    this->HandlePicker->GetPickPosition(this->LastPickPosition);
  }
  else
  {
    this->InteractionState = vtkHandleRepresentation::Outside;
    this->ConstraintAxis = -1;
  }
  this->WaitCount = 0;
}

//------------------------------------------------------------------------------
// 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 vtkAbstractPolygonalHandleRepresentation3D::WidgetInteraction(double eventPos[2])
{
  // Do different things depending on state
  // Calculations everybody does
  double focalPoint[4], pickPoint[4], prevPickPoint[4], startPickPoint[4], z;

  // Compute the two points defining the motion vector
  vtkInteractorObserver::ComputeWorldToDisplay(this->Renderer, this->LastPickPosition[0],
    this->LastPickPosition[1], this->LastPickPosition[2], focalPoint);
  z = focalPoint[2];
  vtkInteractorObserver::ComputeDisplayToWorld(
    this->Renderer, this->LastEventPosition[0], this->LastEventPosition[1], z, prevPickPoint);
  vtkInteractorObserver::ComputeDisplayToWorld(
    this->Renderer, eventPos[0], eventPos[1], z, pickPoint);

  // Process the motion
  if (this->InteractionState == vtkHandleRepresentation::Selecting ||
    this->InteractionState == vtkHandleRepresentation::Translating)
  {
    this->WaitCount++;

    if (this->WaitCount > 3 || !this->Constrained)
    {
      vtkInteractorObserver::ComputeDisplayToWorld(this->Renderer, this->StartEventPosition[0],
        this->StartEventPosition[1], z, startPickPoint);

      this->ConstraintAxis =
        this->DetermineConstraintAxis(this->ConstraintAxis, pickPoint, startPickPoint);

      if (this->InteractionState == vtkHandleRepresentation::Selecting)
      {
        // If we are doing axis constrained motion, ignore the placer.
        // Can't have both the placer and an axis constraint dictating
        // handle placement.
        if (this->ConstraintAxis >= 0 || this->Constrained || !this->PointPlacer)
        {
          this->MoveFocus(prevPickPoint, pickPoint);
        }
        else
        {
          double newCenterPointRequested[3]; // displayPosition
          double newCenterPoint[3], worldOrient[9];

          // Make a request for the new position.
          this->MoveFocusRequest(prevPickPoint, pickPoint, eventPos, newCenterPointRequested);

          vtkFocalPlanePointPlacer* fPlacer =
            vtkFocalPlanePointPlacer::SafeDownCast(this->PointPlacer);
          if (fPlacer)
          {
            // Offset the placer plane to one that passes through the current
            // world position and is parallel to the focal plane. Offset =
            // the distance currentWorldPos is from the focal plane
            //
            double currentWorldPos[3], projDir[3], fp[3];
            this->GetWorldPosition(currentWorldPos);
            this->Renderer->GetActiveCamera()->GetFocalPoint(fp);
            double vec[3] = { currentWorldPos[0] - fp[0], currentWorldPos[1] - fp[1],
              currentWorldPos[2] - fp[2] };
            this->Renderer->GetActiveCamera()->GetDirectionOfProjection(projDir);
            fPlacer->SetOffset(vtkMath::Dot(vec, projDir));
          }

          // See what the placer says.
          if (this->PointPlacer->ComputeWorldPosition(
                this->Renderer, newCenterPointRequested, newCenterPoint, worldOrient))
          {
            // Once the placer has validated us, update the handle position
            this->SetWorldPosition(newCenterPoint);
          }
        }
      }
      else
      {
        // If we are doing axis constrained motion, ignore the placer.
        // Can't have both the placer and the axis constraint dictating
        // handle placement.
        if (this->ConstraintAxis >= 0 || this->Constrained || !this->PointPlacer)
        {
          this->Translate(prevPickPoint, pickPoint);
        }
        else
        {
          double newCenterPointRequested[3]; // displayPosition
          double newCenterPoint[3], worldOrient[9];

          // Make a request for the new position.
          this->MoveFocusRequest(prevPickPoint, pickPoint, eventPos, newCenterPointRequested);

          vtkFocalPlanePointPlacer* fPlacer =
            vtkFocalPlanePointPlacer::SafeDownCast(this->PointPlacer);
          if (fPlacer)
          {
            // Offset the placer plane to one that passes through the current
            // world position and is parallel to the focal plane. Offset =
            // the distance currentWorldPos is from the focal plane
            //
            double currentWorldPos[3], projDir[3], fp[3];
            this->GetWorldPosition(currentWorldPos);
            this->Renderer->GetActiveCamera()->GetFocalPoint(fp);
            double vec[3] = { currentWorldPos[0] - fp[0], currentWorldPos[1] - fp[1],
              currentWorldPos[2] - fp[2] };
            this->Renderer->GetActiveCamera()->GetDirectionOfProjection(projDir);
            fPlacer->SetOffset(vtkMath::Dot(vec, projDir));
          }

          // See what the placer says.
          if (this->PointPlacer->ComputeWorldPosition(
                this->Renderer, newCenterPointRequested, newCenterPoint, worldOrient))
          {
            this->SetWorldPosition(newCenterPoint);
          }
        }
      }
    }
  }

  else if (this->InteractionState == vtkHandleRepresentation::Scaling)
  {
    // Scaling does not change the position of the handle, we needn't
    // ask the placer..
    this->Scale(prevPickPoint, pickPoint, eventPos);
  }

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

  this->Modified();
}

//------------------------------------------------------------------------------
void vtkAbstractPolygonalHandleRepresentation3D ::MoveFocusRequest(
  const double* p1, const double* p2, const double currPos[2], double center[3])
{
  if (this->SmoothMotion)
  {
    double focus[4], v[3] = { 0, 0, 0 };
    this->GetWorldPosition(focus);

    // Move the center of the handle along the motion vector
    if (!this->IsTranslationConstrained())
    {
      v[0] = p2[0] - p1[0];
      v[1] = p2[1] - p1[1];
      v[2] = p2[2] - p1[2];
    }
    else
    {
      assert(this->TranslationAxis > -1 && this->TranslationAxis < 3 &&
        "this->TranslationAxis out of bounds");
      v[this->TranslationAxis] = p2[this->TranslationAxis] - p1[this->TranslationAxis];
    }

    focus[0] += v[0];
    focus[1] += v[1];
    focus[2] += v[2];
    focus[3] = 1.0;

    // Get the display position that this center would fall on.
    this->Renderer->SetWorldPoint(focus);
    this->Renderer->WorldToDisplay();
    this->Renderer->GetDisplayPoint(center);
  }
  else
  {
    center[0] = currPos[0];
    center[1] = currPos[1];
    center[2] = 1.0;
  }
}

//------------------------------------------------------------------------------
void vtkAbstractPolygonalHandleRepresentation3D::MoveFocus(const double* p1, const double* p2)
{
  this->Translate(p1, p2);
}

//------------------------------------------------------------------------------
// Translate everything
void vtkAbstractPolygonalHandleRepresentation3D::Translate(const double* p1, const double* p2)
{
  // Get the motion vector
  double v[3] = { 0, 0, 0 }, pos[3];
  if (!this->IsTranslationConstrained())
  {
    v[0] = p2[0] - p1[0];
    v[1] = p2[1] - p1[1];
    v[2] = p2[2] - p1[2];
  }
  else
  {
    assert(this->TranslationAxis > -1 && this->TranslationAxis < 3 &&
      "this->TranslationAxis out of bounds");
    v[this->TranslationAxis] = p2[this->TranslationAxis] - p1[this->TranslationAxis];
  }

  this->GetWorldPosition(pos);
  double newFocus[3];

  for (int i = 0; i < 3; i++)
  {
    newFocus[i] = pos[i] + v[i];
  }

  this->SetWorldPosition(newFocus);
}

//------------------------------------------------------------------------------
void vtkAbstractPolygonalHandleRepresentation3D ::Scale(
  const double*, const double*, const double eventPos[2])
{
  double sf = 1.0 + (eventPos[1] - this->LastEventPosition[1]) / this->Renderer->GetSize()[1];
  if (sf == 1.0)
  {
    return;
  }

  double handleSize = this->HandleTransformMatrix->GetElement(0, 0) * sf;
  handleSize = (handleSize < 0.001 ? 0.001 : handleSize);

  this->SetUniformScale(handleSize);
}

//------------------------------------------------------------------------------
void vtkAbstractPolygonalHandleRepresentation3D ::SetUniformScale(double handleSize)
{
  this->HandleTransformMatrix->SetElement(0, 0, handleSize);
  this->HandleTransformMatrix->SetElement(1, 1, handleSize);
  this->HandleTransformMatrix->SetElement(2, 2, handleSize);
}

//------------------------------------------------------------------------------
void vtkAbstractPolygonalHandleRepresentation3D::Highlight(int highlight)
{
  this->Actor->SetProperty(highlight ? this->SelectedProperty : this->Property);
}

//------------------------------------------------------------------------------
void vtkAbstractPolygonalHandleRepresentation3D::CreateDefaultProperties()
{
  this->Property = vtkProperty::New();
  this->Property->SetLineWidth(0.5);

  this->SelectedProperty = vtkProperty::New();
  this->SelectedProperty->SetAmbient(1.0);
  this->SelectedProperty->SetAmbientColor(0.0, 1.0, 0.0);
  this->SelectedProperty->SetLineWidth(2.0);
}

//------------------------------------------------------------------------------
void vtkAbstractPolygonalHandleRepresentation3D::UpdateHandle()
{
  // Subclasses should override this.
  this->HandleTransformFilter->Update();
}

//------------------------------------------------------------------------------
void vtkAbstractPolygonalHandleRepresentation3D::BuildRepresentation()
{
  // The net effect is to resize the handle
  if (this->GetMTime() > this->BuildTime ||
    (this->Renderer && this->Renderer->GetVTKWindow() &&
      this->Renderer->GetVTKWindow()->GetMTime() > this->BuildTime))
  {

    // Update the handle.
    this->UpdateHandle();

    // Update the label,
    this->UpdateLabel();

    this->BuildTime.Modified();
  }
}

//------------------------------------------------------------------------------
void vtkAbstractPolygonalHandleRepresentation3D::UpdateLabel()
{
  // Display the label if needed.
  if (this->LabelVisibility)
  {
    if (this->Renderer)
    {
      this->LabelTextActor->SetCamera(this->Renderer->GetActiveCamera());
    }
    else
    {
      vtkErrorMacro("UpdateLabel: no renderer has been set!");
      return;
    }

    // Place the label on the North east of the handle. We need to take into
    // account the viewup vector and the direction of the camera, so that we
    // can bring it on the closest plane of the widget facing the camera.
    double labelPosition[3], vup[3], directionOfProjection[3], xAxis[3], bounds[6];
    this->Renderer->GetActiveCamera()->GetViewUp(vup);
    this->Renderer->GetActiveCamera()->GetDirectionOfProjection(directionOfProjection);
    vtkMath::Cross(directionOfProjection, vup, xAxis);
    this->Mapper->GetBounds(bounds);
    double width = sqrt((bounds[1] - bounds[0]) * (bounds[1] - bounds[0]) +
      (bounds[3] - bounds[2]) * (bounds[3] - bounds[2]) +
      (bounds[5] - bounds[4]) * (bounds[5] - bounds[4]));
    this->GetWorldPosition(labelPosition);
    labelPosition[0] += width / 2.0 * xAxis[0];
    labelPosition[1] += width / 2.0 * xAxis[1];
    labelPosition[2] += width / 2.0 * xAxis[2];

    this->LabelTextActor->SetPosition(labelPosition);

    if (!this->LabelAnnotationTextScaleInitialized)
    {
      // If a font size hasn't been specified by the user, scale the text
      // (font size) according to the size of the handle .
      this->LabelTextActor->SetScale(width / 3.0, width / 3.0, width / 3.0);
    }
  }
}

//------------------------------------------------------------------------------
void vtkAbstractPolygonalHandleRepresentation3D::ShallowCopy(vtkProp* prop)
{
  vtkAbstractPolygonalHandleRepresentation3D* rep =
    vtkAbstractPolygonalHandleRepresentation3D::SafeDownCast(prop);
  if (rep)
  {
    this->SetProperty(rep->GetProperty());
    this->SetSelectedProperty(rep->GetSelectedProperty());
    this->Actor->SetProperty(this->Property);

    // copy the handle shape
    this->HandleTransformFilter->SetInputConnection(
      rep->HandleTransformFilter->GetInputConnection(0, 0));

    this->LabelVisibility = rep->LabelVisibility;
    this->SetLabelText(rep->GetLabelText());
  }
  this->Superclass::ShallowCopy(prop);
}

//------------------------------------------------------------------------------
void vtkAbstractPolygonalHandleRepresentation3D::DeepCopy(vtkProp* prop)
{
  vtkAbstractPolygonalHandleRepresentation3D* rep =
    vtkAbstractPolygonalHandleRepresentation3D::SafeDownCast(prop);
  if (rep)
  {
    this->Property->DeepCopy(rep->GetProperty());
    this->SelectedProperty->DeepCopy(rep->GetSelectedProperty());
    this->Actor->SetProperty(this->Property);

    // copy the handle shape
    vtkPolyData* pd = vtkPolyData::New();
    pd->DeepCopy(rep->HandleTransformFilter->GetInput());
    this->HandleTransformFilter->SetInputData(pd);
    pd->Delete();

    this->LabelVisibility = rep->LabelVisibility;
    this->SetLabelText(rep->GetLabelText());
  }
  this->Superclass::DeepCopy(prop);
}

//------------------------------------------------------------------------------
void vtkAbstractPolygonalHandleRepresentation3D::GetActors(vtkPropCollection* pc)
{
  this->Actor->GetActors(pc);
  this->LabelTextActor->GetActors(pc);
}

//------------------------------------------------------------------------------
void vtkAbstractPolygonalHandleRepresentation3D::ReleaseGraphicsResources(vtkWindow* win)
{
  this->Actor->ReleaseGraphicsResources(win);
  this->LabelTextActor->ReleaseGraphicsResources(win);
}

//------------------------------------------------------------------------------
int vtkAbstractPolygonalHandleRepresentation3D::RenderOpaqueGeometry(vtkViewport* viewport)
{
  int count = 0;
  this->BuildRepresentation();
  if (this->HandleVisibility)
  {
    count += this->Actor->RenderOpaqueGeometry(viewport);
  }
  if (this->LabelVisibility)
  {
    count += this->LabelTextActor->RenderOpaqueGeometry(viewport);
  }
  return count;
}

//------------------------------------------------------------------------------
int vtkAbstractPolygonalHandleRepresentation3D::RenderTranslucentPolygonalGeometry(
  vtkViewport* viewport)
{
  // The internal actor needs to share property keys. This allows depth peeling
  // etc to work.
  this->Actor->SetPropertyKeys(this->GetPropertyKeys());

  int count = 0;
  if (this->HandleVisibility)
  {
    count += this->Actor->RenderTranslucentPolygonalGeometry(viewport);
  }
  if (this->LabelVisibility)
  {
    count += this->LabelTextActor->RenderTranslucentPolygonalGeometry(viewport);
  }
  return count;
}

//------------------------------------------------------------------------------
vtkTypeBool vtkAbstractPolygonalHandleRepresentation3D::HasTranslucentPolygonalGeometry()
{
  int result = 0;
  this->BuildRepresentation();
  if (this->HandleVisibility)
  {
    result |= this->Actor->HasTranslucentPolygonalGeometry();
  }
  if (this->LabelVisibility)
  {
    result |= this->LabelTextActor->HasTranslucentPolygonalGeometry();
  }
  return result;
}

//------------------------------------------------------------------------------
double* vtkAbstractPolygonalHandleRepresentation3D::GetBounds()
{
  this->BuildRepresentation();
  return this->Actor ? this->Actor->GetBounds() : nullptr;
}

//------------------------------------------------------------------------------
vtkAbstractTransform* vtkAbstractPolygonalHandleRepresentation3D::GetTransform()
{
  return this->HandleTransform;
}

//------------------------------------------------------------------------------
void vtkAbstractPolygonalHandleRepresentation3D::SetLabelText(const char* s)
{
  this->LabelTextInput->SetText(s);
}

//------------------------------------------------------------------------------
char* vtkAbstractPolygonalHandleRepresentation3D::GetLabelText()
{
  return this->LabelTextInput->GetText();
}

//------------------------------------------------------------------------------
void vtkAbstractPolygonalHandleRepresentation3D::SetLabelTextScale(double scale[3])
{
  this->LabelTextActor->SetScale(scale);
  this->LabelAnnotationTextScaleInitialized = true;
}

//------------------------------------------------------------------------------
double* vtkAbstractPolygonalHandleRepresentation3D::GetLabelTextScale()
{
  return this->LabelTextActor->GetScale();
}

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

  if (this->Property)
  {
    os << indent << "Property: " << this->Property << "\n";
  }
  else
  {
    os << indent << "Property: (none)\n";
  }
  if (this->SelectedProperty)
  {
    os << indent << "Selected Property: " << this->SelectedProperty << "\n";
  }
  else
  {
    os << indent << "Selected Property: (none)\n";
  }
  os << indent << "LabelVisibility: " << this->LabelVisibility << endl;
  os << indent << "HandleVisibility: " << this->HandleVisibility << endl;
  os << indent << "Actor: " << this->Actor << "\n";
  this->Actor->PrintSelf(os, indent.GetNextIndent());
  os << indent << "LabelTextActor: " << this->LabelTextActor << endl;
  this->LabelTextActor->PrintSelf(os, indent.GetNextIndent());
  os << indent << "Mapper: " << this->Mapper << "\n";
  this->Mapper->PrintSelf(os, indent.GetNextIndent());
  os << indent << "HandleTransformFilter: " << this->HandleTransformFilter << "\n";
  this->HandleTransformFilter->PrintSelf(os, indent.GetNextIndent());
  os << indent << "HandleTransform: " << this->HandleTransform << "\n";
  this->HandleTransform->PrintSelf(os, indent.GetNextIndent());
  os << indent << "HandleTransformMatrix: " << this->HandleTransformMatrix << "\n";
  this->HandleTransformMatrix->PrintSelf(os, indent.GetNextIndent());
  os << indent << "HandlePicker: " << this->HandlePicker << "\n";
  this->HandlePicker->PrintSelf(os, indent.GetNextIndent());
  os << indent << "LastPickPosition: (" << this->LastPickPosition[0] << ","
     << this->LastPickPosition[1] << ")\n";
  os << indent << "LastEventPosition: (" << this->LastEventPosition[0] << ","
     << this->LastEventPosition[1] << ")\n";
  os << indent << "SmoothMotion: " << this->SmoothMotion << endl;
  // ConstraintAxis;
  // WaitingForMotion;
  // WaitCount;
}
VTK_ABI_NAMESPACE_END
