/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkCamera.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.

=========================================================================*/
#include "vtkCamera.h"

#include "vtkInformation.h"
#include "vtkMath.h"
#include "vtkTimeStamp.h"
#include "vtkObjectFactory.h"
#include "vtkPerspectiveTransform.h"
#include "vtkTransform.h"
#include "vtkCallbackCommand.h"
#include "vtkRenderer.h"

#include <cassert>
#include <cmath>

//----------------------------------------------------------------------------
// Needed when we don't use the vtkStandardNewMacro.
vtkAbstractObjectFactoryNewMacro(vtkCamera)

vtkCxxSetObjectMacro(vtkCamera, Information, vtkInformation);
vtkCxxSetObjectMacro(vtkCamera, EyeTransformMatrix, vtkMatrix4x4);
vtkCxxSetObjectMacro(vtkCamera, ModelTransformMatrix, vtkMatrix4x4);
vtkCxxSetObjectMacro(vtkCamera, ExplicitProjectionTransformMatrix, vtkMatrix4x4)

//-----------------------------------------------------------------------------
class vtkCameraCallbackCommand : public vtkCommand
{
public:
  static vtkCameraCallbackCommand *New()
    { return new vtkCameraCallbackCommand; }
  vtkCamera *Self;
  void Execute(vtkObject *, unsigned long, void *) override
  {
      if (this->Self)
      {
        this->Self->Modified();
        this->Self->ComputeViewTransform();
        this->Self->ComputeDistance();
        this->Self->ComputeCameraLightTransform();
      }
  }
protected:
  vtkCameraCallbackCommand() { this->Self = nullptr; }
  ~vtkCameraCallbackCommand() override = default;
};

//----------------------------------------------------------------------------
// Construct camera instance with its focal point at the origin,
// and position=(0,0,1). The view up is along the y-axis,
// view angle is 30 degrees, and the clipping range is (.1,1000).
vtkCamera::vtkCamera()
{
  this->FocalPoint[0] = 0.0;
  this->FocalPoint[1] = 0.0;
  this->FocalPoint[2] = 0.0;

  this->Position[0] = 0.0;
  this->Position[1] = 0.0;
  this->Position[2] = 1.0;

  this->ViewUp[0] = 0.0;
  this->ViewUp[1] = 1.0;
  this->ViewUp[2] = 0.0;

  this->DirectionOfProjection[0] = 0.0;
  this->DirectionOfProjection[1] = 0.0;
  this->DirectionOfProjection[2] = 0.0;

  this->ViewAngle = 30.0;
  this->UseHorizontalViewAngle = 0;

  this->UseOffAxisProjection  = 0;

  this->ScreenBottomLeft[0] = -0.5;
  this->ScreenBottomLeft[1] = -0.5;
  this->ScreenBottomLeft[2] = -0.5;

  this->ScreenBottomRight[0] =  0.5;
  this->ScreenBottomRight[1] = -0.5;
  this->ScreenBottomRight[2] = -0.5;

  this->ScreenTopRight[0] =  0.5;
  this->ScreenTopRight[1] =  0.5;
  this->ScreenTopRight[2] = -0.5;

  this->EyeSeparation = 0.06;

  this->WorldToScreenMatrix = vtkMatrix4x4::New();
  this->WorldToScreenMatrix->Identity();

  this->EyeTransformMatrix = vtkMatrix4x4::New();
  this->EyeTransformMatrix->Identity();

  this->ModelTransformMatrix = vtkMatrix4x4::New();
  this->ModelTransformMatrix->Identity();

  this->ClippingRange[0] = 0.01;
  this->ClippingRange[1] = 1000.01;
  this->Thickness = 1000.0;

  this->ParallelProjection = 0;
  this->ParallelScale = 1.0;

  this->EyeAngle = 2.0;
  this->Stereo = 0;
  this->LeftEye = 1;

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

  this->ViewShear[0] = 0.0;
  this->ViewShear[1] = 0.0;
  this->ViewShear[2] = 1.0;

  this->FocalDisk = 1.0;
  this->FocalDistance = 0.0;

  this->Transform = vtkPerspectiveTransform::New();
  this->ViewTransform = vtkTransform::New();
  this->ProjectionTransform = vtkPerspectiveTransform::New();
  this->CameraLightTransform = vtkTransform::New();
  this->ModelViewTransform = vtkTransform::New();
  this->ExplicitProjectionTransformMatrix = nullptr;
  this->UseExplicitProjectionTransformMatrix = false;
  this->UserTransform = nullptr;
  this->UserViewTransform = nullptr;
  this->UserViewTransformCallbackCommand = nullptr;

  // initialize the ViewTransform
  this->ComputeViewTransform();
  this->ComputeDistance();
  this->ComputeCameraLightTransform();

  this->FreezeFocalPoint = false;
  this->UseScissor = false;

  this->Information = vtkInformation::New();
  this->Information->Register(this);
  this->Information->Delete();
}

//----------------------------------------------------------------------------
vtkCamera::~vtkCamera()
{
  this->WorldToScreenMatrix->Delete();
  this->WorldToScreenMatrix = nullptr;

  this->EyeTransformMatrix->Delete();
  this->EyeTransformMatrix = nullptr;

  this->ModelTransformMatrix->Delete();
  this->ModelTransformMatrix = nullptr;

  this->Transform->Delete();
  this->ViewTransform->Delete();
  this->ProjectionTransform->Delete();
  this->CameraLightTransform->Delete();
  this->ModelViewTransform->Delete();
  if (this->ExplicitProjectionTransformMatrix)
  {
    this->ExplicitProjectionTransformMatrix->UnRegister(this);
    this->ExplicitProjectionTransformMatrix = nullptr;
  }
  if (this->UserTransform)
  {
    this->UserTransform->UnRegister(this);
    this->UserTransform = nullptr;
  }
  if (this->UserViewTransform)
  {
    this->UserViewTransform->RemoveObserver(
      this->UserViewTransformCallbackCommand);
    this->UserViewTransform->UnRegister(this);
    this->UserViewTransform = nullptr;
  }
  if (this->UserViewTransformCallbackCommand)
  {
    this->UserViewTransformCallbackCommand->Delete();
  }

  this->SetInformation(nullptr);
}

//----------------------------------------------------------------------------
void vtkCamera::SetScissorRect(vtkRecti scissorRect)
{
  this->ScissorRect = scissorRect;
}

//----------------------------------------------------------------------------
void vtkCamera::GetScissorRect(vtkRecti& scissorRect)
{
  scissorRect = this->ScissorRect;
}

//----------------------------------------------------------------------------
// The first set of methods deal exclusively with the ViewTransform, which
// is the only transform which is set up entirely in the camera.  The
// perspective transform must be set up by the Renderer because the
// Camera doesn't know the Renderer's aspect ratio.
//----------------------------------------------------------------------------
void vtkCamera::SetPosition(double x, double y, double z)
{
  if (x == this->Position[0] &&
      y == this->Position[1] &&
      z == this->Position[2])
  {
    return;
  }

  this->Position[0] = x;
  this->Position[1] = y;
  this->Position[2] = z;

  vtkDebugMacro(<< " Position set to ( " <<  this->Position[0] << ", "
                << this->Position[1] << ", " << this->Position[2] << ")");

  this->ComputeViewTransform();
  // recompute the focal distance
  this->ComputeDistance();
  this->ComputeCameraLightTransform();

  this->Modified();
}

//----------------------------------------------------------------------------
void vtkCamera::SetUserTransform(vtkHomogeneousTransform *transform)
{
  if (transform == this->UserTransform)
  {
    return;
  }
  if (this->UserTransform)
  {
    this->UserTransform->Delete();
    this->UserTransform = nullptr;
  }
  if (transform)
  {
    this->UserTransform = transform;
    this->UserTransform->Register(this);
  }
  this->Modified();
}

