/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkTableBasedClipDataSet.h

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/

/*****************************************************************************
 *
 * Copyright (c) 2000 - 2009, Lawrence Livermore National Security, LLC
 * Produced at the Lawrence Livermore National Laboratory
 * LLNL-CODE-400124
 * All rights reserved.
 *
 * This file was adapted from the VisIt clipper (vtkVisItClipper). For  details,
 * see https://visit.llnl.gov/.  The full copyright notice is contained in the
 * file COPYRIGHT located at the root of the VisIt distribution or at
 * http://www.llnl.gov/visit/copyright.html.
 *
 *****************************************************************************/

/**
 * @class   vtkTableBasedClipDataSet
 * @brief   Clip any dataset with a user-specified
 *  implicit function or an input scalar point data array.
 *
 *
 *  vtkTableBasedClipDataSet is a filter that clips any type of dataset using
 *  either any subclass of vtkImplicitFunction or an input scalar point data
 *  array. Clipping means that it actually "cuts" through the cells of the
 *  dataset, returning everything outside the specified implicit function (or
 *  greater than the scalar value) including "pieces" of a cell (Note to compare
 *  this with vtkExtractGeometry, which pulls out entire, uncut cells). The
 *  output of this filter is a vtkUnstructuredGrid data.
 *
 *  To use this filter, you need to decide whether an implicit function or an
 *  input scalar point data array is used for clipping. For the former case,
 *  1) define an implicit function
 *  2) provide it to this filter via SetClipFunction()
 *  If a clipping function is not specified, or GenerateClipScalars is off( the
 *  default), the input scalar point data array is then employed for clipping.
 *
 *  You can also specify a scalar (iso-)value, which is used to decide what is
 *  inside and outside the implicit function. You can also reverse the sense of
 *  what inside/outside is by setting IVAR InsideOut. The clipping algorithm
 *  proceeds by computing an implicit function value or using the input scalar
 *  point data value for each point in the dataset. This is compared against the
 *  scalar (iso-)value to determine the inside/outside status.
 *
 *  Although this filter sometimes (but rarely) may resort to the sibling class
 *  vtkClipDataSet for handling some special grids (such as cylinders or cones
 *  with capping faces in the form of a vtkPolyData), it itself is able to deal
 *  with most grids. It is worth mentioning that vtkTableBasedClipDataSet is
 *  capable of addressing the artifacts that may occur with vtkClipDataSet due
 *  to the possibly inconsistent triangulation modes between neighboring cells.
 *  In addition, the former is much faster than the latter. Furthermore, the
 *  former produces less cells (with ratio usually being 5~6) than by the latter
 *  in the output. In other words, this filter retains the original cells (i.e.,
 *  without triangulation / tetrahedralization) wherever possible. All these
 *  advantages are gained by adopting the unique clipping and triangulation tables
 *  proposed by VisIt.
 *
 * @warning
 *  vtkTableBasedClipDataSet makes use of a hash table (that is provided by class
 *  maintained by internal class vtkTableBasedClipperDataSetFromVolume) to achieve
 *  rapid removal of duplicate points. The hash-based mechanism simply compares the
 *  point Ids, without considering the actual inter-point distance (vtkClipDataSet
 *  adopts vtkMergePoints that though considers the inter-point distance for robust
 *  points merging ). As a result, some duplicate points may be present in the output.
 *  This problem occurs when some boundary (cut-through cells) happen to have faces
 *  EXACTLY aligned with the clipping plane (such as Plane, Box, or other implicit
 *  functions with planar shapes). The occurrence (though very rare) of duplicate
 *  points produces degenerate cells, which can be fixed by post-processing the
 *  output with a filter like vtkCleanGrid.
 *
 * @par Thanks:
 *  This filter was adapted from the VisIt clipper (vtkVisItClipper).
 *
 * @sa
 *  vtkClipDataSet vtkClipVolume vtkClipPolyData vtkCutter vtkImplicitFunction
 */

#ifndef vtkTableBasedClipDataSet_h
#define vtkTableBasedClipDataSet_h

