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

  Program:   Visualization Toolkit
  Module:    vtkWarpTransform.h

  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.

=========================================================================*/
/**
 * @class   vtkWarpTransform
 * @brief   superclass for nonlinear geometric transformations
 *
 * vtkWarpTransform provides a generic interface for nonlinear
 * warp transformations.
 * @sa
 * vtkThinPlateSplineTransform vtkGridTransform vtkGeneralTransform
 */

#ifndef vtkWarpTransform_h
#define vtkWarpTransform_h

#include "vtkAbstractTransform.h"
#include "vtkCommonTransformsModule.h" // For export macro

class VTKCOMMONTRANSFORMS_EXPORT vtkWarpTransform : public vtkAbstractTransform
{
public:
  vtkTypeMacro(vtkWarpTransform, vtkAbstractTransform);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  /**
   * Invert the transformation.  Warp transformations are usually
   * inverted using an iterative technique such as Newton's method.
   * The inverse transform is usually around five or six times as
   * computationally expensive as the forward transform.
   */
  void Inverse() override;

  ///@{
  /**
   * Get the inverse flag of the transformation.  This flag is
   * set to zero when the transformation is first created, and
   * is flipped each time Inverse() is called.
   */
  vtkGetMacro(InverseFlag, int);
  ///@}

  ///@{
  /**
   * Set the tolerance for inverse transformation.
   * The default is 0.001.
   */
  vtkSetMacro(InverseTolerance, double);
  vtkGetMacro(InverseTolerance, double);
  ///@}

  ///@{
  /**
   * Set the maximum number of iterations for the inverse
   * transformation.  The default is 500, but usually only
   * 2 to 5 iterations are used.  The inversion method
   * is fairly robust, and it should converge for nearly all smooth
   * transformations that do not fold back on themselves.
   */
  vtkSetMacro(InverseIterations, int);
  vtkGetMacro(InverseIterations, int);
  ///@}

  ///@{
  /**
   * This will calculate the transformation without calling Update.
   * Meant for use only within other VTK classes.
   */
  void InternalTransformPoint(const float in[3], float out[3]) override;
  void InternalTransformPoint(const double in[3], double out[3]) override;
  ///@}

  ///@{
  /**
   * This will calculate the transformation, as well as its derivative
   * without calling Update.  Meant for use only within other VTK
   * classes.
   */
  void InternalTransformDerivative(
    const float in[3], float out[3], float derivative[3][3]) override;
  void InternalTransformDerivative(
    const double in[3], double out[3], double derivative[3][3]) override;
  ///@}

  ///@{
  /**
   * Do not use these methods.  They exists only as a work-around for
   * internal templated functions (I really didn't want to make the
   * Forward/Inverse methods public, is there a decent work around
   * for this sort of thing?)
   */
  void TemplateTransformPoint(const float in[3], float out[3])
  {
    this->ForwardTransformPoint(in, out);
  }
  void TemplateTransformPoint(const double in[3], double out[3])
  {
    this->ForwardTransformPoint(in, out);
  }
  void TemplateTransformPoint(const float in[3], float out[3], float derivative[3][3])
  {
    this->ForwardTransformDerivative(in, out, derivative);
  }
  void TemplateTransformPoint(const double in[3], double out[3], double derivative[3][3])
  {
    this->ForwardTransformDerivative(in, out, derivative);
  }
  void TemplateTransformInverse(const float in[3], float out[3])
  {
    this->InverseTransformPoint(in, out);
  }
  void TemplateTransformInverse(const double in[3], double out[3])
  {
    this->InverseTransformPoint(in, out);
  }
  void TemplateTransformInverse(const float in[3], float out[3], float derivative[3][3])
  {
    this->InverseTransformDerivative(in, out, derivative);
  }
  void TemplateTransformInverse(const double in[3], double out[3], double derivative[3][3])
  {
    this->InverseTransformDerivative(in, out, derivative);
  }
  ///@}

protected:
  vtkWarpTransform();
  ~vtkWarpTransform() override;

  ///@{
  /**
   * If the InverseFlag is set to 0, then a call to InternalTransformPoint
   * results in a call to ForwardTransformPoint.
   */
  virtual void ForwardTransformPoint(const float in[3], float out[3]) = 0;
  virtual void ForwardTransformPoint(const double in[3], double out[3]) = 0;
  ///@}

  ///@{
  /**
   * Calculate the forward transform as well as the derivative.
   */
  virtual void ForwardTransformDerivative(
    const float in[3], float out[3], float derivative[3][3]) = 0;
  virtual void ForwardTransformDerivative(
    const double in[3], double out[3], double derivative[3][3]) = 0;
  ///@}

  ///@{
  /**
   * If the InverseFlag is set to 1, then a call to InternalTransformPoint
   * results in a call to InverseTransformPoint.  The inverse transformation
   * is calculated from using Newton's method.
   */
  virtual void InverseTransformPoint(const float in[3], float out[3]);
  virtual void InverseTransformPoint(const double in[3], double out[3]);
  ///@}

  ///@{
  /**
   * Calculate the inverse transform as well as the derivative of the
   * forward transform (that's correct: the derivative of the
   * forward transform, not of the inverse transform)
   */
  virtual void InverseTransformDerivative(const float in[3], float out[3], float derivative[3][3]);
  virtual void InverseTransformDerivative(
    const double in[3], double out[3], double derivative[3][3]);
  ///@}

  int InverseFlag;
  int InverseIterations;
  double InverseTolerance;

private:
  vtkWarpTransform(const vtkWarpTransform&) = delete;
  void operator=(const vtkWarpTransform&) = delete;
};

#endif
