// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
/**
 * @class   vtkClipClosedSurface
 * @brief   Clip a closed surface with a plane collection
 *
 * vtkClipClosedSurface will clip a closed polydata surface with a
 * collection of clipping planes.  It will produce a new closed surface
 * by creating new polygonal faces where the input data was clipped.
 *
 * The orientation of the polygons that form the surface is important.
 * Polygons have a front face and a back face, and it's the back face that
 * defines the interior or "solid" region of the closed surface.  When a
 * clipping plane cuts through a "solid" region, a new cut face is generated,
 * but not when a clipping plane cuts through a hole or "empty" region.  This
 * distinction is crucial when dealing with complex surfaces.  Note that if
 * a simple surface has its back faces pointing outwards, then that surface
 * defines a hole in a potentially infinite solid.
 *
 * Non-manifold surfaces should not be used as input for this filter.
 * The input surface should have no open edges, and must not have any
 * edges that are shared by more than two faces.  The vtkFeatureEdges
 * filter can be used to verify that a data set satisfies these conditions.
 * In addition, the input surface should not self-intersect, meaning
 * that the faces of the surface should only touch at their edges.
 *
 * If GenerateOutline is on, this filter will generate an outline wherever
 * the clipping planes intersect the data.  The ScalarMode option
 * will add cell scalars to the output, so that the generated faces
 * can be visualized in a different color from the original surface.
 *
 * The InsideOut flag can be used to reverse the sense of what inside/outside
 * the clip region means. This changes the which side of the clipping plane is
 * clipped away.
 *
 * This filter can be configured to compute a second output. The second output
 * is the polygonal data with the new triangulated faces. Set the
 * GenerateClipFaceOutput boolean on if you wish to access this output data.
 *
 * @warning
 * The triangulation of new faces is done in O(n) time for simple convex
 * inputs, but for non-convex inputs the worst-case time is O(n^2*m^2)
 * where n is the number of points and m is the number of 3D cavities.
 * The best triangulation algorithms, in contrast, are O(n log n).
 * There are also rare cases where the triangulation will fail to produce
 * a watertight output.  Turn on TriangulationErrorDisplay to be notified
 * of these failures.
 *
 * @sa
 * vtkOutlineFilter vtkOutlineSource vtkVolumeOutlineSource
 * vtkContourTriangulator
 *
 * @par Thanks:
 * Thanks to David Gobbi for contributing this class to VTK.
 */

#ifndef vtkClipClosedSurface_h
#define vtkClipClosedSurface_h

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

VTK_ABI_NAMESPACE_BEGIN
class vtkPlaneCollection;
class vtkUnsignedCharArray;
class vtkDoubleArray;
class vtkIdTypeArray;
class vtkCellArray;
class vtkPointData;
class vtkCellData;
class vtkPolygon;
class vtkIdList;
class vtkCCSEdgeLocator;

