// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
/**
 * @class   vtkSurfaceLICInterface
 * @brief   public API for surface lic parameters
 *  arbitrary geometry.
 *
 *
 *  vtkSurfaceLICInterface performs LIC on the surface of arbitrary
 *  geometry. Point vectors are used as the vector field for generating the LIC.
 *  The implementation was originallu  based on "Image Space Based Visualization
 *  on Unsteady Flow on Surfaces" by Laramee, Jobard and Hauser appeared in
 *  proceedings of IEEE Visualization '03, pages 131-138.
 *
 *  Internal pipeline:
 * <pre>
 * noise
 *     |
 *     [ PROJ (GAT) (COMP) LIC2D (SCAT) SHADE (CCE) DEP]
 *     |                                               |
 * vectors                                         surface LIC
 * </pre>
 * PROj  - project vectors onto surface
 * GAT   - gather data for compositing and guard pixel generation  (parallel only)
 * COMP  - composite gathered data
 * LIC2D - line intengral convolution, see vtkLineIntegralConvolution2D.
 * SCAT  - scatter result (parallel only, not all compositors use it)
 * SHADE - combine LIC and scalar colors
 * CCE   - color contrast enhancement (optional)
 * DEP   - depth test and copy to back buffer
 *
 * The result of each stage is cached in a texture so that during interaction
 * a stage may be skipped if the user has not modified its parameters or input
 * data.
 *
 * The parallel parts of algorithm are implemented in vtkPSurfaceLICInterface.
 * Note that for MPI enabled builds this class will be automatically created
 * by the object factory.
 *
 * @sa
 * vtkLineIntegralConvolution2D
 */

#ifndef vtkSurfaceLICInterface_h
#define vtkSurfaceLICInterface_h

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

VTK_ABI_NAMESPACE_BEGIN
class vtkRenderWindow;
class vtkRenderer;
class vtkActor;
class vtkImageData;
class vtkDataObject;
class vtkDataArray;
class vtkPainterCommunicator;
class vtkSurfaceLICHelper;
class vtkWindow;

class VTKRENDERINGLICOPENGL2_EXPORT vtkSurfaceLICInterface : public vtkObject
{
public:
  static vtkSurfaceLICInterface* New();
  vtkTypeMacro(vtkSurfaceLICInterface, vtkObject);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  ///@{
  /**
   * Get/Set the number of integration steps in each direction.
   */
  void SetNumberOfSteps(int val);
  vtkGetMacro(NumberOfSteps, int);
  ///@}

  ///@{
  /**
   * Get/Set the step size (in pixels).
   */
  void SetStepSize(double val);
  vtkGetMacro(StepSize, double);
  ///@}

  ///@{
  /**
   * Normalize vectors during integration. When set(the default) the
   * input vector field is normalized during integration, and each
   * integration occurs over the same arclength. When not set each
   * integration occurs over an arc length proportional to the field
   * magnitude as is customary in traditional numerical methods. See,
   * "Imaging Vector Fields Using Line Integral Convolution" for an
   * axample where normalization is used. See, "Image Space Based
   * Visualization of Unsteady Flow on Surfaces" for an example
   * of where no normalization is used.
   */
  void SetNormalizeVectors(int val);
  vtkBooleanMacro(NormalizeVectors, int);
  vtkGetMacro(NormalizeVectors, int);
  ///@}

  ///@{
  /**
   * When set MaskOnSurface computes |V| for use in the fragment masking
   * tests on the surface. When not set the original un-projected
   * un-transformed |V| is used.
   */
  void SetMaskOnSurface(int val);
  vtkBooleanMacro(MaskOnSurface, int);
  vtkGetMacro(MaskOnSurface, int);
  ///@}

