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

  Program:   Visualization Toolkit
  Module:    vtkEuclideanClusterExtraction.h

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See LICENSE file 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.

=========================================================================*/
/**
 * @class   vtkEuclideanClusterExtraction
 * @brief   perform segmentation based on geometric
 * proximity and optional scalar threshold
 *
 * vtkEuclideanClusterExtraction is a filter that extracts points that are in
 * close geometric proximity, and optionally satisfies a scalar threshold
 * criterion. (Points extracted in this way are referred to as clusters.)
 * The filter works in one of five ways: 1) extract the largest cluster in the
 * dataset; 2) extract specified cluster number(s); 3) extract all clusters
 * containing specified point ids; 4) extract the cluster closest to a specified
 * point; or 5) extract all clusters (which can be used for coloring the clusters).
 *
 * Note that geometric proximity is defined by setting the Radius instance
 * variable. This variable defines a local sphere around each point; other
 * points contained in this sphere are considered "connected" to the
 * point. Setting this number too large will connect clusters that should not
 * be; setting it too small will fragment the point cloud into myriad
 * clusters. To accelerate the geometric proximity operations, a point
 * locator may be specified. By default, a vtkStaticPointLocator is used, but
 * any vtkAbstractPointLocator may be specified.
 *
 * The behavior of vtkEuclideanClusterExtraction can be modified by turning
 * on the boolean ivar ScalarConnectivity. If this flag is on, the clustering
 * algorithm is modified so that points are considered part of a cluster if
 * they satisfy both the geometric proximity measure, and the points scalar
 * values falls into the scalar range specified. This use of
 * ScalarConnectivity is particularly useful for data with intensity or color
 * information, serving as a simple "connected segmentation" algorithm. For
 * example, by using a seed point in a known cluster, clustering will pull
 * out all points "representing" the local structure.
 *
 * @sa
 * vtkConnectivityFilter vtkPolyDataConnectivityFilter
*/

#ifndef vtkEuclideanClusterExtraction_h
#define vtkEuclideanClusterExtraction_h

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

#define VTK_EXTRACT_POINT_SEEDED_CLUSTERS 1
#define VTK_EXTRACT_SPECIFIED_CLUSTERS 2
#define VTK_EXTRACT_LARGEST_CLUSTER 3
#define VTK_EXTRACT_ALL_CLUSTERS 4
#define VTK_EXTRACT_CLOSEST_POINT_CLUSTER 5

class vtkDataArray;
class vtkFloatArray;
class vtkIdList;
class vtkIdTypeArray;
class vtkAbstractPointLocator;


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

  /**
   * Construct with default extraction mode to extract largest clusters.
   */
  static vtkEuclideanClusterExtraction *New();

  //@{
  /**
   * Specify the local search radius.
   */
  vtkSetClampMacro(Radius,double,0.0,VTK_FLOAT_MAX);
  vtkGetMacro(Radius,double);
  //@}

  //@{
  /**
   * Turn on/off connectivity based on scalar value. If on, points are
   * connected only if the are proximal AND the scalar value of a candidate
   * point falls in the scalar range specified. Of course input point scalar
   * data must be provided.
   */
  vtkSetMacro(ScalarConnectivity,bool);
  vtkGetMacro(ScalarConnectivity,bool);
  vtkBooleanMacro(ScalarConnectivity,bool);
  //@}

  //@{
  /**
   * Set the scalar range used to extract points based on scalar connectivity.
   */
  vtkSetVector2Macro(ScalarRange,double);
  vtkGetVector2Macro(ScalarRange,double);
  //@}

  //@{
  /**
   * Control the extraction of connected surfaces.
   */
  vtkSetClampMacro(ExtractionMode,int,
            VTK_EXTRACT_POINT_SEEDED_CLUSTERS,VTK_EXTRACT_CLOSEST_POINT_CLUSTER);
  vtkGetMacro(ExtractionMode,int);
  void SetExtractionModeToPointSeededClusters()
    {this->SetExtractionMode(VTK_EXTRACT_POINT_SEEDED_CLUSTERS);};
  void SetExtractionModeToLargestCluster()
    {this->SetExtractionMode(VTK_EXTRACT_LARGEST_CLUSTER);};
  void SetExtractionModeToSpecifiedClusters()
    {this->SetExtractionMode(VTK_EXTRACT_SPECIFIED_CLUSTERS);};
  void SetExtractionModeToClosestPointCluster()
    {this->SetExtractionMode(VTK_EXTRACT_CLOSEST_POINT_CLUSTER);};
  void SetExtractionModeToAllClusters()
    {this->SetExtractionMode(VTK_EXTRACT_ALL_CLUSTERS);};
  const char *GetExtractionModeAsString();
  //@}

  /**
   * Initialize the list of point ids used to seed clusters.
   */
  void InitializeSeedList();

  /**
   * Add a seed id (point id). Note: ids are 0-offset.
   */
  void AddSeed(vtkIdType id);

  /**
   * Delete a seed id.a
   */
  void DeleteSeed(vtkIdType id);

  /**
   * Initialize the list of cluster ids to extract.
   */
  void InitializeSpecifiedClusterList();

  /**
   * Add a cluster id to extract. Note: ids are 0-offset.
   */
  void AddSpecifiedCluster(int id);

  /**
   * Delete a cluster id to extract.
   */
  void DeleteSpecifiedCluster(int id);

  //@{
  /**
   * Used to specify the x-y-z point coordinates when extracting the cluster
   * closest to a specified point.
   */
  vtkSetVector3Macro(ClosestPoint,double);
  vtkGetVectorMacro(ClosestPoint,double,3);
  //@}

  /**
   * Obtain the number of connected clusters. This value is valid only after filter execution.
   */
  int GetNumberOfExtractedClusters();

  //@{
  /**
   * Turn on/off the coloring of connected clusters.
   */
  vtkSetMacro(ColorClusters,bool);
  vtkGetMacro(ColorClusters,bool);
  vtkBooleanMacro(ColorClusters,bool);
  //@}

  //@{
  /**
   * Specify a point locator. By default a vtkStaticPointLocator is
   * used. The locator performs efficient proximity searches near a
   * specified interpolation position.
   */
  void SetLocator(vtkAbstractPointLocator *locator);
  vtkGetObjectMacro(Locator,vtkAbstractPointLocator);
  //@}