//----------------------------------------------------------------------------
void vtkCamera::SetUserViewTransform(vtkHomogeneousTransform *transform)
{
  if (transform == this->UserViewTransform)
  {
    return;
  }
  if (this->UserViewTransform)
  {
    this->UserViewTransform->RemoveObserver(
      this->UserViewTransformCallbackCommand);
    this->UserViewTransform->Delete();
    this->UserViewTransform = nullptr;
  }
  if (transform)
  {
    this->UserViewTransform = transform;
    this->UserViewTransform->Register(this);
    if (!this->UserViewTransformCallbackCommand)
    {
      this->UserViewTransformCallbackCommand = vtkCameraCallbackCommand::New();
      this->UserViewTransformCallbackCommand->Self = this;
    }
    this->UserViewTransform->AddObserver(
      vtkCommand::ModifiedEvent,
      this->UserViewTransformCallbackCommand);
  }
  this->Modified();
  this->ComputeViewTransform();
  this->ComputeDistance();
  this->ComputeCameraLightTransform();
}

//----------------------------------------------------------------------------
void vtkCamera::SetFocalPoint(double x, double y, double z)
{
  if (x == this->FocalPoint[0] &&
      y == this->FocalPoint[1] &&
      z == this->FocalPoint[2])
  {
    return;
  }

  this->FocalPoint[0] = x;
  this->FocalPoint[1] = y;
  this->FocalPoint[2] = z;

  vtkDebugMacro(<< " FocalPoint set to ( " <<  this->FocalPoint[0] << ", " << this->FocalPoint[1] << ", " << this->FocalPoint[2] << ")");

  this->ComputeViewTransform();
  // recompute the focal distance
  this->ComputeDistance();
  this->ComputeCameraLightTransform();

  this->Modified();
}

//----------------------------------------------------------------------------
void vtkCamera::SetViewUp(double x, double y, double z)
{
  // normalize ViewUp, but do _not_ orthogonalize it by default
  double norm = sqrt(x*x + y*y + z*z);

  if(norm != 0)
  {
    x /= norm;
    y /= norm;
    z /= norm;
  }
  else
  {
    x = 0;
    y = 1;
    z = 0;
  }

  if (x == this->ViewUp[0] &&
      y == this->ViewUp[1] &&
      z == this->ViewUp[2])
  {
    return;
  }

  this->ViewUp[0] = x;
  this->ViewUp[1] = y;
  this->ViewUp[2] = z;

  vtkDebugMacro(<< " ViewUp set to ( " <<  this->ViewUp[0] << ", "
    << this->ViewUp[1] << ", " << this->ViewUp[2] << ")");

  this->ComputeViewTransform();
  this->ComputeCameraLightTransform();
  this->Modified();
}

//----------------------------------------------------------------------------
// The ViewTransform depends on only three ivars:  the Position, the
// FocalPoint, and the ViewUp vector.  All the other methods are there
// simply for the sake of the users' convenience.
void vtkCamera::ComputeViewTransform()
{
  // main view through the camera
  this->Transform->Identity();
  if (this->UserViewTransform)
  {
    this->Transform->Concatenate(this->UserViewTransform);
  }
  this->Transform->SetupCamera(this->Position, this->FocalPoint, this->ViewUp);
  this->ViewTransform->Identity();
  this->ViewTransform->Concatenate(this->Transform->GetMatrix());
}

//----------------------------------------------------------------------------
void vtkCamera::ComputeCameraLightTransform()
{
  vtkTransform *t;
  double d;

  // assumes a valid view transform and valid camera distance

  t = this->CameraLightTransform;
  t->Identity();
  t->SetMatrix(this->ViewTransform->GetMatrix());
  t->Inverse();

  d = this->Distance;
  t->Scale(d, d, d);
  t->Translate(0.0, 0.0, -1.0);
}

//----------------------------------------------------------------------------
void vtkCamera::ComputeWorldToScreenMatrix()
{
  // Avoid recalculating screen orientation if we don't need to.
  if(this->WorldToScreenMatrixMTime.GetMTime() < this->GetMTime())
  {
    double xAxis[3];
    double yAxis[3];
    double zAxis[3];

    for(int i = 0; i < 3; ++i)
    {
      xAxis[i] = this->ScreenBottomRight[i] - this->ScreenBottomLeft[i];
      yAxis[i] = this->ScreenTopRight[i]    - this->ScreenBottomRight[i];
    }

    vtkMath::Normalize(xAxis);
    vtkMath::Normalize(yAxis);
    vtkMath::Cross(xAxis, yAxis, zAxis);
    vtkMath::Normalize(zAxis);

    // Setting individual elements of the matrix.

    // Make it column major and then invert it to make sure the translation is correct
    // This is using column major (vectors are copied into the column)
    // \Note: while the initial element assignments are made in column-major
    // ordering, the matrix will be inverted, resulting in a row-major
    // matrix that provides the transformation from World to Screen space.
    this->WorldToScreenMatrix->SetElement(0, 0, xAxis[0]);
    this->WorldToScreenMatrix->SetElement(1, 0, xAxis[1]);
    this->WorldToScreenMatrix->SetElement(2, 0, xAxis[2]);

    this->WorldToScreenMatrix->SetElement(0, 1, yAxis[0]);
    this->WorldToScreenMatrix->SetElement(1, 1, yAxis[1]);
    this->WorldToScreenMatrix->SetElement(2, 1, yAxis[2]);

    this->WorldToScreenMatrix->SetElement(0, 2, zAxis[0]);
    this->WorldToScreenMatrix->SetElement(1, 2, zAxis[1]);
    this->WorldToScreenMatrix->SetElement(2, 2, zAxis[2]);

    this->WorldToScreenMatrix->SetElement(0, 3, this->ScreenBottomLeft[0]);
    this->WorldToScreenMatrix->SetElement(1, 3, this->ScreenBottomLeft[1]);
    this->WorldToScreenMatrix->SetElement(2, 3, this->ScreenBottomLeft[2]);

    this->WorldToScreenMatrix->SetElement(3, 3, 1.0);

    // The reason for doing this as an Invert as the goal here is to put
    // the translation through the rotation that we've just assigned ie.
    // the translation has to be put into screen space too.
    this->WorldToScreenMatrix->Invert();

    this->WorldToScreenMatrixMTime.Modified();
  }
}

//----------------------------------------------------------------------------
void vtkCamera::ComputeOffAxisProjectionFrustum()
{
  this->ComputeWorldToScreenMatrix();

  // \NOTE: Variable names reflect naming convention used in
  // "High Resolution Virtual Reality", in Proc.
  // SIGGRAPH '92, Computer Graphics, pages 195-202, 1992.

  // OffAxis calculations.


  // vtkMatrix::MultiplyPoint expect homogeneous coordinate.
  double E[4] = {0.0, 0.0, 0.0, 1.0};

  double L[4] = {this->ScreenBottomLeft[0], this->ScreenBottomLeft[1], this->ScreenBottomLeft[2], 1.0};
  double H[4] = {this->ScreenTopRight[0],   this->ScreenTopRight[1], this->ScreenTopRight[2], 1.0};

  double eyeSeparationCorrectionFactor = 10.0;
  double shiftDistance = this->EyeSeparation / (2.0 * eyeSeparationCorrectionFactor);
  if(this->Distance < 1.0)
  {
    shiftDistance *= this->Distance;
  }
  if(this->LeftEye)
  {
    E[0] -= shiftDistance;
  }
  else
  {
    E[0] += shiftDistance;
  }

  // First transform the eye to new position.
  this->EyeTransformMatrix->MultiplyPoint(E, E);

  // Now transform the eye and screen corner points into the screen
  // coordinate system.
  this->WorldToScreenMatrix->MultiplyPoint(E, E);
  this->WorldToScreenMatrix->MultiplyPoint(H, H);
  this->WorldToScreenMatrix->MultiplyPoint(L, L);

  double matrix[4][4];
  double width  = H[0] - L[0];
  double height = H[1] - L[1];

  // Back and front are not traditional near and far.
  // Front (aka near)
  double F = E[2] - (this->Distance + this->Thickness);//E[2] - 10000.0;//this->ClippingRange[1];
  // Back (aka far)
  double nearDistanceCorrectionFactor  = 1000.0;
  double B = E[2] - (this->Distance / nearDistanceCorrectionFactor);//E[2] - .1;//this->ClippingRange[0];

  double depth = B - F;
  matrix[0][0] =  ( 2*E[2] ) / width;
  matrix[1][0] =  0;
  matrix[2][0] =  0;
  matrix[3][0] =  0;

  matrix[0][1] =  0;
  matrix[1][1] =  ( 2*E[2] )/ height;
  matrix[2][1] =  0;
  matrix[3][1] =  0;

  matrix[0][2] = ( H[0]+L[0] - 2*E[0] )/width;
  matrix[1][2] = ( H[1]+L[1] - 2*E[1] )/height;
  matrix[2][2] = ( B+F-2*E[2] )/depth;
  matrix[3][2] = -1;

  matrix[0][3] = ( -E[2]*( H[0]+L[0] ) )/width;
  matrix[1][3] = ( -E[2]*( H[1]+L[1] ) )/height;
  matrix[2][3] = B-E[2]- ( B *( B+F - 2*E[2] )/depth );
  matrix[3][3] = E[2];

  for ( int i=0; i<4; i++ )
  {
    for ( int j=0; j<4; j++ )
    {
      this->ProjectionTransform->GetMatrix()->SetElement( i,j,  matrix[i][j] ) ;
    }
  }

  //  Now move the world into display space.
  vtkMatrix4x4::Multiply4x4(this->ProjectionTransform->GetMatrix(), this->WorldToScreenMatrix,
                            this->ProjectionTransform->GetMatrix());
}

