// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
/**
 * @class   vtkTransformInterpolator
 * @brief   interpolate a series of transformation matrices
 *
 * This class is used to interpolate a series of 4x4 transformation
 * matrices. Position, scale and orientation (i.e., rotations) are
 * interpolated separately, and can be interpolated linearly or with a spline
 * function. Note that orientation is interpolated using quaternions via
 * SLERP (spherical linear interpolation) or the special vtkQuaternionSpline
 * class.
 *
 * To use this class, specify at least two pairs of (t,transformation matrix)
 * with the AddTransform() method.  Then interpolated the transforms with the
 * InterpolateTransform(t,transform) method, where "t" must be in the range
 * of (min,max) times specified by the AddTransform() method.
 *
 * By default, spline interpolation is used for the interpolation of the
 * transformation matrices. The position, scale and orientation of the
 * matrices are interpolated with instances of the classes
 * vtkTupleInterpolator (position,scale) and vtkQuaternionInterpolator
 * (rotation). The user can override the interpolation behavior by gaining
 * access to these separate interpolation classes.  These interpolator
 * classes (vtkTupleInterpolator and vtkQuaternionInterpolator) can be
 * modified to perform linear versus spline interpolation, and/or different
 * spline basis functions can be specified.
 *
 * @warning
 * The interpolator classes are initialized when the InterpolateTransform()
 * is called. Any changes to the interpolators, or additions to the list of
 * transforms to be interpolated, causes a reinitialization of the
 * interpolators the next time InterpolateTransform() is invoked. Thus the
 * best performance is obtained by 1) configuring the interpolators, 2) adding
 * all the transforms, and 3) finally performing interpolation.
 */

#ifndef vtkTransformInterpolator_h
#define vtkTransformInterpolator_h

#include "vtkObject.h"
#include "vtkRenderingCoreModule.h" // For export macro

VTK_ABI_NAMESPACE_BEGIN
class vtkTransform;
class vtkMatrix4x4;
class vtkProp3D;
class vtkTupleInterpolator;
class vtkQuaternionInterpolator;
class vtkTransformList;

class VTKRENDERINGCORE_EXPORT vtkTransformInterpolator : public vtkObject
{
public:
  vtkTypeMacro(vtkTransformInterpolator, vtkObject);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  /**
   * Instantiate the class.
   */
  static vtkTransformInterpolator* New();

  /**
   * Return the number of transforms in the list of transforms.
   */
  int GetNumberOfTransforms();

  ///@{
  /**
   * Obtain some information about the interpolation range. The numbers
   * returned (corresponding to parameter t, usually thought of as time)
   * are undefined if the list of transforms is empty.
   */
  double GetMinimumT();
  double GetMaximumT();
  ///@}

  /**
   * Clear the list of transforms.
   */
  void Initialize();

  ///@{
  /**
   * Add another transform to the list of transformations defining
   * the transform function. Note that using the same time t value
   * more than once replaces the previous transform value at t.
   * At least two transforms must be added to define a function.
   * There are variants to this method depending on whether you are
   * adding a vtkTransform, vtkMaxtirx4x4, and/or vtkProp3D.
   */
  void AddTransform(double t, vtkTransform* xform);
  void AddTransform(double t, vtkMatrix4x4* matrix);
  void AddTransform(double t, vtkProp3D* prop3D);
  ///@}

  /**
   * Delete the transform at a particular parameter t. If there is no
   * transform defined at location t, then the method does nothing.
   */
  void RemoveTransform(double t);

  /**
   * Interpolate the list of transforms and determine a new transform (i.e.,
   * fill in the transformation provided). If t is outside the range of
   * (min,max) values, then t is clamped.
   */
  void InterpolateTransform(double t, vtkTransform* xform);

  /**
   * Enums to control the type of interpolation to use.
   */
  enum
  {
    INTERPOLATION_TYPE_LINEAR = 0,
    INTERPOLATION_TYPE_SPLINE,
    INTERPOLATION_TYPE_MANUAL
  };

  ///@{
  /**
   * These are convenience methods to switch between linear and spline
   * interpolation. The methods simply forward the request for linear or
   * spline interpolation to the position, scale and orientation
   * interpolators. Note that if the InterpolationType is set to "Manual",
   * then the interpolators are expected to be directly manipulated and
   * this class does not forward the request for interpolation type to its
   * interpolators.
   */
  vtkSetClampMacro(InterpolationType, int, INTERPOLATION_TYPE_LINEAR, INTERPOLATION_TYPE_MANUAL);
  vtkGetMacro(InterpolationType, int);
  void SetInterpolationTypeToLinear() { this->SetInterpolationType(INTERPOLATION_TYPE_LINEAR); }
  void SetInterpolationTypeToSpline() { this->SetInterpolationType(INTERPOLATION_TYPE_SPLINE); }
  void SetInterpolationTypeToManual() { this->SetInterpolationType(INTERPOLATION_TYPE_MANUAL); }
  ///@}

  ///@{
  /**
   * Set/Get the tuple interpolator used to interpolate the position portion
   * of the transformation matrix. Note that you can modify the behavior of
   * the interpolator (linear vs spline interpolation; change spline basis)
   * by manipulating the interpolator instances.
   */
  virtual void SetPositionInterpolator(vtkTupleInterpolator*);
  vtkGetObjectMacro(PositionInterpolator, vtkTupleInterpolator);
  ///@}

  ///@{
  /**
   * Set/Get the tuple interpolator used to interpolate the scale portion
   * of the transformation matrix. Note that you can modify the behavior of
   * the interpolator (linear vs spline interpolation; change spline basis)
   * by manipulating the interpolator instances.
   */
  virtual void SetScaleInterpolator(vtkTupleInterpolator*);
  vtkGetObjectMacro(ScaleInterpolator, vtkTupleInterpolator);
  ///@}

  ///@{
  /**
   * Set/Get the tuple interpolator used to interpolate the orientation portion
   * of the transformation matrix. Note that you can modify the behavior of
   * the interpolator (linear vs spline interpolation; change spline basis)
   * by manipulating the interpolator instances.
   */
  virtual void SetRotationInterpolator(vtkQuaternionInterpolator*);
  vtkGetObjectMacro(RotationInterpolator, vtkQuaternionInterpolator);
  ///@}

  /**
   * Override GetMTime() because we depend on the interpolators which may be
   * modified outside of this class.
   */
  vtkMTimeType GetMTime() override;

protected:
  vtkTransformInterpolator();
  ~vtkTransformInterpolator() override;

  // Control the interpolation type
  int InterpolationType;

  // Interpolators
  vtkTupleInterpolator* PositionInterpolator;
  vtkTupleInterpolator* ScaleInterpolator;
  vtkQuaternionInterpolator* RotationInterpolator;

  // Initialize the interpolating splines
  int Initialized;
  vtkTimeStamp InitializeTime;
  void InitializeInterpolation();

  // Keep track of inserted data
  vtkTransformList* TransformList;

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

VTK_ABI_NAMESPACE_END
#endif
