// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
/**
 * @class   vtkParametricFunctionSource
 * @brief   tessellate parametric functions
 *
 * This class tessellates parametric functions. The user must specify how
 * many points in the parametric coordinate directions are required (i.e.,
 * the resolution), and the mode to use to generate scalars.
 *
 * @par Thanks:
 * Andrew Maclean andrew.amaclean@gmail.com for creating and contributing
 * the class.
 *
 * @sa
 * vtkParametricFunction
 *
 * @sa
 * Implementation of parametrics for 1D lines:
 * vtkParametricSpline
 *
 * @sa
 * Subclasses of vtkParametricFunction implementing non-orentable surfaces:
 * vtkParametricBoy vtkParametricCrossCap vtkParametricFigure8Klein
 * vtkParametricKlein vtkParametricMobius vtkParametricRoman
 *
 * @sa
 * Subclasses of vtkParametricFunction implementing orientable surfaces:
 * vtkParametricConicSpiral vtkParametricDini vtkParametricEllipsoid
 * vtkParametricEnneper vtkParametricRandomHills vtkParametricSuperEllipsoid
 * vtkParametricSuperToroid vtkParametricTorus
 *
 */

#ifndef vtkParametricFunctionSource_h
#define vtkParametricFunctionSource_h

#include "vtkFiltersSourcesModule.h" // For export macro
#include "vtkPolyDataAlgorithm.h"

VTK_ABI_NAMESPACE_BEGIN
class vtkCellArray;
class vtkParametricFunction;

class VTKFILTERSSOURCES_EXPORT vtkParametricFunctionSource : public vtkPolyDataAlgorithm
{
public:
  vtkTypeMacro(vtkParametricFunctionSource, vtkPolyDataAlgorithm);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  /**
   * Create a new instance with (50,50,50) points in the (u-v-w) directions.
   */
  static vtkParametricFunctionSource* New();

  ///@{
  /**
   * Specify the parametric function to use to generate the tessellation.
   */
  virtual void SetParametricFunction(vtkParametricFunction*);
  vtkGetObjectMacro(ParametricFunction, vtkParametricFunction);
  ///@}

  ///@{
  /**
   * Set/Get the number of subdivisions / tessellations in the u parametric
   * direction. Note that the number of tessellant points in the u
   * direction is the UResolution + 1.
   */
  vtkSetClampMacro(UResolution, int, 2, VTK_INT_MAX);
  vtkGetMacro(UResolution, int);
  ///@}

  ///@{
  /**
   * Set/Get the number of subdivisions / tessellations in the v parametric
   * direction. Note that the number of tessellant points in the v
   * direction is the VResolution + 1.
   */
  vtkSetClampMacro(VResolution, int, 2, VTK_INT_MAX);
  vtkGetMacro(VResolution, int);
  ///@}

  ///@{
  /**
   * Set/Get the number of subdivisions / tessellations in the w parametric
   * direction. Note that the number of tessellant points in the w
   * direction is the WResolution + 1.
   */
  vtkSetClampMacro(WResolution, int, 2, VTK_INT_MAX);
  vtkGetMacro(WResolution, int);
  ///@}

  ///@{
  /**
   * Set/Get the generation of texture coordinates. This is off by
   * default.
   * Note that this is only applicable to parametric surfaces
   * whose parametric dimension is 2.
   * Note that texturing may fail in some cases.
   */
  vtkBooleanMacro(GenerateTextureCoordinates, vtkTypeBool);
  vtkSetClampMacro(GenerateTextureCoordinates, vtkTypeBool, 0, 1);
  vtkGetMacro(GenerateTextureCoordinates, vtkTypeBool);
  ///@}

  ///@{
  /**
   * Set/Get the generation of normals. This is on by
   * default.
   * Note that this is only applicable to parametric surfaces
   * whose parametric dimension is 2.
   */
  vtkBooleanMacro(GenerateNormals, vtkTypeBool);
  vtkSetClampMacro(GenerateNormals, vtkTypeBool, 0, 1);
  vtkGetMacro(GenerateNormals, vtkTypeBool);
  ///@}