//----------------------------------------------------------------------------
void vtkCamera::ComputeModelViewMatrix()
{
  if(this->ModelViewTransform->GetMTime() < this->ModelTransformMatrix->GetMTime() ||
     this->ModelViewTransform->GetMTime() < this->ViewTransform->GetMTime())
  {
    vtkMatrix4x4::Multiply4x4(this->ViewTransform->GetMatrix(), this->ModelTransformMatrix,
                              this->ModelViewTransform->GetMatrix());
  }
}

//----------------------------------------------------------------------------
void vtkCamera::OrthogonalizeViewUp()
{
  // the orthogonalized ViewUp is just the second row of the view matrix
  vtkMatrix4x4 *matrix = this->ViewTransform->GetMatrix();
  this->ViewUp[0] = matrix->GetElement(1,0);
  this->ViewUp[1] = matrix->GetElement(1,1);
  this->ViewUp[2] = matrix->GetElement(1,2);

  this->Modified();
}

//----------------------------------------------------------------------------
// Set the distance of the focal point from the camera. The focal point is
// modified accordingly. This should be positive.
void vtkCamera::SetDistance(double d)
{
  if (this->Distance == d)
  {
    return;
  }

  this->Distance = d;

  // Distance should be greater than .0002
  if (this->Distance < 0.0002)
  {
    this->Distance = 0.0002;
    vtkDebugMacro(<< " Distance is set to minimum.");
  }

  // we want to keep the camera pointing in the same direction
  double *vec = this->DirectionOfProjection;

  // recalculate FocalPoint
  this->FocalPoint[0] = this->Position[0] + vec[0]*this->Distance;
  this->FocalPoint[1] = this->Position[1] + vec[1]*this->Distance;
  this->FocalPoint[2] = this->Position[2] + vec[2]*this->Distance;

  vtkDebugMacro(<< " Distance set to ( " <<  this->Distance << ")");

  this->ComputeViewTransform();
  this->ComputeCameraLightTransform();
  this->Modified();
}

//----------------------------------------------------------------------------
// This method must be called when the focal point or camera position changes
void vtkCamera::ComputeDistance()
{
  double dx = this->FocalPoint[0] - this->Position[0];
  double dy = this->FocalPoint[1] - this->Position[1];
  double dz = this->FocalPoint[2] - this->Position[2];

  this->Distance = sqrt(dx*dx + dy*dy + dz*dz);

  if (this->Distance < 1e-20)
  {
    this->Distance = 1e-20;
    vtkDebugMacro(<< " Distance is set to minimum.");

    double *vec = this->DirectionOfProjection;

    // recalculate FocalPoint
    this->FocalPoint[0] = this->Position[0] + vec[0]*this->Distance;
    this->FocalPoint[1] = this->Position[1] + vec[1]*this->Distance;
    this->FocalPoint[2] = this->Position[2] + vec[2]*this->Distance;
  }

  this->DirectionOfProjection[0] = dx/this->Distance;
  this->DirectionOfProjection[1] = dy/this->Distance;
  this->DirectionOfProjection[2] = dz/this->Distance;

  this->ComputeViewPlaneNormal();
}

//----------------------------------------------------------------------------
// Move the position of the camera along the view plane normal. Moving
// towards the focal point (e.g., > 1) is a dolly-in, moving away
// from the focal point (e.g., < 1) is a dolly-out.
void vtkCamera::Dolly(double amount)
{
  if (amount <= 0.0)
  {
    return;
  }

  // dolly moves the camera towards the focus
  double d = this->Distance/amount;

  this->SetPosition(this->FocalPoint[0] - d*this->DirectionOfProjection[0],
                    this->FocalPoint[1] - d*this->DirectionOfProjection[1],
                    this->FocalPoint[2] - d*this->DirectionOfProjection[2]);
}

//----------------------------------------------------------------------------
// Set the roll angle of the camera about the direction of projection
void vtkCamera::SetRoll(double roll)
{
  // roll is a rotation of camera view up about the direction of projection
  vtkDebugMacro(<< " Setting Roll to " << roll << "");

  // subtract the current roll
  roll -= this->GetRoll();

  if (fabs(roll) < 0.00001)
  {
    return;
  }

  this->Roll(roll);
}

//----------------------------------------------------------------------------
// Returns the roll of the camera.
double vtkCamera::GetRoll()
{
  double orientation[3];
  this->ViewTransform->GetOrientation(orientation);
  return orientation[2];
}

//----------------------------------------------------------------------------
// Rotate the camera around the view plane normal.
void vtkCamera::Roll(double angle)
{
  double newViewUp[3];
  this->Transform->Identity();

  // rotate ViewUp about the Direction of Projection
  this->Transform->RotateWXYZ(angle,this->DirectionOfProjection);

  // okay, okay, TransformPoint shouldn't be used on vectors -- but
  // the transform is rotation with no translation so this works fine.
  this->Transform->TransformPoint(this->ViewUp,newViewUp);
  this->SetViewUp(newViewUp);
}

//----------------------------------------------------------------------------
// Rotate the focal point about the view up vector centered at the camera's
// position.
void vtkCamera::Yaw(double angle)
{
  double newFocalPoint[3];
  double *pos = this->Position;
  this->Transform->Identity();

  // translate the camera to the origin,
  // rotate about axis,
  // translate back again
  this->Transform->Translate(+pos[0],+pos[1],+pos[2]);
  this->Transform->RotateWXYZ(angle,this->ViewUp);
  this->Transform->Translate(-pos[0],-pos[1],-pos[2]);

  // now transform focal point
  this->Transform->TransformPoint(this->FocalPoint,newFocalPoint);
  this->SetFocalPoint(newFocalPoint);
}

