// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
/**
 * @class   vtkEllipsoidalGaussianKernel
 * @brief   an ellipsoidal Gaussian interpolation kernel
 *
 *
 * vtkEllipsoidalGaussianKernel is an interpolation kernel that returns the
 * weights for all points found in the ellipsoid defined by radius R in
 * combination with local data (normals and/or scalars). For example, "pancake"
 * weightings (the local normal parallel to the minimum ellisoidal axis); or
 * "needle" weightings (the local normal parallel to the maximum ellipsoidal
 * axis) are possible. (Note that spherical Gaussian weightings are more
 * efficiently computed using vtkGaussianKernel.)
 *
 * The ellipsoidal Gaussian can be described by:
 *
 *     W(x) = S * exp( -( Sharpness^2 * ((rxy/E)**2 + z**2)/R**2) )
 *
 * where S is the local scalar value; E is a user-defined eccentricity factor
 * that controls the elliptical shape of the splat; z is the distance of the
 * current voxel sample point along the local normal N; and rxy is the
 * distance to neighbor point x in the direction perpendicular to N.
 *
 * @warning
 * The weights are normalized so that SUM(Wi) = 1. If a neighbor point p
 * precisely lies on the point to be interpolated, then the interpolated
 * point takes on the values associated with p.
 *
 * @sa
 * vtkPointInterpolator vtkInterpolationKernel vtkGeneralizedKernel
 * vtkGaussianKernel vtkVoronoiKernel vtkSPHKernel vtkShepardKernel
 */

#ifndef vtkEllipsoidalGaussianKernel_h
#define vtkEllipsoidalGaussianKernel_h

#include "vtkFiltersPointsModule.h" // For export macro
#include "vtkGeneralizedKernel.h"
#include "vtkStdString.h" // For vtkStdString ivars

VTK_ABI_NAMESPACE_BEGIN
class vtkIdList;
class vtkDataArray;
class vtkDoubleArray;

class VTKFILTERSPOINTS_EXPORT vtkEllipsoidalGaussianKernel : public vtkGeneralizedKernel
{
public:
  ///@{
  /**
   * Standard methods for instantiation, obtaining type information, and printing.
   */
  static vtkEllipsoidalGaussianKernel* New();
  vtkTypeMacro(vtkEllipsoidalGaussianKernel, vtkGeneralizedKernel);
  void PrintSelf(ostream& os, vtkIndent indent) override;
  ///@}

  /**
   * Initialize the kernel. Overload the superclass to set up scalars and
   * vectors.
   */
  void Initialize(vtkAbstractPointLocator* loc, vtkDataSet* ds, vtkPointData* pd) override;

  // Re-use any superclass signatures that we don't override.
  using vtkGeneralizedKernel::ComputeWeights;

  /**
   * Given a point x, a list of basis points pIds, and a probability
   * weighting function prob, compute interpolation weights associated with
   * these basis points.  Note that basis points list pIds, the probability
   * weighting prob, and the weights array are provided by the caller of the
   * method, and may be dynamically resized as necessary. The method returns
   * the number of weights (pIds may be resized in some cases). Typically
   * this method is called after ComputeBasis(), although advanced users can
   * invoke ComputeWeights() and provide the interpolation basis points pIds
   * directly. The probably weighting prob are numbers 0<=prob<=1 which are
   * multiplied against the interpolation weights before normalization. They
   * are estimates of local confidence of weights. The prob may be nullptr in
   * which all probabilities are considered =1.
   */
  vtkIdType ComputeWeights(
    double x[3], vtkIdList* pIds, vtkDoubleArray* prob, vtkDoubleArray* weights) override;

  ///@{
  /**
   * Specify whether vector values should be used to affect the shape
   * of the Gaussian distribution. By default this is on.
   */
  vtkSetMacro(UseNormals, bool);
  vtkGetMacro(UseNormals, bool);
  vtkBooleanMacro(UseNormals, bool);
  ///@}

  ///@{
  /**
   * Specify the normals array name. Used to orient the ellipsoid. Note that
   * by default the input normals are used (i.e. the input to
   * vtkPointInterpolator). If no input normals are available, then the named
   * NormalsArrayName is used.
   */
  vtkSetMacro(NormalsArrayName, vtkStdString);
  vtkGetMacro(NormalsArrayName, vtkStdString);
  ///@}

  ///@{
  /**
   * Specify whether scalar values should be used to scale the weights.
   * By default this is off.
   */
  vtkSetMacro(UseScalars, bool);
  vtkGetMacro(UseScalars, bool);
  vtkBooleanMacro(UseScalars, bool);
  ///@}

  ///@{
  /**
   * Specify the scalars array name. Used to scale the ellipsoid. Note that
   * by default the input scalars are used (i.e. the input to
   * vtkPointInterpolator). If no input scalars are available, then the named
   * ScalarsArrayName is used.
   */
  vtkSetMacro(ScalarsArrayName, vtkStdString);
  vtkGetMacro(ScalarsArrayName, vtkStdString);
  ///@}

  ///@{
  /**
   * Multiply the Gaussian splat distribution by this value. If UseScalars is
   * on and a scalar array is provided, then the scalar value will be
   * multiplied by the ScaleFactor times the Gaussian function.
   */
  vtkSetClampMacro(ScaleFactor, double, 0.0, VTK_DOUBLE_MAX);
  vtkGetMacro(ScaleFactor, double);
  ///@}

  ///@{
  /**
   * Set / Get the sharpness (i.e., falloff) of the Gaussian. By default
   * Sharpness=2. As the sharpness increases the effects of distant points
   * are reduced.
   */
  vtkSetClampMacro(Sharpness, double, 1, VTK_FLOAT_MAX);
  vtkGetMacro(Sharpness, double);
  ///@}

  ///@{
  /**
   * Set / Get the eccentricity of the ellipsoidal Gaussian. A value=1.0
   * produces a spherical distribution. Values < 1 produce a needle like
   * distribution (in the direction of the normal); values > 1 produce a
   * pancake like distribution (orthogonal to the normal).
   */
  vtkSetClampMacro(Eccentricity, double, 0.000001, VTK_FLOAT_MAX);
  vtkGetMacro(Eccentricity, double);
  ///@}

protected:
  vtkEllipsoidalGaussianKernel();
  ~vtkEllipsoidalGaussianKernel() override;

  bool UseNormals;
  bool UseScalars;

  vtkStdString NormalsArrayName;
  vtkStdString ScalarsArrayName;

  double ScaleFactor;
  double Sharpness;
  double Eccentricity;

  // Internal structure to reduce computation
  double F2, E2;
  vtkDataArray* NormalsArray;
  vtkDataArray* ScalarsArray;

  void FreeStructures() override;

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

VTK_ABI_NAMESPACE_END
#endif