enum
{
  VTK_CCS_SCALAR_MODE_NONE = 0,
  VTK_CCS_SCALAR_MODE_COLORS = 1,
  VTK_CCS_SCALAR_MODE_LABELS = 2
};

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

  ///@{
  /**
   * Set the vtkPlaneCollection that holds the clipping planes.
   */
  virtual void SetClippingPlanes(vtkPlaneCollection* planes);
  vtkGetObjectMacro(ClippingPlanes, vtkPlaneCollection);
  ///@}

  ///@{
  /**
   * Set the tolerance for creating new points while clipping.  If the
   * tolerance is too small, then degenerate triangles might be produced.
   * The default tolerance is 1e-6.
   */
  vtkSetMacro(Tolerance, double);
  vtkGetMacro(Tolerance, double);
  ///@}

  ///@{
  /**
   * Pass the point data to the output.  Point data will be interpolated
   * when new points are generated.  This is off by default.
   */
  vtkSetMacro(PassPointData, vtkTypeBool);
  vtkBooleanMacro(PassPointData, vtkTypeBool);
  vtkGetMacro(PassPointData, vtkTypeBool);
  ///@}

  ///@{
  /**
   * Set whether to generate an outline wherever an input face was
   * cut by a plane.  This is off by default.
   */
  vtkSetMacro(GenerateOutline, vtkTypeBool);
  vtkBooleanMacro(GenerateOutline, vtkTypeBool);
  vtkGetMacro(GenerateOutline, vtkTypeBool);
  ///@}

  ///@{
  /**
   * Set whether to generate polygonal faces for the output.  This is
   * on by default.  If it is off, then the output will have no polys.
   */
  vtkSetMacro(GenerateFaces, vtkTypeBool);
  vtkBooleanMacro(GenerateFaces, vtkTypeBool);
  vtkGetMacro(GenerateFaces, vtkTypeBool);
  ///@}

  ///@{
  /**
   * Set whether to add cell scalars, so that new faces and outlines
   * can be distinguished from original faces and lines.  The options
   * are "None", "Colors", and "Labels".  For the "Labels" option,
   * a scalar value of "0" indicates an original cell, "1" indicates
   * a new cell on a cut face, and "2" indicates a new cell on the
   * ActivePlane as set by the SetActivePlane() method.  The default
   * scalar mode is "None".
   */
  vtkSetClampMacro(ScalarMode, int, VTK_CCS_SCALAR_MODE_NONE, VTK_CCS_SCALAR_MODE_LABELS);
  void SetScalarModeToNone() { this->SetScalarMode(VTK_CCS_SCALAR_MODE_NONE); }
  void SetScalarModeToColors() { this->SetScalarMode(VTK_CCS_SCALAR_MODE_COLORS); }
  void SetScalarModeToLabels() { this->SetScalarMode(VTK_CCS_SCALAR_MODE_LABELS); }
  vtkGetMacro(ScalarMode, int);
  const char* GetScalarModeAsString();
  ///@}

  ///@{
  /**
   * Set the color for all cells were part of the original geometry.
   * If the input data already has color cell scalars, then those
   * values will be used and parameter will be ignored.  The default color
   * is red.  Requires SetScalarModeToColors.
   */
  vtkSetVector3Macro(BaseColor, double);
  vtkGetVector3Macro(BaseColor, double);
  ///@}

  ///@{
  /**
   * Set the color for any new geometry, either faces or outlines, that are
   * created as a result of the clipping. The default color is orange.
   * Requires SetScalarModeToColors.
   */
  vtkSetVector3Macro(ClipColor, double);
  vtkGetVector3Macro(ClipColor, double);
  ///@}

  ///@{
  /**
   * Set the active plane, so that the clipping from that plane can be
   * displayed in a different color.  Set this to -1 if there is no active
   * plane.  The default value is -1.
   */
  vtkSetMacro(ActivePlaneId, int);
  vtkGetMacro(ActivePlaneId, int);
  ///@}

  ///@{
  /**
   * Set the color for any new geometry produced by clipping with the
   * ActivePlane, if ActivePlaneId is set.  Default is yellow.
   * Requires SetScalarModeToColors.
   */
  vtkSetVector3Macro(ActivePlaneColor, double);
  vtkGetVector3Macro(ActivePlaneColor, double);
  ///@}

  ///@{
  /**
   * Generate errors when the triangulation fails.  Usually the
   * triangulation errors are too small to see, but they result in
   * a surface that is not watertight.  This option has no impact
   * on performance.
   */
  vtkSetMacro(TriangulationErrorDisplay, vtkTypeBool);
  vtkBooleanMacro(TriangulationErrorDisplay, vtkTypeBool);
  vtkGetMacro(TriangulationErrorDisplay, vtkTypeBool);
  ///@}

  ///@{
  /**
   * Set/Get the InsideOut flag. When off, a vertex is considered inside the
   * implicit function if it lies in front of the clipping plane. When
   * InsideOutside is turned on, a vertex is considered inside if it lies on the
   * back side of the plane.  InsideOut is off by default.
   *
   * \note Regardless of the InsideOut flag, it is not possible to generate an
   * inside (that is, convex) corner by clipping.
   */
  vtkSetMacro(InsideOut, vtkTypeBool);
  vtkGetMacro(InsideOut, vtkTypeBool);
  vtkBooleanMacro(InsideOut, vtkTypeBool);
  ///@}

  ///@{
  /**
   * Control whether a second output is generated. The second output contains
   * the polygonal data that is generated at the clip face as a result of the
   * triangulation.  GenerateClipFaceOutput is off by default.
   */
  vtkSetMacro(GenerateClipFaceOutput, vtkTypeBool);
  vtkGetMacro(GenerateClipFaceOutput, vtkTypeBool);
  vtkBooleanMacro(GenerateClipFaceOutput, vtkTypeBool);
  ///@}

  /**
   * Return the clip face triangulated output.
   */
  vtkPolyData* GetClipFaceOutput();