//----------------------------------------------------------------------------
// Rotate the focal point about the cross product of the view up vector
// and the negative of the , centered at the camera's position.
void vtkCamera::Pitch(double angle)
{
  double axis[3], newFocalPoint[3], savedViewUp[3];
  double *pos = this->Position;
  this->Transform->Identity();

  // the axis is the first row of the view transform matrix
  axis[0] = this->ViewTransform->GetMatrix()->GetElement(0,0);
  axis[1] = this->ViewTransform->GetMatrix()->GetElement(0,1);
  axis[2] = this->ViewTransform->GetMatrix()->GetElement(0,2);

  // temporarily set the view up with the transformation applied
  // to avoid bad cross product computations during SetFocalPoint call
  this->GetViewUp(savedViewUp);
  this->Transform->RotateWXYZ(angle, axis);
  this->Transform->TransformPoint(this->ViewUp, this->ViewUp);
  this->Transform->Identity();

  // translate the camera to the origin,
  // rotate about axis,
  // translate back again
  this->Transform->Translate(+pos[0],+pos[1],+pos[2]);
  this->Transform->RotateWXYZ(angle,axis);
  this->Transform->Translate(-pos[0],-pos[1],-pos[2]);

  // now transform focal point
  this->Transform->TransformPoint(this->FocalPoint,newFocalPoint);
  this->SetFocalPoint(newFocalPoint);

  // restore the previous ViewUp vector
  this->ViewUp[0] = savedViewUp[0];
  this->ViewUp[1] = savedViewUp[1];
  this->ViewUp[2] = savedViewUp[2];
  // this is needed since the last time Modified was called (in SetFocalPoint),
  // the ViewUp was not same as savedViewUp. Since we're changing its value
  // here, we need to fire Modified event. We don't call `SetViewUp` since we
  // don't want the computation of the view transform to happen again.
  this->Modified();
}

//----------------------------------------------------------------------------
// Rotate the camera about the view up vector centered at the focal point.
void vtkCamera::Azimuth(double angle)
{
  double newPosition[3];
  double *fp = this->FocalPoint;
  this->Transform->Identity();

  // translate the focal point to the origin,
  // rotate about view up,
  // translate back again
  this->Transform->Translate(+fp[0],+fp[1],+fp[2]);
  this->Transform->RotateWXYZ(angle,this->ViewUp);
  this->Transform->Translate(-fp[0],-fp[1],-fp[2]);

  // apply the transform to the position
  this->Transform->TransformPoint(this->Position,newPosition);
  this->SetPosition(newPosition);
}

//----------------------------------------------------------------------------
// Rotate the camera about the cross product of the negative of the
// direction of projection and the view up vector centered on the focal point.
void vtkCamera::Elevation(double angle)
{
  double axis[3], newPosition[3], savedViewUp[3];
  double *fp = this->FocalPoint;
  this->Transform->Identity();

  // snatch the axis from the view transform matrix
  axis[0] = -this->ViewTransform->GetMatrix()->GetElement(0,0);
  axis[1] = -this->ViewTransform->GetMatrix()->GetElement(0,1);
  axis[2] = -this->ViewTransform->GetMatrix()->GetElement(0,2);

  // temporarily set the view up with the transformation applied
  // to avoid bad cross product computations during SetPosition call
  this->GetViewUp(savedViewUp);
  this->Transform->RotateWXYZ(angle, axis);
  this->Transform->TransformPoint(this->ViewUp, this->ViewUp);
  this->Transform->Identity();

    // translate the focal point to the origin,
  // rotate about axis,
  // translate back again
  this->Transform->Translate(+fp[0],+fp[1],+fp[2]);
  this->Transform->RotateWXYZ(angle,axis);
  this->Transform->Translate(-fp[0],-fp[1],-fp[2]);

  // now transform position
  this->Transform->TransformPoint(this->Position,newPosition);
  this->SetPosition(newPosition);

  // restore the previous ViewUp vector
  this->ViewUp[0] = savedViewUp[0];
  this->ViewUp[1] = savedViewUp[1];
  this->ViewUp[2] = savedViewUp[2];
  // this is needed since the last time Modified was called (in SetPosition),
  // the ViewUp was not same as savedViewUp. Since we're changing its value
  // here, we need to fire Modified event. We don't call `SetViewUp` since we
  // don't want the computation of the view transform to happen again.
  this->Modified();
}

//----------------------------------------------------------------------------
// Apply Transform to camera
void vtkCamera::ApplyTransform(vtkTransform *t)
{
  double posOld[4], posNew[4], fpOld[4], fpNew[4], vuOld[4], vuNew[4];

  this->GetPosition(posOld);
  this->GetFocalPoint(fpOld);
  this->GetViewUp(vuOld);

  posOld[3] = 1.0;
  fpOld[3] = 1.0;
  vuOld[3] = 1.0;

  vuOld[0] += posOld[0];
  vuOld[1] += posOld[1];
  vuOld[2] += posOld[2];

  t->MultiplyPoint(posOld, posNew);
  t->MultiplyPoint(fpOld, fpNew);
  t->MultiplyPoint(vuOld, vuNew);

  vuNew[0] -= posNew[0];
  vuNew[1] -= posNew[1];
  vuNew[2] -= posNew[2];

  this->SetPosition(posNew);
  this->SetFocalPoint(fpNew);
  this->SetViewUp(vuNew);
}

//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// The following methods set up the information that the Renderer needs
// to set up the perspective transform.  The transformation matrix is
// created using the GetPerspectiveTransformMatrix method.
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
void vtkCamera::SetParallelProjection(vtkTypeBool flag)
{
  if ( this->ParallelProjection != flag )
  {
    this->ParallelProjection = flag;
    this->Modified();
    this->ViewingRaysModified();
  }
}

//----------------------------------------------------------------------------
void vtkCamera::SetViewAngle(double angle)
{
  double min = 0.00000001;
  double max = 179.0;

  if ( this->ViewAngle != angle )
  {
    this->ViewAngle = (angle<min?min:(angle>max?max:angle));
    this->Modified();
    this->ViewingRaysModified();
  }
}

//----------------------------------------------------------------------------
void vtkCamera::SetUseHorizontalViewAngle(vtkTypeBool flag)
{
  if (flag == this->UseHorizontalViewAngle)
  {
    return;
  }
  this->UseHorizontalViewAngle = flag;
  this->Modified();
  this->ViewingRaysModified();
}

//----------------------------------------------------------------------------
void vtkCamera::SetParallelScale(double scale)
{
  if ( this->ParallelScale != scale )
  {
    this->ParallelScale = scale;
    this->Modified();
    this->ViewingRaysModified();
  }
}

//----------------------------------------------------------------------------
// Change the ViewAngle (for perspective) or the ParallelScale (for parallel)
// so that more or less of a scene occupies the viewport.  A value > 1 is a
// zoom-in. A value < 1 is a zoom-out.
void vtkCamera::Zoom(double amount)
{
  if (amount <= 0.0)
  {
    return;
  }

  if (this->ParallelProjection)
  {
    this->SetParallelScale(this->ParallelScale/amount);
  }
  else
  {
    this->SetViewAngle(this->ViewAngle/amount);
  }
}

//----------------------------------------------------------------------------
void vtkCamera::SetClippingRange(double nearz, double farz)
{
  double thickness;

  // check the order
  if ( nearz > farz )
  {
    vtkDebugMacro(<< " Front and back clipping range reversed");
    double temp = nearz;
    nearz = farz;
    farz = temp;
  }

  thickness = farz - nearz;

  // thickness should be greater than 1e-20
  if (thickness < 1e-20)
  {
    thickness = 1e-20;
    vtkDebugMacro(<< " ClippingRange thickness is set to minimum.");

    // set back plane
    farz = nearz + thickness;
  }

  if (nearz == this->ClippingRange[0] &&
      farz == this->ClippingRange[1] &&
      this->Thickness == thickness)
  {
    return;
  }

  this->ClippingRange[0] = nearz;
  this->ClippingRange[1] = farz;
  this->Thickness = thickness;

  vtkDebugMacro(<< " ClippingRange set to ( " <<  this->ClippingRange[0]
    << ", "  << this->ClippingRange[1] << ")");

  this->Modified();
}