  ///@{
  /**
   * The MaskThreshold controls the rendering of fragments in stagnant
   * regions of flow.  // In these regions LIC noise texture will be masked,
   * where |V| < MaskThreshold is satisfied. The masking process blends a
   * the MaskColor with the scalar color of the surface proportional to
   * MaskIntesnsity. See MaskIntensity for more information on the blending
   * algorithm. This blending allows one control over the masking process
   * so that masked fragments may be: highlighted (by setting a unique
   * mask color and mask intensity > 0), made invisible with and without
   * passing the un-convolved noise texture (by setting mask intensity 0),
   * made to blend into the LIC.

   * MaskThreshold units are in the original vector space. Note that the
   * threshold can be applied to the original vector field or to the surface
   * projected vector field. See MaskOnSurface.
   */
  void SetMaskThreshold(double val);
  vtkGetMacro(MaskThreshold, double);
  ///@}

  ///@{
  /**
   * The MaskColor is used on masked fragments. The default of (0.5, 0.5, 0.5)
   * makes the masked fragments look similar to the LIC'd fragments. The mask
   * color is applied only when MaskIntensity > 0.
   */
  void SetMaskColor(double* val);
  void SetMaskColor(double r, double g, double b)
  {
    double rgb[3] = { r, g, b };
    this->SetMaskColor(rgb);
  }
  vtkGetVector3Macro(MaskColor, double);
  ///@}

  ///@{
  /**
   * The MaskIntensity controls the blending of the mask color and the geometry
   * color. The color of masked fragments is given by:

   * c = maskColor * maskIntensity + geomColor * (1 - maskIntensity)

   * The default value of 0.0 results in the geometry color being used.
   */
  void SetMaskIntensity(double val);
  vtkGetMacro(MaskIntensity, double);
  ///@}

  ///@{
  /**
   * EnhancedLIC mean compute the LIC twice with the second pass using
   * the edge-enhanced result of the first pass as a noise texture. Edge
   * enhancedment is made by a simple Laplace convolution.
   */
  void SetEnhancedLIC(int val);
  vtkGetMacro(EnhancedLIC, int);
  vtkBooleanMacro(EnhancedLIC, int);
  ///@}

  ///@{
  /**
   * Enable/Disable contrast and dynamic range correction stages. Contrast
   * enhancement can be enabled during LIC computations (See
   * vtkLineINtegralComvolution2D) and after the scalar colors have been
   * combined with the LIC.

   * The best approach for using this feature is to enable LIC enhancement,
   * and only if the image is to dark or dull enable COLOR enhancement.

   * Both stages are implemented by a histogram stretching algorithm. During
   * LIC stages the contrast enhancement is applied to gray scale LIC image.
   * During the scalar coloring stage the contrast enhancement is applied to
   * the lightness channel of the color image in HSL color space. The
   * histogram stretching is implemented as follows:

   * L = (L-m)/(M-m)

   * where, L is the fragment intensity/lightness, m is the intensity/lightness
   * to map to 0, M is the intensity/lightness to map to 1. The default values
   * of m and M are the min and max taken over all fragments.

   * This increase the dynamic range and contrast in the LIC'd image, both of
   * which are natuarly attenuated by the convolution process.

   * Values

   * ENHANCE_CONTRAST_OFF   -- don't enhance LIC or scalar colors
   * ENHANCE_CONTRAST_LIC   -- enhance in LIC high-pass input and output
   * ENHANCE_CONTRAST_COLOR -- enhance after scalars are combined with LIC
   * ENHANCE_CONTRAST_BOTH  -- enhance in LIC stages and after scalar colors

   * This feature is disabled by default.
   */
  enum
  {
    ENHANCE_CONTRAST_OFF = 0,
    ENHANCE_CONTRAST_LIC = 1,
    ENHANCE_CONTRAST_COLOR = 3,
    ENHANCE_CONTRAST_BOTH = 4
  };
  void SetEnhanceContrast(int val);
  vtkGetMacro(EnhanceContrast, int);
  ///@}