  /**
   * Enumerate the supported scalar generation modes.<br>
   * SCALAR_NONE - Scalars are not generated (default).<br>
   * SCALAR_U - The scalar is set to the u-value.<br>
   * SCALAR_V - The scalar is set to the v-value.<br>
   * SCALAR_U0 - The scalar is set to 1 if
   * u = (u_max - u_min)/2 = u_avg, 0 otherwise.<br>
   * SCALAR_V0 - The scalar is set to 1 if
   * v = (v_max - v_min)/2 = v_avg, 0 otherwise.<br>
   * SCALAR_U0V0 - The scalar is
   * set to 1 if u == u_avg, 2 if v == v_avg,
   * 3 if u = u_avg && v = v_avg, 0 otherwise.<br>
   * SCALAR_MODULUS - The scalar is set to (sqrt(u*u+v*v)),
   * this is measured relative to (u_avg,v_avg).<br>
   * SCALAR_PHASE - The scalar is set to (atan2(v,u))
   * (in degrees, 0 to 360),
   * this is measured relative to (u_avg,v_avg).<br>
   * SCALAR_QUADRANT - The scalar is set to 1, 2, 3 or 4.
   * depending upon the quadrant of the point (u,v).<br>
   * SCALAR_X - The scalar is set to the x-value.<br>
   * SCALAR_Y - The scalar is set to the y-value.<br>
   * SCALAR_Z - The scalar is set to the z-value.<br>
   * SCALAR_DISTANCE - The scalar is set to (sqrt(x*x+y*y+z*z)).
   * I.e. distance from the origin.<br>
   * SCALAR_USER_DEFINED - The scalar is set to the value
   * returned from EvaluateScalar().<br>
   */
  enum SCALAR_MODE
  {
    SCALAR_NONE = 0,
    SCALAR_U,
    SCALAR_V,
    SCALAR_U0,
    SCALAR_V0,
    SCALAR_U0V0,
    SCALAR_MODULUS,
    SCALAR_PHASE,
    SCALAR_QUADRANT,
    SCALAR_X,
    SCALAR_Y,
    SCALAR_Z,
    SCALAR_DISTANCE,
    SCALAR_FUNCTION_DEFINED
  };

  ///@{
  /**
   * Get/Set the mode used for the scalar data.
   * See SCALAR_MODE for a description of the types of scalars generated.
   */
  vtkSetClampMacro(ScalarMode, int, SCALAR_NONE, SCALAR_FUNCTION_DEFINED);
  vtkGetMacro(ScalarMode, int);
  void SetScalarModeToNone() { this->SetScalarMode(SCALAR_NONE); }
  void SetScalarModeToU() { this->SetScalarMode(SCALAR_U); }
  void SetScalarModeToV() { this->SetScalarMode(SCALAR_V); }
  void SetScalarModeToU0() { this->SetScalarMode(SCALAR_U0); }
  void SetScalarModeToV0() { this->SetScalarMode(SCALAR_V0); }
  void SetScalarModeToU0V0() { this->SetScalarMode(SCALAR_U0V0); }
  void SetScalarModeToModulus() { this->SetScalarMode(SCALAR_MODULUS); }
  void SetScalarModeToPhase() { this->SetScalarMode(SCALAR_PHASE); }
  void SetScalarModeToQuadrant() { this->SetScalarMode(SCALAR_QUADRANT); }
  void SetScalarModeToX() { this->SetScalarMode(SCALAR_X); }
  void SetScalarModeToY() { this->SetScalarMode(SCALAR_Y); }
  void SetScalarModeToZ() { this->SetScalarMode(SCALAR_Z); }
  void SetScalarModeToDistance() { this->SetScalarMode(SCALAR_DISTANCE); }
  void SetScalarModeToFunctionDefined() { this->SetScalarMode(SCALAR_FUNCTION_DEFINED); }
  ///@}

  /**
   * Return the MTime also considering the parametric function.
   */
  vtkMTimeType GetMTime() override;

  ///@{
  /**
   * Set/get the desired precision for the output points.
   * See the documentation for the vtkAlgorithm::Precision enum for an
   * explanation of the available precision settings.
   */
  vtkSetMacro(OutputPointsPrecision, int);
  vtkGetMacro(OutputPointsPrecision, int);
  ///@}

protected:
  vtkParametricFunctionSource();
  ~vtkParametricFunctionSource() override;

  // Usual data generation method
  int RequestData(
    vtkInformation* info, vtkInformationVector** input, vtkInformationVector* output) override;

  // Variables
  vtkParametricFunction* ParametricFunction;

  int UResolution;
  int VResolution;
  int WResolution;
  vtkTypeBool GenerateTextureCoordinates;
  vtkTypeBool GenerateNormals;
  int ScalarMode;
  int OutputPointsPrecision;

private:
  // Create output depending on function dimension
  void Produce1DOutput(vtkInformationVector* output);
  void Produce2DOutput(vtkInformationVector* output);

  /**
   * Generate triangles from an ordered set of points.

   * Given a parametrization f(u,v)->(x,y,z), this function generates
   * a vtkCellAarray of point IDs over the range MinimumU <= u < MaximumU
   * and MinimumV <= v < MaximumV.

   * Before using this function, ensure that: UResolution,
   * VResolution, MinimumU, MaximumU, MinimumV, MaximumV, JoinU, JoinV,
   * TwistU, TwistV, ordering are set appropriately for the parametric function.
   */
  void MakeTriangles(vtkCellArray* strips, int PtsU, int PtsV);

  vtkParametricFunctionSource(const vtkParametricFunctionSource&) = delete;
  void operator=(const vtkParametricFunctionSource&) = delete;
};

VTK_ABI_NAMESPACE_END
#endif