//----------------------------------------------------------------------------
// Set the distance between clipping planes.
// This method adjusts the back clipping plane to the specified thickness
// behind the front clipping plane
void vtkCamera::SetThickness(double s)
{
  if (this->Thickness == s)
  {
    return;
  }

  this->Thickness = s;

  // thickness should be greater than 1e-20
  if (this->Thickness < 1e-20)
  {
    this->Thickness = 1e-20;
    vtkDebugMacro(<< " ClippingRange thickness is set to minimum.");
  }

  // set back plane
  this->ClippingRange[1] = this->ClippingRange[0] + this->Thickness;

  vtkDebugMacro(<< " ClippingRange set to ( " <<  this->ClippingRange[0]
    << ", " << this->ClippingRange[1] << ")");

  this->Modified();
}

//----------------------------------------------------------------------------
void vtkCamera::SetWindowCenter(double x, double y)
{
  if (this->WindowCenter[0] != x || this->WindowCenter[1] != y)
  {
    this->Modified();
    this->ViewingRaysModified();
    this->WindowCenter[0] = x;
    this->WindowCenter[1] = y;
  }
}

//----------------------------------------------------------------------------
void vtkCamera::SetObliqueAngles(double alpha, double beta)
{
  alpha = vtkMath::RadiansFromDegrees( alpha );
  beta = vtkMath::RadiansFromDegrees( beta );

  double cotbeta = cos( beta ) / sin( beta );
  double dxdz = cos( alpha ) * cotbeta;
  double dydz = sin( alpha ) * cotbeta;

  this->SetViewShear( dxdz, dydz, 1.0 );
}

//----------------------------------------------------------------------------
// Set the shear transform of the viewing frustum.  Parameters are
// dx/dz, dy/dz, and center.  center is a factor that describes where
// to shear around. The distance dshear from the camera where
// no shear occurs is given by (dshear = center * FocalDistance).
//
void vtkCamera::SetViewShear(double dxdz, double dydz, double center)
{
  if(dxdz   != this->ViewShear[0] ||
     dydz   != this->ViewShear[1] ||
     center != this->ViewShear[2])
  {
    this->Modified();
    this->ViewingRaysModified();

    this->ViewShear[0] = dxdz;
    this->ViewShear[1] = dydz;
    this->ViewShear[2] = center;

    this->ComputeViewPlaneNormal();
  }
}
//----------------------------------------------------------------------------

void vtkCamera::SetViewShear(double d[3])
{
  this->SetViewShear(d[0], d[1], d[2]);
}

//----------------------------------------------------------------------------
// Compute the projection transform matrix. This is used in converting
// between view and world coordinates.
void vtkCamera::ComputeProjectionTransform(double aspect,
                                           double nearz, double farz)
{
  this->ProjectionTransform->Identity();

  // apply user defined transform last if there is one
  if ( this->UserTransform )
  {
    this->ProjectionTransform->Concatenate( this->UserTransform->GetMatrix() );
  }

  if (this->UseExplicitProjectionTransformMatrix)
  {
    assert(this->ExplicitProjectionTransformMatrix != nullptr);
    this->ProjectionTransform->Concatenate(
          this->ExplicitProjectionTransformMatrix);
    return;
  }

  // adjust Z-buffer range
  this->ProjectionTransform->AdjustZBuffer( -1, +1, nearz, farz );

  if ( this->ParallelProjection )
  {
    // set up a rectangular parallelipiped

    double width = this->ParallelScale * aspect;
    double height = this->ParallelScale;

    double xmin = ( this->WindowCenter[0] - 1.0 ) * width;
    double xmax = ( this->WindowCenter[0] + 1.0 ) * width;
    double ymin = ( this->WindowCenter[1] - 1.0 ) * height;
    double ymax = ( this->WindowCenter[1] + 1.0 ) * height;

    this->ProjectionTransform->Ortho( xmin, xmax, ymin, ymax,
                                      this->ClippingRange[0],
                                      this->ClippingRange[1] );
  }
  else if(this->UseOffAxisProjection)
  {
    this->ComputeOffAxisProjectionFrustum();
  }
  else
  {
    // set up a perspective frustum

    double tmp = tan( vtkMath::RadiansFromDegrees( this->ViewAngle ) / 2. );
    double width;
    double height;
    if ( this->UseHorizontalViewAngle )
    {
      width = this->ClippingRange[0] * tmp;
      height = this->ClippingRange[0] * tmp / aspect;
    }
    else
    {
      width = this->ClippingRange[0] * tmp * aspect;
      height = this->ClippingRange[0] * tmp;
    }

    double xmin = ( this->WindowCenter[0] - 1.0 ) * width;
    double xmax = ( this->WindowCenter[0] + 1.0 ) * width;
    double ymin = ( this->WindowCenter[1] - 1.0 ) * height;
    double ymax = ( this->WindowCenter[1] + 1.0 ) * height;

    this->ProjectionTransform->Frustum( xmin, xmax, ymin, ymax,
                                        this->ClippingRange[0],
                                        this->ClippingRange[1] );
  }

  if ( this->Stereo && !this->UseOffAxisProjection)
  {
    // set up a shear for stereo views
    if ( this->LeftEye )
    {
      this->ProjectionTransform->Stereo( -this->EyeAngle/2,
                                          this->Distance );
    }
    else
    {
      this->ProjectionTransform->Stereo( +this->EyeAngle/2,
                                          this->Distance );
    }
  }

  if ( this->ViewShear[0] != 0.0 || this->ViewShear[1] != 0.0 )
  {
    this->ProjectionTransform->Shear( this->ViewShear[0],
                                      this->ViewShear[1],
                                      this->ViewShear[2] * this->Distance );
  }

}

//----------------------------------------------------------------------------
// Return the projection transform matrix. See ComputeProjectionTransform.
vtkMatrix4x4 *vtkCamera::GetProjectionTransformMatrix(vtkRenderer *ren)
{
  double aspect[2];
  int  lowerLeft[2];
  int usize, vsize;
  vtkMatrix4x4 *matrix = vtkMatrix4x4::New();

  ren->GetTiledSizeAndOrigin(&usize, &vsize, lowerLeft, lowerLeft+1);

  // some renderer subclasses may have more complicated computations for the
  // aspect ratio. So take that into account by computing the difference
  // between our simple aspect ratio and what the actual renderer is reporting.
  ren->ComputeAspect();
  ren->GetAspect(aspect);
  double aspect2[2];
  ren->vtkViewport::ComputeAspect();
  ren->vtkViewport::GetAspect(aspect2);
  double aspectModification = aspect[0] * aspect2[1] / (aspect[1] * aspect2[0]);

  if (usize && vsize)
  {
    matrix->DeepCopy(this->GetProjectionTransformMatrix(
                       aspectModification * usize / vsize, -1, 1));
    matrix->Transpose();
  }

  return matrix;
}

//----------------------------------------------------------------------------
// Return the projection transform matrix. See ComputeProjectionTransform.
vtkMatrix4x4 *vtkCamera::GetProjectionTransformMatrix(double aspect,
                                                      double nearz,
                                                      double farz)
{
  this->ComputeProjectionTransform(aspect, nearz, farz);

  // return the transform
  return this->ProjectionTransform->GetMatrix();
}

//----------------------------------------------------------------------------
// Return the projection transform object. See ComputeProjectionTransform.
vtkPerspectiveTransform *vtkCamera::GetProjectionTransformObject(double aspect,
                                                                 double nearz,
                                                                 double farz)
{
  this->ComputeProjectionTransform(aspect, nearz, farz);

  // return the transform
  return this->ProjectionTransform;
}

//----------------------------------------------------------------------------
// Return the projection transform matrix. See ComputeProjectionTransform.
vtkMatrix4x4 *vtkCamera::GetCompositeProjectionTransformMatrix(double aspect,
                                                               double nearz,
                                                               double farz)
{
  // turn off stereo, the CompositeProjectionTransformMatrix is used for
  // picking, not for rendering.
  int stereo = this->Stereo;
  this->Stereo = 0;

  this->Transform->Identity();
  this->Transform->Concatenate(this->GetProjectionTransformMatrix(aspect,
                                                                  nearz,
                                                                  farz));
  this->Transform->Concatenate(this->GetViewTransformMatrix());

  this->Stereo = stereo;

  // return the transform
  return this->Transform->GetMatrix();
}