protected:
  vtkClipClosedSurface();
  ~vtkClipClosedSurface() override;

  vtkPlaneCollection* ClippingPlanes;

  double Tolerance;

  vtkTypeBool PassPointData;
  vtkTypeBool GenerateOutline;
  vtkTypeBool GenerateFaces;
  int ActivePlaneId;
  int ScalarMode;
  double BaseColor[3];
  double ClipColor[3];
  double ActivePlaneColor[3];
  vtkTypeBool InsideOut = false;
  vtkTypeBool GenerateClipFaceOutput = false;

  vtkTypeBool TriangulationErrorDisplay;

  vtkIdList* IdList;

  int ComputePipelineMTime(vtkInformation* request, vtkInformationVector** inputVector,
    vtkInformationVector* outputVector, int requestFromOutputPort, vtkMTimeType* mtime) override;

  int RequestData(vtkInformation* request, vtkInformationVector** inputVector,
    vtkInformationVector* outputVector) override;

  /**
   * Method for clipping lines and copying the scalar data.
   */
  void ClipLines(vtkPoints* points, vtkDoubleArray* pointScalars, vtkPointData* pointData,
    vtkCCSEdgeLocator* edgeLocator, vtkCellArray* inputCells, vtkCellArray* outputLines,
    vtkCellData* inCellData, vtkCellData* outLineData);

  /**
   * Clip and contour polys in one step, in order to guarantee
   * that the contour lines exactly match the new free edges of
   * the clipped polygons.  This exact correspondence is necessary
   * in order to guarantee that the surface remains closed.
   */
  void ClipAndContourPolys(vtkPoints* points, vtkDoubleArray* pointScalars, vtkPointData* pointData,
    vtkCCSEdgeLocator* edgeLocator, int triangulate, vtkCellArray* inputCells,
    vtkCellArray* outputPolys, vtkCellArray* outputLines, vtkCellData* inCellData,
    vtkCellData* outPolyData, vtkCellData* outLineData);

  /**
   * A helper function for interpolating a new point along an edge.  It
   * stores the index of the interpolated point in "i", and returns 1 if
   * a new point was added to the points.  The values i0, i1, v0, v1 are
   * the edge endpoints and scalar values, respectively.
   */
  static int InterpolateEdge(vtkPoints* points, vtkPointData* pointData,
    vtkCCSEdgeLocator* edgeLocator, double tol, vtkIdType i0, vtkIdType i1, double v0, double v1,
    vtkIdType& i);

  /**
   * A robust method for triangulating a polygon.  It cleans up the polygon
   * and then applies the ear-cut method that is implemented in vtkPolygon.
   * A zero return value indicates that triangulation failed.
   */
  int TriangulatePolygon(vtkIdList* polygon, vtkPoints* points, vtkCellArray* triangles);

  /**
   * Given some closed contour lines, create a triangle mesh that
   * fills those lines.  The input lines must be single-segment lines,
   * not polylines.  The input lines do not have to be in order.
   * Only lines from firstLine to will be used.  Specify the normal
   * of the clip plane, which will be opposite the normals
   * of the polys that will be produced.  If outCD has scalars, then color
   * scalars will be added for each poly that is created.
   */
  void TriangulateContours(vtkPolyData* data, vtkIdType firstLine, vtkIdType numLines,
    vtkCellArray* outputPolys, const double normal[3]);

  /**
   * Break polylines into individual lines, copying scalar values from
   * inputScalars starting at firstLineScalar.  If inputScalars is zero,
   * then scalars will be set to color.  If scalars is zero, then no
   * scalars will be generated.
   */
  static void BreakPolylines(vtkCellArray* inputLines, vtkCellArray* outputLines,
    vtkUnsignedCharArray* inputScalars, vtkIdType firstLineScalar,
    vtkUnsignedCharArray* outputScalars, const unsigned char color[3]);

  /**
   * Copy polygons and their associated scalars to a new array.
   * If inputScalars is set to zero, set polyScalars to color instead.
   * If polyScalars is set to zero, don't generate scalars.
   */
  static void CopyPolygons(vtkCellArray* inputPolys, vtkCellArray* outputPolys,
    vtkUnsignedCharArray* inputScalars, vtkIdType firstPolyScalar,
    vtkUnsignedCharArray* outputScalars, const unsigned char color[3]);

  /**
   * Break triangle strips and add the triangles to the output. See
   * CopyPolygons for more information.
   */
  static void BreakTriangleStrips(vtkCellArray* inputStrips, vtkCellArray* outputPolys,
    vtkUnsignedCharArray* inputScalars, vtkIdType firstStripScalar,
    vtkUnsignedCharArray* outputScalars, const unsigned char color[3]);

  /**
   * Squeeze the points and store them in the output.  Only the points that
   * are used by the cells will be saved, and the pointIds of the cells will
   * be modified.
   */
  static void SqueezeOutputPoints(
    vtkPolyData* output, vtkPoints* points, vtkPointData* pointData, int outputPointDataType);

  /**
   * Take three colors as doubles, and convert to unsigned char.
   */
  static void CreateColorValues(const double color1[3], const double color2[3],
    const double color3[3], unsigned char colors[3][3]);

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

VTK_ABI_NAMESPACE_END
#endif