  ///@{
  /**
   * This feature is used to fine tune the contrast enhancement. There are two
   * modes AUTOMATIC and MANUAL.In AUTOMATIC mode values are provided indicating
   * the fraction of the range to adjust M and m by, during contrast enahncement
   * histogram stretching. M and m are the intensity/lightness values that map
   * to 1 and 0. (see EnhanceContrast for an explanation of the mapping
   * procedure). m and M are computed using the factors as follows:

   * m = min(C) + mFactor * (max(C) - min(C))
   * M = max(C) - MFactor * (max(C) - min(C))

   * the default values for mFactor and MFactor are 0 which result in
   * m = min(C), M = max(C), taken over the entire image. Modifying mFactor and
   * MFactor above or below zero provide control over the saturation/
   * de-saturation during contrast enhancement.
   */
  vtkGetMacro(LowLICContrastEnhancementFactor, double);
  vtkGetMacro(HighLICContrastEnhancementFactor, double);
  void SetLowLICContrastEnhancementFactor(double val);
  void SetHighLICContrastEnhancementFactor(double val);
  //
  vtkGetMacro(LowColorContrastEnhancementFactor, double);
  vtkGetMacro(HighColorContrastEnhancementFactor, double);
  void SetLowColorContrastEnhancementFactor(double val);
  void SetHighColorContrastEnhancementFactor(double val);
  ///@}

  ///@{
  /**
   * Enable/Disable the anti-aliasing pass. This optional pass (disabled by
   * default) can be enabled to reduce jagged patterns in the final LIC image.
   * Values greater than 0 control the number of iterations, 1 is typically
   * sufficient.
   */
  void SetAntiAlias(int val);
  vtkBooleanMacro(AntiAlias, int);
  vtkGetMacro(AntiAlias, int);
  ///@}

  ///@{
  /**
   * Set/Get the color mode. The color mode controls how scalar colors are
   * combined with the LIC in the final image. The BLEND mode combines scalar
   * colors with LIC intensities with proportional blending controlled by the
   * LICIntensity parameter. The MAP mode combines scalar colors with LIC,
   * by multiplication the HSL representation of color's lightness.

   * The default is COLOR_MODE_BLEND.
   */
  enum
  {
    COLOR_MODE_BLEND = 0,
    COLOR_MODE_MAP
  };
  void SetColorMode(int val);
  vtkGetMacro(ColorMode, int);
  ///@}

  ///@{
  /**
   * Factor used when blend mode is set to COLOR_MODE_BLEND. This controls the
   * contribution of the LIC in the final output image as follows:

   * c = LIC * LICIntensity + scalar * (1 - LICIntensity);

   * 0.0 produces same result as disabling LIC altogether, while 1.0 implies
   * show LIC result alone.
   */
  void SetLICIntensity(double val);
  vtkGetMacro(LICIntensity, double);
  ///@}

  ///@{
  /**
   * Factor used when blend mode is set to COLOR_MODE_MAP. This adds a bias to
   * the LIC image. The purpose of this is to adjust the brightness when a
   * brighter image is desired. The default of 0.0 results in no change. Values
   * gretaer than 0.0 will brighten the image while values less than 0.0 darken
   * the image.
   */
  void SetMapModeBias(double val);
  vtkGetMacro(MapModeBias, double);
  ///@}

  ///@{
  /**
   * Set the data containing a noise array as active scalars. Active scalars
   * array will be converted into a texture for use as noise in the LIC process.
   * Noise datasets are expected to be gray scale.
   */
  void SetNoiseDataSet(vtkImageData* data);
  vtkImageData* GetNoiseDataSet();
  ///@}