//----------------------------------------------------------------------------
// Return the attached light transform matrix.
vtkMatrix4x4 *vtkCamera::GetCameraLightTransformMatrix()
{
  // return the transform
  return this->CameraLightTransform->GetMatrix();
}


//----------------------------------------------------------------------------
void vtkCamera::ComputeViewPlaneNormal()
{
  if (this->ViewShear[0] != 0.0 || this->ViewShear[1] != 0.0)
  {
    // set the VPN in camera coordinates
    this->ViewPlaneNormal[0] = this->ViewShear[0];
    this->ViewPlaneNormal[1] = this->ViewShear[1];
    this->ViewPlaneNormal[2] = 1.0;
    // transform the VPN to world coordinates using inverse of view transform
    this->ViewTransform->GetLinearInverse()->TransformNormal(
                                              this->ViewPlaneNormal,
                                              this->ViewPlaneNormal);
  }
  else
  {
    // VPN is -DOP
    this->ViewPlaneNormal[0] = -this->DirectionOfProjection[0];
    this->ViewPlaneNormal[1] = -this->DirectionOfProjection[1];
    this->ViewPlaneNormal[2] = -this->DirectionOfProjection[2];
  }
}

//----------------------------------------------------------------------------
// Return the 6 planes (Ax + By + Cz + D = 0) that bound
// the view frustum.
void vtkCamera::GetFrustumPlanes(double aspect, double planes[24])
{
  int i;
  double f, normals[6][4], matrix[4][4];

  // set up the normals
  for (i = 0; i < 6; i++)
  {
    normals[i][0] = 0.0;
    normals[i][1] = 0.0;
    normals[i][2] = 0.0;
    normals[i][3] = 1.0;
    // if i is even set to 1, if odd set to -1
    normals[i][i/2] = 1 - (i%2)*2;
  }

  // get the composite perspective matrix
  vtkMatrix4x4::DeepCopy(
    *matrix,
    this->GetCompositeProjectionTransformMatrix(aspect,-1,+1));

  // transpose the matrix for use with normals
  vtkMatrix4x4::Transpose(*matrix,*matrix);

  // transform the normals to world coordinates
  for (i = 0; i < 6; i++)
  {
    vtkMatrix4x4::MultiplyPoint(*matrix,normals[i],normals[i]);

    f = 1.0/sqrt(normals[i][0]*normals[i][0] +
                 normals[i][1]*normals[i][1] +
                 normals[i][2]*normals[i][2]);

    planes[4*i + 0] = normals[i][0]*f;
    planes[4*i + 1] = normals[i][1]*f;
    planes[4*i + 2] = normals[i][2]*f;
    planes[4*i + 3] = normals[i][3]*f;
  }
}

//----------------------------------------------------------------------------
vtkMTimeType vtkCamera::GetViewingRaysMTime()
{
  return this->ViewingRaysMTime.GetMTime();
}

//----------------------------------------------------------------------------
void vtkCamera::ViewingRaysModified()
{
  this->ViewingRaysMTime.Modified();
}

// ----------------------------------------------------------------------------
// Description:
// Copy the properties of `source' into `this'.
// Copy pointers of matrices.
// \pre source_exists!=0
// \pre not_this: source!=this
void vtkCamera::ShallowCopy(vtkCamera *source)
{
  assert("pre: source_exists" && source!=nullptr);
  assert("pre: not_this" && source!=this);

  this->PartialCopy(source);

  // Shallow copy of matrices:
  if(this->UserTransform!=nullptr)
  {
    this->UserTransform->Delete();
  }
  this->UserTransform=source->UserTransform;
  if(this->UserTransform!=nullptr)
  {
    this->UserTransform->Register(this);
  }
  if(this->UserViewTransform!=nullptr)
  {
    this->UserViewTransform->Delete();
  }
  this->UserViewTransform=source->UserViewTransform;
  if(this->UserViewTransform!=nullptr)
  {
    this->UserViewTransform->Register(this);
  }

  if(this->ViewTransform!=nullptr)
  {
    this->ViewTransform->Delete();
  }
  this->ViewTransform=source->ViewTransform;
  if(this->ViewTransform!=nullptr)
  {
    this->ViewTransform->Register(this);
  }

  if(this->ProjectionTransform!=nullptr)
  {
    this->ProjectionTransform->Delete();
  }
  this->ProjectionTransform=source->ProjectionTransform;
  if(this->ProjectionTransform!=nullptr)
  {
    this->ProjectionTransform->Register(this);
  }

  if(this->Transform!=nullptr)
  {
    this->Transform->Delete();
  }
  this->Transform=source->Transform;
  if(this->Transform!=nullptr)
  {
    this->Transform->Register(this);
  }

  if(this->CameraLightTransform!=nullptr)
  {
    this->CameraLightTransform->Delete();
  }
  this->CameraLightTransform=source->CameraLightTransform;
  if(this->CameraLightTransform!=nullptr)
  {
    this->CameraLightTransform->Register(this);
  }

  if (this->EyeTransformMatrix != nullptr)
  {
    this->EyeTransformMatrix->Delete();
  }
  this->EyeTransformMatrix = source->EyeTransformMatrix;
  if (this->EyeTransformMatrix!=nullptr)
  {
    this->EyeTransformMatrix->Register(this);
  }

  if (this->WorldToScreenMatrix != nullptr)
  {
    this->WorldToScreenMatrix->Delete();
  }
  this->WorldToScreenMatrix = source->WorldToScreenMatrix;
  if (this->WorldToScreenMatrix!=nullptr)
  {
    this->WorldToScreenMatrix->Register(this);
  }

  if (this->ModelTransformMatrix != nullptr)
  {
    this->ModelTransformMatrix->Delete();
  }
  this->ModelTransformMatrix = source->ModelTransformMatrix;
  if (this->ModelTransformMatrix!=nullptr)
  {
    this->ModelTransformMatrix->Register(this);
  }

  if (this->ModelViewTransform != nullptr)
  {
    this->ModelViewTransform->Delete();
  }
  this->ModelViewTransform = source->ModelViewTransform;
  if (this->ModelViewTransform!=nullptr)
  {
    this->ModelViewTransform->Register(this);
  }
}

