// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
/**
 * @class   vtkImageBSplineCoefficients
 * @brief   convert image to b-spline knots
 *
 * vtkImageBSplineCoefficients prepares an image for b-spline
 * interpolation by converting the image values into b-spline
 * knot coefficients.  It is a necessary pre-filtering step
 * before applying b-spline interpolation with vtkImageReslice.
 *
 * This class is based on code provided by Philippe Thevenaz of
 * EPFL, Lausanne, Switzerland.  Please acknowledge his contribution
 * by citing the following paper:
 * [1] P. Thevenaz, T. Blu, M. Unser, "Interpolation Revisited,"
 *     IEEE Transactions on Medical Imaging 19(7):739-758, 2000.
 *
 * The clamped boundary condition (which is the default) is taken
 * from code presented in the following paper:
 * [2] D. Ruijters, P. Thevenaz,
 *     "GPU Prefilter for Accurate Cubic B-spline Interpolation,"
 *     The Computer Journal, doi: 10.1093/comjnl/bxq086, 2010.
 *
 * @par Thanks:
 * This class was written by David Gobbi at the Seaman Family MR Research
 * Centre, Foothills Medical Centre, Calgary, Alberta.
 * DG Gobbi and YP Starreveld,
 * "Uniform B-Splines for the VTK Imaging Pipeline,"
 * VTK Journal, 2011,
 * http://hdl.handle.net/10380/3252
 */

#ifndef vtkImageBSplineCoefficients_h
#define vtkImageBSplineCoefficients_h

#include "vtkImageBSplineInterpolator.h" // for constants
#include "vtkImagingCoreModule.h"        // For export macro
#include "vtkThreadedImageAlgorithm.h"

VTK_ABI_NAMESPACE_BEGIN
class VTKIMAGINGCORE_EXPORT vtkImageBSplineCoefficients : public vtkThreadedImageAlgorithm
{
public:
  static vtkImageBSplineCoefficients* New();
  vtkTypeMacro(vtkImageBSplineCoefficients, vtkThreadedImageAlgorithm);

  void PrintSelf(ostream& os, vtkIndent indent) override;

  ///@{
  /**
   * Set the degree of the spline polynomial.  The default value is 3,
   * and the maximum is 9.
   */
  vtkSetClampMacro(SplineDegree, int, 0, VTK_IMAGE_BSPLINE_DEGREE_MAX);
  vtkGetMacro(SplineDegree, int);
  ///@}

  ///@{
  /**
   * Set the border mode.  The filter that is used to create the
   * coefficients must repeat the image somehow to make a theoretically
   * infinite input.  The default is to clamp values that are off the
   * edge of the image, to the value at the closest point on the edge.
   * The other ways of virtually extending the image are to produce
   * mirrored copies, which results in optimal smoothness at the boundary,
   * or to repeat the image, which results in a cyclic or periodic spline.
   */
  vtkSetClampMacro(BorderMode, vtkImageBorderMode, VTK_IMAGE_BORDER_CLAMP, VTK_IMAGE_BORDER_MIRROR);
  void SetBorderModeToClamp() { this->SetBorderMode(VTK_IMAGE_BORDER_CLAMP); }
  void SetBorderModeToRepeat() { this->SetBorderMode(VTK_IMAGE_BORDER_REPEAT); }
  void SetBorderModeToMirror() { this->SetBorderMode(VTK_IMAGE_BORDER_MIRROR); }
  vtkGetMacro(BorderMode, vtkImageBorderMode);
  const char* GetBorderModeAsString();
  ///@}

  ///@{
  /**
   * Set the scalar type of the output.  Default is float.
   * Floating-point output is used to avoid overflow, since the
   * range of the output values is larger than the input values.
   */
  vtkSetClampMacro(OutputScalarType, int, VTK_FLOAT, VTK_DOUBLE);
  vtkGetMacro(OutputScalarType, int);
  void SetOutputScalarTypeToFloat() { this->SetOutputScalarType(VTK_FLOAT); }
  void SetOutputScalarTypeToDouble() { this->SetOutputScalarType(VTK_DOUBLE); }
  const char* GetOutputScalarTypeAsString();
  ///@}

  ///@{
  /**
   * Bypass the filter, do not do any processing.  If this is on,
   * then the output data will reference the input data directly,
   * and the output type will be the same as the input type.  This
   * is useful a downstream filter sometimes uses b-spline interpolation
   * and sometimes uses other forms of interpolation.
   */
  vtkSetMacro(Bypass, vtkTypeBool);
  vtkBooleanMacro(Bypass, vtkTypeBool);
  vtkGetMacro(Bypass, vtkTypeBool);
  ///@}

  /**
   * Check a point against the image bounds.  Return 0 if out of bounds,
   * and 1 if inside bounds.  Calling Evaluate on a point outside the
   * bounds will not generate an error, but the value returned will
   * depend on the BorderMode.
   */
  int CheckBounds(const double point[3]);

  ///@{
  /**
   * Interpolate a value from the image.  You must call Update() before
   * calling this method for the first time.  The first signature can
   * return multiple components, while the second signature is for use
   * on single-component images.
   */
  void Evaluate(const double point[3], double* value);
  double Evaluate(double x, double y, double z);
  double Evaluate(const double point[3]) { return this->Evaluate(point[0], point[1], point[2]); }
  ///@}

protected:
  vtkImageBSplineCoefficients();
  ~vtkImageBSplineCoefficients() override;

  void AllocateOutputData(vtkImageData* out, vtkInformation* outInfo, int* uExtent) override;
  vtkImageData* AllocateOutputData(vtkDataObject* out, vtkInformation* outInfo) override;

  int RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) override;
  int RequestInformation(vtkInformation*, vtkInformationVector**, vtkInformationVector*) override;
  int RequestUpdateExtent(vtkInformation*, vtkInformationVector**, vtkInformationVector*) override;

  void ThreadedExecute(
    vtkImageData* inData, vtkImageData* outData, int outExt[6], int threadId) override;

  int SplineDegree;
  vtkImageBorderMode BorderMode;
  int OutputScalarType;
  vtkTypeBool Bypass;
  int DataWasPassed;
  int Iteration;

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

VTK_ABI_NAMESPACE_END
#endif