#include "vtkFiltersGeneralModule.h" // For export macro
#include "vtkUnstructuredGridAlgorithm.h"

class vtkCallbackCommand;
class vtkImplicitFunction;
class vtkIncrementalPointLocator;

class VTKFILTERSGENERAL_EXPORT vtkTableBasedClipDataSet : public vtkUnstructuredGridAlgorithm
{
public:
  vtkTypeMacro(vtkTableBasedClipDataSet, vtkUnstructuredGridAlgorithm);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  /**
   * Create an instance with a user-specified implicit function, turning off
   * IVARs InsideOut and GenerateClipScalars and setting IVAR Value to 0.0.
   */
  static vtkTableBasedClipDataSet* New();

  /**
   * Get the MTime for which the point locator and clip function are considered.
   */
  vtkMTimeType GetMTime() override;

  //@{
  /**
   * Set/Get the InsideOut flag. With this flag off, a vertex is considered
   * inside (the implicit function or the isosurface) if the (function or scalar)
   * value is greater than IVAR Value. With this flag on, a vertex is considered
   * inside (the implicit function or the isosurface) if the (function or scalar)
   * value is less than or equal to IVAR Value. This flag is off by default.
   */
  vtkSetMacro(InsideOut, vtkTypeBool);
  vtkGetMacro(InsideOut, vtkTypeBool);
  vtkBooleanMacro(InsideOut, vtkTypeBool);
  //@}

  //@{
  /**
   * Set/Get the clipping value of the implicit function (if an implicit function
   * is applied) or scalar data array (if a scalar data array is used), with 0.0
   * as the default value. This value is ignored if flag UseValueAsOffset is true
   * AND a clip function is defined.
   */
  vtkSetMacro(Value, double);
  vtkGetMacro(Value, double);
  //@}

  //@{
  /**
   * Set/Get flag UseValueAsOffset, with true as the default value. With this flag
   * on, IVAR Value is used as an offset parameter to the implicit function. Value
   * is used only when clipping using a scalar array.
   */
  vtkSetMacro(UseValueAsOffset, bool);
  vtkGetMacro(UseValueAsOffset, bool);
  vtkBooleanMacro(UseValueAsOffset, bool);
  //@}

  //@{
  /**
   * Set/Get the implicit function with which to perform the clipping operation.
   * Unless an implicit function is defined, the specified input scalar data will
   * be used for clipping.
   */
  virtual void SetClipFunction(vtkImplicitFunction*);
  vtkGetObjectMacro(ClipFunction, vtkImplicitFunction);
  //@}

  //@{
  /**
   * Set/Get flag GenerateClipScalars, with 0 as the default value. With this
   * flag on, the scalar point data values obtained by evaluating the implicit
   * function will be exported to the output. Note that this flag requiries that
   * an implicit function be provided.
   */
  vtkSetMacro(GenerateClipScalars, vtkTypeBool);
  vtkGetMacro(GenerateClipScalars, vtkTypeBool);
  vtkBooleanMacro(GenerateClipScalars, vtkTypeBool);
  //@}

  //@{
  /**
   * Set/Get a point locator locator for merging duplicate points. By default,
   * an instance of vtkMergePoints is used. Note that this IVAR is provided
   * in this class only because this filter may resort to its sibling class
   * vtkClipDataSet when processing some special grids (such as cylinders or
   * cones with capping faces in the form of a vtkPolyData) while the latter
   * requires a point locator. This filter itself does not need a locator.
   */
  void SetLocator(vtkIncrementalPointLocator* locator);
  vtkGetObjectMacro(Locator, vtkIncrementalPointLocator);
  //@}

  //@{
  /**
   * Set/Get the tolerance used for merging duplicate points near the clipping
   * intersection cells. This tolerance may prevent the generation of degenerate
   * primitives. Note that only 3D cells actually use this IVAR.
   */
  vtkSetClampMacro(MergeTolerance, double, 0.0001, 0.25);
  vtkGetMacro(MergeTolerance, double);
  //@}