// ----------------------------------------------------------------------------
// Description:
// Copy the properties of `source' into `this'.
// Copy the contents of the matrices.
// \pre source_exists!=0
// \pre not_this: source!=this
void vtkCamera::DeepCopy(vtkCamera *source)
{
  assert("pre: source_exists" && source!=nullptr);
  assert("pre: not_this" && source!=this);

  this->PartialCopy(source);

  // Deep copy the matrices:
  if(source->UserTransform==nullptr)
  {
    if(this->UserTransform!=nullptr)
    {
      this->UserTransform->UnRegister(this);
      this->UserTransform=nullptr;
    }
  }
  else
  {
    if(this->UserTransform==nullptr)
    {
      this->UserTransform=
        static_cast<vtkHomogeneousTransform *>(
          source->UserTransform->MakeTransform());
    }
    this->UserTransform->DeepCopy(source->UserTransform);
  }

  if(source->UserViewTransform==nullptr)
  {
    if(this->UserViewTransform!=nullptr)
    {
      this->UserViewTransform->UnRegister(this);
      this->UserViewTransform=nullptr;
    }
  }
  else
  {
    if(this->UserViewTransform==nullptr)
    {
      this->UserViewTransform=
        static_cast<vtkHomogeneousTransform *>(
          source->UserViewTransform->MakeTransform());
    }
    this->UserViewTransform->DeepCopy(source->UserViewTransform);
  }

  if(source->ViewTransform==nullptr)
  {
    if(this->ViewTransform!=nullptr)
    {
      this->ViewTransform->UnRegister(this);
      this->ViewTransform=nullptr;
    }
  }
  else
  {
    if(this->ViewTransform==nullptr)
    {
      this->ViewTransform=
        static_cast<vtkTransform *>(
          source->ViewTransform->MakeTransform());
    }
    this->ViewTransform->DeepCopy(source->ViewTransform);
  }

  if(source->ProjectionTransform==nullptr)
  {
    if(this->ProjectionTransform!=nullptr)
    {
      this->ProjectionTransform->UnRegister(this);
      this->ProjectionTransform=nullptr;
    }
  }
  else
  {
    if(this->ProjectionTransform==nullptr)
    {
      this->ProjectionTransform=
        static_cast<vtkPerspectiveTransform *>(
          source->ProjectionTransform->MakeTransform());
    }
    this->ProjectionTransform->DeepCopy(source->ProjectionTransform);
  }

  if(source->Transform==nullptr)
  {
    if(this->Transform!=nullptr)
    {
      this->Transform->UnRegister(this);
      this->Transform=nullptr;
    }
  }
  else
  {
    if(this->Transform==nullptr)
    {
      this->Transform=
        static_cast<vtkPerspectiveTransform *>(
          source->Transform->MakeTransform());
    }
    this->Transform->DeepCopy(source->Transform);
  }

  if(source->CameraLightTransform==nullptr)
  {
    if(this->CameraLightTransform!=nullptr)
    {
      this->CameraLightTransform->UnRegister(this);
      this->CameraLightTransform=nullptr;
    }
  }
  else
  {
    if(this->CameraLightTransform==nullptr)
    {
      this->CameraLightTransform=
        static_cast<vtkTransform *>(
          source->CameraLightTransform->MakeTransform());
    }
    this->CameraLightTransform->DeepCopy(source->CameraLightTransform);
  }

  if(source->ModelViewTransform==nullptr)
  {
    if(this->ModelViewTransform!=nullptr)
    {
      this->ModelViewTransform->UnRegister(this);
      this->ModelViewTransform=nullptr;
    }
  }
  else
  {
    if(this->ModelViewTransform==nullptr)
    {
      this->ModelViewTransform=
        static_cast<vtkTransform *>(
          source->ModelViewTransform->MakeTransform());
    }
    this->ModelViewTransform->DeepCopy(source->ModelViewTransform);
  }

  if(source->ModelTransformMatrix == nullptr)
  {
    if(this->ModelTransformMatrix != nullptr)
    {
      this->ModelTransformMatrix->UnRegister(this);
      this->ModelTransformMatrix = nullptr;
    }
  }
  else
  {
    if(this->ModelTransformMatrix==nullptr)
    {
      this->ModelTransformMatrix=
        static_cast<vtkMatrix4x4 *>(
          source->ModelTransformMatrix->NewInstance());
    }
    this->ModelTransformMatrix->DeepCopy(source->ModelTransformMatrix);
  }

  if(source->EyeTransformMatrix == nullptr)
  {
    if(this->EyeTransformMatrix != nullptr)
    {
      this->EyeTransformMatrix->UnRegister(this);
      this->EyeTransformMatrix = nullptr;
    }
  }
  else
  {
    if(this->EyeTransformMatrix==nullptr)
    {
      this->EyeTransformMatrix=
        static_cast<vtkMatrix4x4 *>(
          source->EyeTransformMatrix->NewInstance());
    }
    this->EyeTransformMatrix->DeepCopy(source->EyeTransformMatrix);
  }

  if(source->WorldToScreenMatrix == nullptr)
  {
    if(this->WorldToScreenMatrix != nullptr)
    {
      this->WorldToScreenMatrix->UnRegister(this);
      this->WorldToScreenMatrix = nullptr;
    }
  }
  else
  {
    if(this->WorldToScreenMatrix==nullptr)
    {
      this->WorldToScreenMatrix=
        static_cast<vtkMatrix4x4 *>(
          source->WorldToScreenMatrix->NewInstance());
    }
    this->WorldToScreenMatrix->DeepCopy(source->WorldToScreenMatrix);
  }
}

// ----------------------------------------------------------------------------
// Description:
// Copy the ivars. Do nothing for the matrices.
// Called by ShallowCopy() and DeepCopy()
// \pre source_exists!=0
// \pre not_this: source!=this
void vtkCamera::PartialCopy(vtkCamera *source)
{
  assert("pre: source_exists" && source!=nullptr);
  assert("pre: not_this" && source!=this);

  int i;

  i=0;
  while(i<2)
  {
    this->WindowCenter[i]=source->WindowCenter[i];
    this->ObliqueAngles[i]=source->ObliqueAngles[i];
    this->ClippingRange[i]=source->ClippingRange[i];
    ++i;
  }
  i=0;
  while(i<3)
  {
    this->FocalPoint[i]=source->FocalPoint[i];
    this->Position[i]=source->Position[i];
    this->ViewUp[i]=source->ViewUp[i];
    this->DirectionOfProjection[i]=source->DirectionOfProjection[i];
    this->ViewPlaneNormal[i]=source->ViewPlaneNormal[i];
    this->ViewShear[i]=source->ViewShear[i];

    this->ScreenBottomLeft[i]=source->ScreenBottomLeft[i];
    this->ScreenBottomRight[i]=source->ScreenBottomRight[i];
    this->ScreenTopRight[i]=source->ScreenTopRight[i];
    ++i;
  }

  this->ViewAngle=source->ViewAngle;
  this->EyeAngle=source->EyeAngle;
  this->ParallelProjection=source->ParallelProjection;
  this->ParallelScale=source->ParallelScale;
  this->Stereo=source->Stereo;
  this->LeftEye=source->LeftEye;
  this->Thickness=source->Thickness;
  this->Distance=source->Distance;
  this->UseHorizontalViewAngle=source->UseHorizontalViewAngle;
  this->UseOffAxisProjection=source->UseOffAxisProjection;

  this->FocalDisk=source->FocalDisk;
  this->FocalDistance=source->FocalDistance;
  this->EyeSeparation=source->EyeSeparation;
  this->WorldToScreenMatrixMTime=source->WorldToScreenMatrixMTime;

  this->ViewingRaysMTime=source->ViewingRaysMTime;
}

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

  os << indent << "ClippingRange: (" << this->ClippingRange[0] << ", "
     << this->ClippingRange[1] << ")\n";
  os << indent << "DirectionOfProjection: (" << this->DirectionOfProjection[0]
     << ", " << this->DirectionOfProjection[1]
     << ", " << this->DirectionOfProjection[2] << ")\n";
  os << indent << "Distance: " << this->Distance << "\n";
  os << indent << "EyeAngle: " << this->EyeAngle << "\n";
  os << indent << "FocalDisk: " << this->FocalDisk << "\n";
  os << indent << "FocalDistance: " << this->FocalDistance << "\n";
  os << indent << "FocalPoint: (" << this->FocalPoint[0] << ", "
     << this->FocalPoint[1] << ", " << this->FocalPoint[2] << ")\n";
  os << indent << "ViewShear: (" << this->ViewShear[0]
     << ", " << this->ViewShear[1]
     << ", " << this->ViewShear[2] << ")\n";
  os << indent << "ParallelProjection: " <<
    (this->ParallelProjection ? "On\n" : "Off\n");
  os << indent << "ParallelScale: " << this->ParallelScale << "\n";
  os << indent << "Position: (" << this->Position[0] << ", "
     << this->Position[1] << ", " << this->Position[2] << ")\n";
  os << indent << "Stereo: " << (this->Stereo ? "On\n" : "Off\n");
  os << indent << "Left Eye: " << this->LeftEye << endl;
  os << indent << "Thickness: " << this->Thickness << "\n";
  os << indent << "ViewAngle: " << this->ViewAngle << "\n";
  os << indent << "UseHorizontalViewAngle: " << this->UseHorizontalViewAngle
     << "\n";
  os << indent << "UserTransform: ";
  if (this->UserTransform)
  {
    os << this->UserTransform << "\n";
  }
  else
  {
    os << "(none)\n";
  }
  if (this->UserViewTransform)
  {
    os << this->UserViewTransform << "\n";
  }
  else
  {
    os << "(none)\n";
  }
  os << indent << "FreezeFocalPoint: ";
  if( this->FreezeFocalPoint )
  {
    os << this->FreezeFocalPoint << "\n";
  }
  else
  {
    os << "(none)\n";
  }
  os << indent << "ViewPlaneNormal: (" << this->ViewPlaneNormal[0]
     << ", " << this->ViewPlaneNormal[1]
     << ", " << this->ViewPlaneNormal[2] << ")\n";
  os << indent << "ViewUp: (" << this->ViewUp[0] << ", "
     << this->ViewUp[1] << ", " << this->ViewUp[2] << ")\n";
  os << indent << "WindowCenter: (" << this->WindowCenter[0] << ", "
     << this->WindowCenter[1] << ")\n";

  os << indent << "UseOffAxisProjection: (" << this->UseOffAxisProjection
     << ")\n";

  os << indent << "ScreenBottomLeft: (" << this->ScreenBottomLeft[0]
     << ", " << this->ScreenBottomLeft[1] << ", " << this->ScreenBottomLeft[2]
     << ")\n";

  os << indent << "ScreenBottomRight: (" << this->ScreenBottomRight[0]
     << ", " << this->ScreenBottomRight[1] << ", " << this->ScreenBottomRight[2]
     << ")\n";

  os << indent << "ScreenTopRight: (" << this->ScreenTopRight[0]
     << ", " << this->ScreenTopRight[1] << ", " << this->ScreenTopRight[2]
     << ")\n";

  os << indent << "EyeSeparation: (" << this->EyeSeparation
     << ")\n";

  os << indent << "WorldToScreenMatrix: (" << this->WorldToScreenMatrix << "\n";
  this->WorldToScreenMatrix->PrintSelf(os, indent.GetNextIndent());
  os << indent << ")\n";

  os << indent << "EyeTransformMatrix: (" << this->EyeTransformMatrix << "\n";
  this->EyeTransformMatrix->PrintSelf(os, indent.GetNextIndent());
  os << indent << ")\n";

  os << indent << "ModelTransformMatrix: (" << this->ModelTransformMatrix << "\n";
  this->ModelTransformMatrix->PrintSelf(os, indent.GetNextIndent());
  os << indent << ")\n";

  os << indent << "ProjectionTransform: (" << this->ProjectionTransform << "\n";
  this->ProjectionTransform->PrintSelf(os, indent.GetNextIndent());
  os << indent << ")\n";
}