  ///@{
  /**
   * Set/Get the noise texture source. When not set the default 200x200 white
   * noise texture is used (see VTKData/Data/Data/noise.png). When set a noise
   * texture is generated based on the following parameters:

   * NoiseType               - select noise type. Gaussian, Uniform, etc
   * NoiseTextureSize        - number of pixels in square noise texture(side)
   * NoiseGrainSize          - number of pixels each noise value spans(side)
   * MinNoiseValue           - minimum noise color >=0 && < MaxNoiseValue
   * MaxNoiseValue           - maximum noise color <=1 ** > MinNoiseValue
   * NumberOfNoiseLevels     - number of discrete noise colors
   * ImpulseNoiseProbability - impulse noise is generated when < 1
   * ImpulseNoiseBackgroundValue  - the background color for untouched pixels
   * NoiseGeneratorSeed      - seed the random number generators

   * Changing the noise texture gives one greater control over the look of the
   * final image. The default is 0 which results in the use of a static 200x200
   * Gaussian noise texture. See VTKData/Data/Data/noise.png.
   */
  void SetGenerateNoiseTexture(int shouldGenerate);
  vtkGetMacro(GenerateNoiseTexture, int);
  ///@}

  ///@{
  /**
   * Select the statistical distribution of randomly generated noise values.
   * With uniform noise there is greater control over the range of values
   * in the noise texture. The Default is NOISE_TYPE_GAUSSIAN.
   */
  enum
  {
    NOISE_TYPE_UNIFORM = 0,
    NOISE_TYPE_GAUSSIAN = 1,
    NOISE_TYPE_PERLIN = 2
  };
  void SetNoiseType(int type);
  vtkGetMacro(NoiseType, int);
  ///@}

  ///@{
  /**
   * Set/Get the side length in pixels of the noise texture. The texture will
   * be length^2 pixels in area.
   */
  void SetNoiseTextureSize(int length);
  vtkGetMacro(NoiseTextureSize, int);
  ///@}

  ///@{
  /**
   * Each noise value will be length^2 pixels in area.
   */
  void SetNoiseGrainSize(int val);
  vtkGetMacro(NoiseGrainSize, int);
  ///@}

  ///@{
  /**
   * Set/Get the minimum and mximum  gray scale values that the generated noise
   * can take on. The generated noise will be in the range of MinNoiseValue to
   * MaxNoiseValue. Values are clamped within 0 to 1. MinNoiseValue must be
   * less than MaxNoiseValue.
   */
  void SetMinNoiseValue(double val);
  void SetMaxNoiseValue(double val);
  vtkGetMacro(MinNoiseValue, double);
  vtkGetMacro(MaxNoiseValue, double);
  ///@}

  ///@{
  /**
   * Set/Get the number of discrete values a noise pixel may take on. Default
   * 1024.
   */
  void SetNumberOfNoiseLevels(int val);
  vtkGetMacro(NumberOfNoiseLevels, int);
  ///@}

  ///@{
  /**
   * Control the density of the noise. A value of 1.0 produces uniform random
   * noise while values < 1.0 produce impulse noise with the given probability.
   */
  void SetImpulseNoiseProbability(double val);
  vtkGetMacro(ImpulseNoiseProbability, double);
  ///@}

  ///@{
  /**
   * The color to use for untouched pixels when impulse noise probability < 1.
   */
  void SetImpulseNoiseBackgroundValue(double val);
  vtkGetMacro(ImpulseNoiseBackgroundValue, double);
  ///@}

  ///@{
  /**
   * Set/Get the seed value used by the random number generator.
   */
  void SetNoiseGeneratorSeed(int val);
  vtkGetMacro(NoiseGeneratorSeed, int);
  ///@}

  ///@{
  /**
   * Control the screen space decomposition where LIC is computed.
   */
  enum
  {
    COMPOSITE_INPLACE = 0,
    COMPOSITE_INPLACE_DISJOINT = 1,
    COMPOSITE_BALANCED = 2,
    COMPOSITE_AUTO = 3
  };
  void SetCompositeStrategy(int val);
  vtkGetMacro(CompositeStrategy, int);
  ///@}

  /**
   * Returns true if the rendering context supports extensions needed by this
   * painter.
   */
  static bool IsSupported(vtkRenderWindow* context);

  /**
   * Methods used for parallel benchmarks. Use cmake to define
   * vtkSurfaceLICMapperTIME to enable benchmarks. During each
   * update timing information is stored, it can be written to
   * disk by calling WriteLog.
   */
  virtual void WriteTimerLog(const char*) {}