protected:
  vtkEuclideanClusterExtraction();
  ~vtkEuclideanClusterExtraction() override;

  double Radius; //connection radius
  bool ColorClusters; //boolean turns on/off scalar gen for separate clusters
  int ExtractionMode; //how to extract clusters
  vtkIdList *Seeds; //id's of points or cells used to seed clusters
  vtkIdList *SpecifiedClusterIds; //clusters specified for extraction
  vtkIdTypeArray *ClusterSizes; //size (in cells) of each cluster extracted

  double ClosestPoint[3];

  bool ScalarConnectivity;
  double ScalarRange[2];

  vtkAbstractPointLocator *Locator;

  // Configure the pipeline
  int RequestData(vtkInformation *, vtkInformationVector **,
                          vtkInformationVector *) override;
  int FillInputPortInformation(int port, vtkInformation *info) override;

  // Internal method for propagating connected waves.
  void InsertIntoWave(vtkIdList *wave, vtkIdType ptId);
  void TraverseAndMark(vtkPoints *pts);

private:
  vtkEuclideanClusterExtraction(const vtkEuclideanClusterExtraction&) = delete;
  void operator=(const vtkEuclideanClusterExtraction&) = delete;

  // used to support algorithm execution
  vtkFloatArray *NeighborScalars;
  vtkIdList *NeighborPointIds;
  char *Visited;
  vtkIdType *PointMap;
  vtkIdTypeArray *NewScalars;
  vtkIdType ClusterNumber;
  vtkIdType PointNumber;
  vtkIdType NumPointsInCluster;
  vtkDataArray *InScalars;
  vtkIdList *Wave;
  vtkIdList *Wave2;
  vtkIdList *PointIds;

};

//@{
/**
 * Return the method of extraction as a string.
 */
inline const char *vtkEuclideanClusterExtraction::GetExtractionModeAsString(void)
{
  if ( this->ExtractionMode == VTK_EXTRACT_POINT_SEEDED_CLUSTERS )
  {
    return "ExtractPointSeededClusters";
  }
  else if ( this->ExtractionMode == VTK_EXTRACT_SPECIFIED_CLUSTERS )
  {
    return "ExtractSpecifiedClusters";
  }
  else if ( this->ExtractionMode == VTK_EXTRACT_ALL_CLUSTERS )
  {
    return "ExtractAllClusters";
  }
  else if ( this->ExtractionMode == VTK_EXTRACT_CLOSEST_POINT_CLUSTER )
  {
    return "ExtractClosestPointCluster";
  }
  else
  {
    return "ExtractLargestCluster";
  }
}
//@}

#endif
