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

#include "vtkCallbackCommand.h"
#include "vtkCamera.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkPerspectiveTransform.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkWindows.h"

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkInteractorStyleFlight);

class CPIDControl
{
public:
  int m_iDeltaT;
  int m_iDeltaTf;
  double m_dPrevX;
  double m_dKp;
  double m_dKd;
  double m_dKi;
  double m_dXSum;
  double m_dDelta;

  double m_dVelSum;
  int m_iVelCount;
  double m_dVelAvg;

  CPIDControl(double dKp, double dKd, double dKi);
  double PIDCalc(double dX, double dFinalX);
  void SetCoefficients(double dKp, double dKd, double dKi);
};

CPIDControl::CPIDControl(double dKp, double dKd, double dKi)
{
  m_dXSum = 0.0;
  m_dPrevX = 0.0;
  m_dDelta = 0.0;
  m_iDeltaT = 0;
  m_iDeltaTf = 0;
  m_dVelSum = 0.0;
  m_iVelCount = 0;
  m_dVelAvg = 0.0;
  this->SetCoefficients(dKp, dKd, dKi);
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
double CPIDControl::PIDCalc(double dX, double dFinalX)
{
  double dVal = dX - dFinalX;
  m_dXSum = m_dXSum + dVal;
  // Calculate the velocity
  dVal = m_dPrevX - dX;
  // Average the velocity
  m_dVelSum += dVal;
  m_iVelCount++;
  if (m_iVelCount >= 10)
  {
    m_dVelAvg = m_dVelSum / m_iVelCount;
    m_iVelCount = 0;
    m_dVelSum = 0.0;
  }
  m_dDelta = m_dKp * dX + m_dKd * m_dVelAvg + m_dKi * m_dXSum;
  m_dPrevX = dX;

  return m_dDelta;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void CPIDControl::SetCoefficients(double dKp, double dKd, double dKi)
{
  m_dKp = dKp;
  m_dKd = dKd;
  m_dKi = dKi;
  // should reset internal params here, but no need for this simple usage
}

//------------------------------------------------------------------------------
vtkInteractorStyleFlight::vtkInteractorStyleFlight()
{
  this->KeysDown = 0;
  this->UseTimers = 1;

  this->DiagonalLength = 1.0;
  this->MotionStepSize = 1.0 / 250.0;
  this->MotionUserScale = 1.0; // +/- key adjustment
  this->MotionAccelerationFactor = 10.0;
  this->AngleStepSize = 1.0;
  this->AngleAccelerationFactor = 5.0;

  this->DisableMotion = 0;
  this->RestoreUpVector = 1;
  this->DefaultUpVector[0] = 0;
  this->DefaultUpVector[1] = 0;
  this->DefaultUpVector[2] = 1;
  //
  PID_Yaw = new CPIDControl(-0.05, 0.0, -0.0008);
  PID_Pitch = new CPIDControl(-0.05, 0.0, -0.0008);
  Transform = vtkPerspectiveTransform::New();
}

//------------------------------------------------------------------------------
vtkInteractorStyleFlight::~vtkInteractorStyleFlight()
{
  Transform->Delete();
  delete PID_Yaw;
  delete PID_Pitch;
}
//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::ForwardFly()
{
  if (this->CurrentRenderer == nullptr)
  {
    return;
  }
  vtkCamera* camera = this->CurrentRenderer->GetActiveCamera();
  //
  if (this->KeysDown)
  {
    this->FlyByKey(camera);
  }
  else
  {
    this->UpdateSteering(camera);
    this->FlyByMouse(camera);
  }
  //
  this->FinishCamera(camera);
}
//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::ReverseFly()
{
  // The code is the same, just the state variable that is tracked...
  ForwardFly();
}

//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::StartForwardFly()
{
  if (this->State != VTKIS_NONE)
  {
    return;
  }
  this->StartState(VTKIS_FORWARDFLY);
}

//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::EndForwardFly()
{
  if (this->State != VTKIS_FORWARDFLY)
  {
    return;
  }
  this->StopState();
}

//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::StartReverseFly()
{
  if (this->State != VTKIS_NONE)
  {
    return;
  }
  this->StartState(VTKIS_REVERSEFLY);
}

//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::EndReverseFly()
{
  if (this->State != VTKIS_REVERSEFLY)
  {
    return;
  }
  this->StopState();
}

//------------------------------------------------------------------------------
// All actual motion is performed in the timer
//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::OnTimer()
{
  switch (this->State)
  {
    case VTKIS_FORWARDFLY:
      this->ForwardFly();
      break;

    case VTKIS_REVERSEFLY:
      this->ReverseFly();
      break;

    default:
      break;
  }
}

//------------------------------------------------------------------------------
// Mouse event handlers
//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::OnMouseMove()
{
  int x = this->Interactor->GetEventPosition()[0];
  int y = this->Interactor->GetEventPosition()[1];
  this->FindPokedRenderer(x, y);
  vtkCamera* cam = this->CurrentRenderer->GetActiveCamera();
  switch (this->State)
  {
    case VTKIS_FORWARDFLY:
    case VTKIS_REVERSEFLY:
      this->UpdateMouseSteering(cam);
      this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
      break;
  }
}

//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::OnLeftButtonDown()
{
  int x = this->Interactor->GetEventPosition()[0];
  int y = this->Interactor->GetEventPosition()[1];
  this->FindPokedRenderer(x, y);
  if (this->CurrentRenderer == nullptr)
  {
    return;
  }

  //
  this->GrabFocus(this->EventCallbackCommand);
  vtkCamera* cam = this->CurrentRenderer->GetActiveCamera();
  switch (this->State)
  {
    case VTKIS_REVERSEFLY:
      this->State = VTKIS_FORWARDFLY;
      break;
    default:
      this->SetupMotionVars(cam);
      this->StartForwardFly();
      break;
  }
}

//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::OnLeftButtonUp()
{
  switch (this->State)
  {
    case VTKIS_FORWARDFLY:
      this->EndForwardFly();
      break;
    default:
      break;
  }
  if (this->Interactor)
  {
    this->ReleaseFocus();
  }
}

//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::OnMiddleButtonDown() {}

//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::OnMiddleButtonUp() {}

//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::OnRightButtonDown()
{
  int x = this->Interactor->GetEventPosition()[0];
  int y = this->Interactor->GetEventPosition()[1];
  this->FindPokedRenderer(x, y);
  if (this->CurrentRenderer == nullptr)
  {
    return;
  }

  //
  this->GrabFocus(this->EventCallbackCommand);
  vtkCamera* cam = this->CurrentRenderer->GetActiveCamera();
  switch (this->State)
  {
    case VTKIS_FORWARDFLY:
      this->State = VTKIS_REVERSEFLY;
      break;
    default:
      this->SetupMotionVars(cam);
      this->StartReverseFly();
      break;
  }
}

//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::OnRightButtonUp()
{
  switch (this->State)
  {
    case VTKIS_REVERSEFLY:
      this->EndReverseFly();
      break;
    default:
      break;
  }
  if (this->Interactor)
  {
    this->ReleaseFocus();
  }
}

//------------------------------------------------------------------------------
// Keyboard event handlers
// Note, OnChar is a key press down and then up event
// Note, OnKeyDown/OnKeyUp are more sensitive for controlling motion
//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::OnKeyDown()
{
  // New Flight mode behaviour
  // Note that we'll need #defines for ARROW key defs under non win32 OS
#ifdef _WIN32
  switch (this->Interactor->GetKeyCode())
  {
    case VK_LEFT:
      this->KeysDown |= 1;
      break;
    case VK_RIGHT:
      this->KeysDown |= 2;
      break;
    case VK_UP:
      this->KeysDown |= 4;
      break;
    case VK_DOWN:
      this->KeysDown |= 8;
      break;
    case 'a':
    case 'A':
      this->KeysDown |= 16;
      break;
    case 'z':
    case 'Z':
      this->KeysDown |= 32;
      break;
  }
  if ((this->KeysDown & (32 + 16)) == (32 + 16))
  {
    if (this->State == VTKIS_FORWARDFLY)
    {
      this->EndForwardFly();
    }
    if (this->State == VTKIS_REVERSEFLY)
    {
      this->EndReverseFly();
    }
  }
  else if ((this->KeysDown & 32) == 32)
  {
    if (this->State == VTKIS_FORWARDFLY)
    {
      this->EndForwardFly();
    }
    this->StartReverseFly();
  }
  else if ((this->KeysDown & 16) == 16)
  {
    if (this->State == VTKIS_REVERSEFLY)
    {
      this->EndReverseFly();
    }
    this->StartForwardFly();
  }
#else
  // the following if statement is a dummy one to prevent keycode not used
  // warnings under unix, (until the correct keycodes are supplied)
  if (this->Interactor->GetKeyCode() == 0x7F)
  {
    vtkWarningMacro(<< "Dummy test to prevent compiler warning");
  }
#endif
}

//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::OnKeyUp()
{
#ifdef _WIN32
  switch (this->Interactor->GetKeyCode())
  {
    case VK_LEFT:
      this->KeysDown &= ~1;
      break;
    case VK_RIGHT:
      this->KeysDown &= ~2;
      break;
    case VK_UP:
      this->KeysDown &= ~4;
      break;
    case VK_DOWN:
      this->KeysDown &= ~8;
      break;
    case 'a':
    case 'A':
      this->KeysDown &= ~16;
      break;
    case 'z':
    case 'Z':
      this->KeysDown &= ~32;
      break;
  }
  switch (this->State)
  {
    case VTKIS_FORWARDFLY:
      if ((this->KeysDown & 16) == 0)
      {
        this->EndForwardFly();
      }
      break;
    case VTKIS_REVERSEFLY:
      if ((this->KeysDown & 32) == 0)
      {
        this->EndReverseFly();
      }
      break;
  }
#else
  // the following if statement is a dummy one to prevent keycode not used
  // warnings under unix, (until the correct keycodes are supplied)
  if (this->Interactor->GetKeyCode() == 0x7F)
  {
    vtkWarningMacro(<< "Dummy test to prevent compiler warning");
  }
#endif
}

//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::OnChar()
{
  switch (this->Interactor->GetKeyCode())
  {
    case '+':
      this->MotionUserScale *= 2.0;
      break;
    case '-':
      this->MotionUserScale *= 0.5;
      break;
    default:
      this->Superclass::OnChar();
      break;
  }
}

//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::JumpTo(double campos[3], double focpos[3])
{
  if (this->CurrentRenderer == nullptr)
  {
    return;
  }
  vtkCamera* cam = this->CurrentRenderer->GetActiveCamera();
  cam->SetPosition(campos);
  cam->SetFocalPoint(focpos);
  FinishCamera(cam);
  this->Interactor->Render();
}

void vtkInteractorStyleFlight::FinishCamera(vtkCamera* cam)
{
  cam->OrthogonalizeViewUp();
  if (this->RestoreUpVector)
  {
    double delta[3];
    cam->GetViewUp(delta);
    double weight = vtkMath::Dot(this->DefaultUpVector, delta);
    // only correct up if we're close to it already...
    if (weight > 0.3)
    {
      weight = 0.25 * fabs(weight);
      delta[0] = delta[0] + (this->DefaultUpVector[0] - delta[0]) * weight;
      delta[1] = delta[1] + (this->DefaultUpVector[1] - delta[1]) * weight;
      delta[2] = delta[2] + (this->DefaultUpVector[2] - delta[2]) * weight;
      cam->SetViewUp(delta);
    }
  }
  if (this->AutoAdjustCameraClippingRange)
  {
    this->CurrentRenderer->ResetCameraClippingRange();
  }
  if (this->Interactor->GetLightFollowCamera())
  {
    this->CurrentRenderer->UpdateLightsGeometryToFollowCamera();
  }
}

//------------------------------------------------------------------------------
// Use this mouse pos and last mouse pos to get the amount of motion
// Compute an "Ideal" focal point, The flight will sterr towards this ideal
// point, but will be damped in Yaw/Pitch by our PID Controllers.
// The damping and motion is done in the timer event.
//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::UpdateMouseSteering(vtkCamera* cam)
{
  int* thispos = this->Interactor->GetEventPosition();
  int* lastpos = this->Interactor->GetLastEventPosition();

  double aspeed =
    this->AngleStepSize * (this->Interactor->GetShiftKey() ? this->AngleAccelerationFactor : 1.0);
  //
  // we want to steer by an amount proportional to window viewangle and size
  // compute dx and dy increments relative to last mouse click
  const int* size = this->Interactor->GetSize();
  double scalefactor = 5 * cam->GetViewAngle() / size[0];
  double dx = -(thispos[0] - lastpos[0]) * scalefactor * aspeed;
  double dy = (thispos[1] - lastpos[1]) * scalefactor * aspeed;

  // Temporary until I get smooth flight working
  this->DeltaPitch = dy;
  this->DeltaYaw = dx;
  /*
    Not happy with smooth flight yet, please leave this code here
    until I get around to experimenting : JB July 2002

    // dx and dy need to be converted to a new 'ideal' camera focal point
    // we maintain an actual FocalPoint and an IdealFocalPoint so that we can
    // smooth the motion using our PID controllers (one for yaw, one for pitch).
    cam->OrthogonalizeViewUp();
    double LRaxis[3];
    double *ViewUp     = cam->GetViewUp();
    double *Position   = cam->GetPosition();
    double *FocalPoint = cam->GetFocalPoint();
    this->GetLRVector(LRaxis, cam);
    //
    this->Transform->Identity();
    this->Transform->Translate(+Position[0],+Position[1],+Position[2]);
    this->Transform->RotateWXYZ(dx, ViewUp);
    this->Transform->RotateWXYZ(dy, LRaxis);
    this->Transform->Translate(-Position[0],-Position[1],-Position[2]);
    this->Transform->TransformPoint(this->IdealFocalPoint,this->IdealFocalPoint);
  */
}

// We know the ideal and actual focal points. We now want to reduce these
// to a 2D Yaw+Pitch form so that we can smooth the motion on each
void vtkInteractorStyleFlight::UpdateSteering(vtkCamera* vtkNotUsed(cam))
{
  /*
    #define D2R 0.01745329251994329576923690768    // degrees to radians
    #define R2D 57.2957795130823208767981548141    // radians to degrees

    Not happy with smooth flight yet, please leave this code here
    until I get around to experimenting : JB July 2002

    cam->OrthogonalizeViewUp();
    double *Position = cam->GetPosition();
    double *FocalPoint = cam->GetFocalPoint();
    double vec1[3], vec2[3], norm1[3], norm2[3];
    vec1[0] = FocalPoint[0]-Position[0];
    vec1[1] = FocalPoint[1]-Position[1];
    vec1[2] = FocalPoint[2]-Position[2];
    vec2[0] = this->IdealFocalPoint[0]-Position[0];
    vec2[1] = this->IdealFocalPoint[1]-Position[1];
    vec2[2] = this->IdealFocalPoint[2]-Position[2];
    // what's the azimuthal angle between the Ideal and actual focal points
    // angle between planes given by Up/Focus and Up/IdealFocus
    double *ViewUp = cam->GetViewUp();
    vtkMath::Cross(vec1, ViewUp, norm1);
    vtkMath::Cross(vec2, ViewUp, norm2);
    vtkMath::Normalize(norm1);
    vtkMath::Normalize(norm2);
    //
    double dot = vtkMath::Dot(norm1, norm2);
    if (dot>1.0) {
      dot = 1.0;
    }
    else if (dot<-1.0) {
      dot =-1.0;
    }
    double yaw = R2D*acos(dot);
    // we know the angle, but is it +ve or -ve (which side is new focal point)
    double d1,d2;
    d1 = vtkMath::Dot(norm1,this->IdealFocalPoint);
    d2 = vtkMath::Dot(norm1,FocalPoint);
    if (d1-d2>0) yaw = -yaw;
    //
    // what's the elevation angle between the Ideal and actual focal points
    // angle between planes given by LR/Focus and LR/IdealFocus
    double LRaxis[3];
    this->GetLRVector(LRaxis, cam);
    vtkMath::Cross(vec1, LRaxis, norm1);
    vtkMath::Cross(vec2, LRaxis, norm2);
    vtkMath::Normalize(norm1);
    vtkMath::Normalize(norm2);
    //
    dot = vtkMath::Dot(norm1, norm2);
    if (dot>1.0) {
      dot = 1.0;
    } else if (dot<-1.0) {
      dot =-1.0;
    }
    double pitch = R2D*acos(dot);
    d1 = vtkMath::Dot(norm1,this->IdealFocalPoint);
    d2 = vtkMath::Dot(norm1,FocalPoint);
    if (d1-d2>0) pitch = -pitch;

    // use our motion dampers to reduce the yaw/pitch errors to zero gradually
  //  this->DeltaYaw   = PID_Yaw->PIDCalc(yaw, 0);
  //  this->DeltaPitch = PID_Pitch->PIDCalc(pitch, 0);

    this->DeltaPitch = 0.75*pitch + 0.25*lPitch;
    lPitch = pitch;

    this->DeltaYaw = 0.75*yaw + 0.25*lYaw;
    lYaw = yaw;

  */
}

//------------------------------------------------------------------------------
// useful utility functions
//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::SetupMotionVars(vtkCamera* cam)
{
  lPitch = 0;
  lYaw = 0;
  cam->GetFocalPoint(IdealFocalPoint);

  double bounds[6];
  this->CurrentRenderer->ComputeVisiblePropBounds(bounds);
  if (!vtkMath::AreBoundsInitialized(bounds))
  {
    this->DiagonalLength = 1.0;
  }
  else
  {
    this->DiagonalLength = sqrt((bounds[0] - bounds[1]) * (bounds[0] - bounds[1]) +
      (bounds[2] - bounds[3]) * (bounds[2] - bounds[3]) +
      (bounds[4] - bounds[5]) * (bounds[4] - bounds[5]));
  }
}

//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::MotionAlongVector(double vector[3], double amount, vtkCamera* cam)
{
  double oldcampos[3], oldcamfoc[3];
  cam->GetPosition(oldcampos);
  cam->GetFocalPoint(oldcamfoc);
  // move camera and focus along DirectionOfProjection
  cam->SetPosition(oldcampos[0] - amount * vector[0], oldcampos[1] - amount * vector[1],
    oldcampos[2] - amount * vector[2]);
  cam->SetFocalPoint(oldcamfoc[0] - amount * vector[0], oldcamfoc[1] - amount * vector[1],
    oldcamfoc[2] - amount * vector[2]);
}

//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::GetLRVector(double vector[3], vtkCamera* cam)
{
  vtkMatrix4x4* vtm;
  vtm = cam->GetViewTransformMatrix();
  vector[0] = vtm->GetElement(0, 0);
  vector[1] = vtm->GetElement(0, 1);
  vector[2] = vtm->GetElement(0, 2);
}

//------------------------------------------------------------------------------
// Perform the motion
//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::FlyByMouse(vtkCamera* cam)
{
  double a_vector[3];
  double speed = this->DiagonalLength * this->MotionStepSize * this->MotionUserScale;
  speed = speed * (this->Interactor->GetShiftKey() ? this->MotionAccelerationFactor : 1.0);
  if (this->DisableMotion)
  {
    speed = 0;
  }
  // Sidestep (convert steering angles to left right movement :
  // only because I added this after doing the angles earlier
  if (this->Interactor->GetControlKey())
  {
    if (this->DeltaYaw != 0.0)
    {
      this->GetLRVector(a_vector, cam);
      this->MotionAlongVector(a_vector, -this->DeltaYaw * speed / 2.0, cam);
    }
    if (this->DeltaPitch != 0.0)
    {
      cam->GetViewUp(a_vector);
      this->MotionAlongVector(a_vector, -this->DeltaPitch * speed / 2.0, cam);
    }
  }
  else
  {
    cam->Yaw(this->DeltaYaw);
    cam->Pitch(this->DeltaPitch);
    this->DeltaYaw = 0;
    this->DeltaPitch = 0;
    // cam->SetFocalPoint(this->IdealFocalPoint);
  }
  //
  if (!this->Interactor->GetControlKey())
  {
    cam->GetDirectionOfProjection(a_vector); // reversed (use -speed)
    switch (this->State)
    {
      case VTKIS_FORWARDFLY:
        this->MotionAlongVector(a_vector, -speed, cam);
        break;
      case VTKIS_REVERSEFLY:
        this->MotionAlongVector(a_vector, speed, cam);
        break;
    }
  }
}

//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::FlyByKey(vtkCamera* cam)
{
  double speed = this->DiagonalLength * this->MotionStepSize * this->MotionUserScale;
  speed = speed * (this->Interactor->GetShiftKey() ? this->MotionAccelerationFactor : 1.0);
  if (this->DisableMotion)
  {
    speed = 0;
  }
  //
  double aspeed =
    this->AngleStepSize * (this->Interactor->GetShiftKey() ? this->AngleAccelerationFactor : 1.0);
  double a_vector[3];
  // Left and right
  if (this->Interactor->GetControlKey())
  { // Sidestep
    this->GetLRVector(a_vector, cam);
    if (this->KeysDown & 1)
    {
      this->MotionAlongVector(a_vector, -speed, cam);
    }
    if (this->KeysDown & 2)
    {
      this->MotionAlongVector(a_vector, speed, cam);
    }
  }
  else
  {
    if (this->KeysDown & 1)
    {
      cam->Yaw(aspeed);
    }
    if (this->KeysDown & 2)
    {
      cam->Yaw(-aspeed);
    }
  }

  // Up and Down
  if (this->Interactor->GetControlKey())
  { // Sidestep
    cam->GetViewUp(a_vector);
    if (this->KeysDown & 4)
    {
      this->MotionAlongVector(a_vector, -speed, cam);
    }
    if (this->KeysDown & 8)
    {
      this->MotionAlongVector(a_vector, speed, cam);
    }
  }
  else
  {
    if (this->KeysDown & 4)
    {
      cam->Pitch(-aspeed);
    }
    if (this->KeysDown & 8)
    {
      cam->Pitch(aspeed);
    }
  }

  // forward and backward
  cam->GetDirectionOfProjection(a_vector);
  if (this->KeysDown & 16)
  {
    this->MotionAlongVector(a_vector, speed, cam);
  }
  if (this->KeysDown & 32)
  {
    this->MotionAlongVector(a_vector, -speed, cam);
  }
}

//------------------------------------------------------------------------------
void vtkInteractorStyleFlight::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << indent << "MotionStepSize: " << this->MotionStepSize << "\n";
  os << indent << "MotionAccelerationFactor: " << this->MotionAccelerationFactor << "\n";
  os << indent << "AngleStepSize: " << this->AngleStepSize << "\n";
  os << indent << "AngleAccelerationFactor: " << this->AngleAccelerationFactor << "\n";
  os << indent << "MotionUserScale: " << this->MotionUserScale << "\n";
  os << indent << "DisableMotion: " << this->DisableMotion << "\n";
  os << indent << "RestoreUpVector: " << this->RestoreUpVector << "\n";
  os << indent << "DefaultUpVector: " << this->DefaultUpVector[0] << " " << this->DefaultUpVector[1]
     << " " << this->DefaultUpVector[2] << "\n";
}
VTK_ABI_NAMESPACE_END