  /**
   * Make a shallow copy of this interface
   */
  void ShallowCopy(vtkSurfaceLICInterface* m);

  /**
   * Release any graphics resources that are being consumed by this mapper.
   * The parameter window could be used to determine which graphic
   * resources to release. In this case, releases the display lists.
   */
  virtual void ReleaseGraphicsResources(vtkWindow* win);

  /**
   * Returns true when rendering LIC is possible.
   */
  bool CanRenderSurfaceLIC(vtkActor* actor);

  /**
   * Look for changes that would trigger stage updates
   */
  void ValidateContext(vtkRenderer* renderer);

  /**
   * Creates a new communicator with/without the calling processes
   * as indicated by the passed in flag, if not 0 the calling process
   * is included in the new communicator. In parallel this call is mpi
   * collective on the world communicator. In serial this is a no-op.
   */
  virtual vtkPainterCommunicator* CreateCommunicator(int);

  /**
   * Creates a new communicator for internal use based on this
   * rank's visible data.
   */
  void CreateCommunicator(vtkRenderer*, vtkActor*, vtkDataObject* data);

  vtkPainterCommunicator* GetCommunicator();

  /**
   * Called from a mapper, does what is needed to make sure
   * the communicators are ready
   */
  void UpdateCommunicator(vtkRenderer* renderer, vtkActor* actor, vtkDataObject* data);

  ///@{
  /**
   * Does the data have vectors which we require
   */
  void SetHasVectors(bool val);
  bool GetHasVectors();
  ///@}

  /**
   * resoucre allocators
   */
  void InitializeResources();

  void PrepareForGeometry();
  void CompletedGeometry();
  void GatherVectors();
  void ApplyLIC();
  void CombineColorsAndLIC();
  void CopyToScreen();

  /**
   * Get the min/max across all ranks. min/max are in/out.
   * In serial operation this is a no-op, in parallel it
   * is a global collective reduction.
   */
  virtual void GetGlobalMinMax(vtkPainterCommunicator*, float&, float&) {}

  ///@{
  /**
   * Enable/Disable LIC.
   */
  vtkSetMacro(Enable, int);
  vtkGetMacro(Enable, int);
  vtkBooleanMacro(Enable, int);
  ///@}

protected:
  vtkSurfaceLICInterface();
  ~vtkSurfaceLICInterface() override;

  /**
   * Updates the noise texture, downsampling by the requested sample rate.
   */
  void UpdateNoiseImage(vtkRenderWindow* renWin);

  ///@{
  /**
   * Return false if stage can be skipped
   */
  virtual bool NeedToUpdateCommunicator();
  bool NeedToRenderGeometry(vtkRenderer* renderer, vtkActor* actor);
  bool NeedToGatherVectors();
  bool NeedToComputeLIC();
  bool NeedToColorLIC();
  void SetUpdateAll();
  ///@}

  int Enable;

  // Unit is a pixel length.
  int NumberOfSteps;
  double StepSize;
  int NormalizeVectors;

  int EnhancedLIC;
  int EnhanceContrast;
  double LowLICContrastEnhancementFactor;
  double HighLICContrastEnhancementFactor;
  double LowColorContrastEnhancementFactor;
  double HighColorContrastEnhancementFactor;
  int AntiAlias;

  int MaskOnSurface;
  double MaskThreshold;
  double MaskIntensity;
  double MaskColor[3];

  int ColorMode;
  double LICIntensity;
  double MapModeBias;

  int GenerateNoiseTexture;
  int NoiseType;
  int NoiseTextureSize;
  int NoiseGrainSize;
  double MinNoiseValue;
  double MaxNoiseValue;
  int NumberOfNoiseLevels;
  double ImpulseNoiseProbability;
  double ImpulseNoiseBackgroundValue;
  int NoiseGeneratorSeed;

  int AlwaysUpdate;
  int CompositeStrategy;

  vtkSurfaceLICHelper* Internals;

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

VTK_ABI_NAMESPACE_END
#endif