  /**
   * Create a default point locator when none is specified. The point locator is
   * used to merge coincident points.
   */
  void CreateDefaultLocator();

  //@{
  /**
   * Set/Get whether a second output is generated. The second output contains the
   * polygonal data that is clipped away by the iso-surface.
   */
  vtkSetMacro(GenerateClippedOutput, vtkTypeBool);
  vtkGetMacro(GenerateClippedOutput, vtkTypeBool);
  vtkBooleanMacro(GenerateClippedOutput, vtkTypeBool);
  //@}

  /**
   * Return the clipped output.
   */
  vtkUnstructuredGrid* GetClippedOutput();

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

protected:
  vtkTableBasedClipDataSet(vtkImplicitFunction* cf = nullptr);
  ~vtkTableBasedClipDataSet() override;

  int RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) override;
  int FillInputPortInformation(int port, vtkInformation* info) override;

  /**
   * This function resorts to the sibling class vtkClipDataSet to handle
   * special grids (such as cylinders or cones with capping faces in the
   * form a vtkPolyData).
   */
  void ClipDataSet(vtkDataSet* pDataSet, vtkDataArray* clipAray, vtkUnstructuredGrid* unstruct);

  /**
   * This function takes a vtkImageData as a vtkRectilinearGrid, which is then
   * clipped by ClipRectilinearGridData(......).
   */
  void ClipImageData(
    vtkDataSet* inputGrd, vtkDataArray* clipAray, double isoValue, vtkUnstructuredGrid* outputUG);

  /**
   * This function clips a vtkPolyData object based on a specified iso-value
   * (isoValue) using a scalar point data array (clipAray) that is either just an
   * input scalar point data array or the result of evaluating an implicit function
   * (provided via SetClipFunction()). The clipping result is exported to outputUG.
   */
  void ClipPolyData(
    vtkDataSet* inputGrd, vtkDataArray* clipAray, double isoValue, vtkUnstructuredGrid* outputUG);

  /**
   * This function clips a vtkRectilinearGrid based on a specified iso-value
   * (isoValue) using a scalar point data array (clipAray) that is either just an
   * input scalar point data array or the result of evaluating an implicit function
   * (provided via SetClipFunction()). The clipping result is exported to outputUG.
   */
  void ClipRectilinearGridData(
    vtkDataSet* inputGrd, vtkDataArray* clipAray, double isoValue, vtkUnstructuredGrid* outputUG);

  /**
   * This function clips a vtkStructuredGrid based on a specified iso-value
   * (isoValue) using a scalar point data array (clipAray) that is either just an
   * input scalar point data array or the result of evaluating an implicit function
   * (provided via SetClipFunction()). The clipping result is exported to outputUG.
   */
  void ClipStructuredGridData(
    vtkDataSet* inputGrd, vtkDataArray* clipAray, double isoValue, vtkUnstructuredGrid* outputUG);

  /**
   * This function clips a vtkUnstructuredGrid based on a specified iso-value
   * (isoValue) using a scalar point data array (clipAray) that is either just an
   * input scalar point data array or the result of evaluating an implicit function
   * (provided via SetClipFunction()). The clipping result is exported to outputUG.
   */
  void ClipUnstructuredGridData(
    vtkDataSet* inputGrd, vtkDataArray* clipAray, double isoValue, vtkUnstructuredGrid* outputUG);

  /**
   * Register a callback function with the InternalProgressObserver.
   */
  static void InternalProgressCallbackFunction(vtkObject*, unsigned long, void* clientdata, void*);

  /**
   * The actual operation executed by the callback function.
   */
  void InternalProgressCallback(vtkAlgorithm* algorithm);

  vtkTypeBool InsideOut;
  vtkTypeBool GenerateClipScalars;
  vtkTypeBool GenerateClippedOutput;
  bool UseValueAsOffset;
  double Value;
  double MergeTolerance;
  vtkCallbackCommand* InternalProgressObserver;
  vtkImplicitFunction* ClipFunction;
  vtkIncrementalPointLocator* Locator;

  int OutputPointsPrecision;

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

#endif