//-----------------------------------------------------------------------------
void vtkCamera::SetEyePosition(double eyePosition[3])
{
  if(!eyePosition)
  {
    vtkErrorMacro(<< "ERROR: Invalid or nullptr eye position\n");
    return;
  }

  this->EyeTransformMatrix->SetElement(0, 3, eyePosition[0]);
  this->EyeTransformMatrix->SetElement(1, 3, eyePosition[1]);
  this->EyeTransformMatrix->SetElement(2, 3, eyePosition[2]);

  this->Modified();
}

//-----------------------------------------------------------------------------
void vtkCamera::GetEyePosition(double eyePosition[3])
{
  if(!eyePosition)
  {
    vtkErrorMacro(<< "ERROR: Invalid or nullptr eye position\n");
    return;
  }

  eyePosition[0] = this->EyeTransformMatrix->GetElement(0, 3);
  eyePosition[1] = this->EyeTransformMatrix->GetElement(1, 3);
  eyePosition[2] = this->EyeTransformMatrix->GetElement(2, 3);
}

//-----------------------------------------------------------------------------
void vtkCamera::GetEyePlaneNormal(double normal[3])
{
  if(!normal)
  {
    vtkErrorMacro(<< "ERROR: Invalid or nullptr normal\n");
    return;
  }

  // Homogeneous normal.
  double localNormal[4];

  // Get the normal from the screen orientation.
  localNormal[0] = this->WorldToScreenMatrix->GetElement(2, 0);
  localNormal[1] = this->WorldToScreenMatrix->GetElement(2, 1);
  localNormal[2] = this->WorldToScreenMatrix->GetElement(2, 2);
  localNormal[3] = 0.0;

  // Just to be sure.
  vtkMath::Normalize(localNormal);

  normal[0] = localNormal[0];
  normal[1] = localNormal[1];
  normal[2] = localNormal[2];
}

//-----------------------------------------------------------------------------
vtkMatrix4x4 *vtkCamera::GetModelViewTransformMatrix()
{
  this->ComputeModelViewMatrix();

  return this->ModelViewTransform->GetMatrix();
}

//-----------------------------------------------------------------------------
vtkTransform *vtkCamera::GetModelViewTransformObject()
{
  this->ComputeModelViewMatrix();

  return this->ModelViewTransform;
}

//-----------------------------------------------------------------------------
vtkMatrix4x4 *vtkCamera::GetViewTransformMatrix()
{
  return this->GetModelViewTransformMatrix();
}

//-----------------------------------------------------------------------------
vtkTransform *vtkCamera::GetViewTransformObject()
{
  return this->GetModelViewTransformObject();
}

//-----------------------------------------------------------------------------
double *vtkCamera::GetOrientation()
{ return this->ViewTransform->GetOrientation(); };

//-----------------------------------------------------------------------------
double *vtkCamera::GetOrientationWXYZ()
{ return this->ViewTransform->GetOrientationWXYZ(); };

// ----------------------------------------------------------------------------
void vtkCamera::SetEyeTransformMatrix(const double elements[16])
{
  this->EyeTransformMatrix->Element[0][0] = elements[0];
  this->EyeTransformMatrix->Element[0][1] = elements[1];
  this->EyeTransformMatrix->Element[0][2] = elements[2];
  this->EyeTransformMatrix->Element[0][3] = elements[3];

  this->EyeTransformMatrix->Element[1][0] = elements[4];
  this->EyeTransformMatrix->Element[1][1] = elements[5];
  this->EyeTransformMatrix->Element[1][2] = elements[6];
  this->EyeTransformMatrix->Element[1][3] = elements[7];

  this->EyeTransformMatrix->Element[2][0] = elements[8];
  this->EyeTransformMatrix->Element[2][1] = elements[9];
  this->EyeTransformMatrix->Element[2][2] = elements[10];
  this->EyeTransformMatrix->Element[2][3] = elements[11];

  this->EyeTransformMatrix->Element[3][0] = elements[12];
  this->EyeTransformMatrix->Element[3][1] = elements[13];
  this->EyeTransformMatrix->Element[3][2] = elements[14];
  this->EyeTransformMatrix->Element[3][3] = elements[15];
  this->Modified();
}

// ----------------------------------------------------------------------------
void vtkCamera::SetModelTransformMatrix(const double elements[16])
{
  this->ModelTransformMatrix->Element[0][0] = elements[0];
  this->ModelTransformMatrix->Element[0][1] = elements[1];
  this->ModelTransformMatrix->Element[0][2] = elements[2];
  this->ModelTransformMatrix->Element[0][3] = elements[3];

  this->ModelTransformMatrix->Element[1][0] = elements[4];
  this->ModelTransformMatrix->Element[1][1] = elements[5];
  this->ModelTransformMatrix->Element[1][2] = elements[6];
  this->ModelTransformMatrix->Element[1][3] = elements[7];

  this->ModelTransformMatrix->Element[2][0] = elements[8];
  this->ModelTransformMatrix->Element[2][1] = elements[9];
  this->ModelTransformMatrix->Element[2][2] = elements[10];
  this->ModelTransformMatrix->Element[2][3] = elements[11];

  this->ModelTransformMatrix->Element[3][0] = elements[12];
  this->ModelTransformMatrix->Element[3][1] = elements[13];
  this->ModelTransformMatrix->Element[3][2] = elements[14];
  this->ModelTransformMatrix->Element[3][3] = elements[15];
  this->Modified();
}
