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

  Program:   Visualization Toolkit
  Module:    vtkMultiObjectMassProperties.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.

=========================================================================*/
/**
 * @class   vtkMultiObjectMassProperties
 * @brief   compute volume and area of objects in a polygonal mesh
 *
 * vtkMultiObjectMassProperties estimates the volume and the surface area of
 * a polygonal mesh. Multiple, valid closed objects may be represented, and
 * each object is assumed to be defined as a polyhedron defined by polygonal
 * faces (i.e., the faces do not have to be triangles). The algorithm
 * computes the total volume and area, as well as per object values which are
 * placed in data arrays. Note that an object is valid only if it is manifold
 * and closed (i.e., each edge is used exactly two times by two different
 * polygons). Invalid objects are processed but may produce inaccurate
 * results. Inconsistent polygon ordering is also allowed.
 *
 * The algorithm is composed of two basic parts. First a connected traversal
 * is performed to identify objects, detect whether the objects are valid,
 * and ensure that the composing polygons are ordered consistently. Next, in
 * threaded execution, a parallel process of computing areas and volumes is
 * performed. It is possible to skip the first part if the SkipValidityCheck
 * is enabled, AND a vtkIdTypeArray data array named "ObjectIds" is
 * associated with the polygon input (i.e., cell data) that enumerates which
 * object every polygon belongs to (i.e., indictaes that it is a boundary
 * polygon of a specified object).
 *
 * The algorithm implemented here is inspired by this paper:
 * http://chenlab.ece.cornell.edu/Publication/Cha/icip01_Cha.pdf. Also see
 * the Stackflow entry: https://stackoverflow.com/questions/1406029/
 * how-to-calculate-the-volume-of-a-3d-mesh-object-the-surface-of-which-is-made-up.
 * The general assumption here is that the model is of closed surface.  Also,
 * this approach requires triangulating the polygons so triangle meshes are
 * processed much faster. Finally, the volume and area calculations are done
 * in paraellel (threaded) after a connectivity pass is made (used to
 * identify objects and verify that they are manifold and closed).
 *
 * The output contains six additional data arrays. The arrays
 * "ObjectValidity", "ObjectVolumes" and "ObjectAreas" are placed in the
 * output field data.  These are arrays which indicate which objects are
 * valid; the volume of each object; and the surface area of each
 * object. Three additional arrays are placed in the output cell data, and
 * indicate, on a per polygons basis, which object the polygon bounds
 * "ObjectIds"; the polygon area "Areas"; and the contribution of volume
 * "Volumes".  Additionally, the TotalVolume and TotalArea is available after
 * the filter executes (i.e., the sum of the ObjectVolumes and ObjectAreas
 * arrays).
 *
 * Per object validity, as mentioned previously, is reported in the
 * ObjectValidity array. However another variable, AllValid, is set after
 * filter execution which indicates whether all objects are valid (!=0) or
 * not. This information can be used as a shortcut in case you want to skip
 * validity checking on an object-by-object basis.
 *
 * @warning
 * This filter operates on the polygonal data contained in the input
 * vtkPolyData. Other types (verts, lines, triangle strips) are ignored and
 * not passed to the output. The input polys and points, as well as
 * associated point and cell data, are passed through to the output.
 *
 * @warning
 * This filter is similar to vtkMassProperties. However vtkMassProperties
 * operates on triangle meshes and assumes only a single, closed, properly
 * oriented surface is represented. vtkMultiObjectMassProperties performs
 * additional topological and connectivity operations to identify separate
 * objects, and confirms that they are manifold. It also accommodates
 * inconsistent ordering.
 *
 * @warning
 * This class has been threaded with vtkSMPTools. Using TBB or other
 * non-sequential type (set in the CMake variable
 * VTK_SMP_IMPLEMENTATION_TYPE) may improve performance significantly.
 *
 * @sa
 * vtkMassProperties
 */

#ifndef vtkMultiObjectMassProperties_h
#define vtkMultiObjectMassProperties_h

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

class vtkDoubleArray;
class vtkUnsignedCharArray;
class vtkIdTypeArray;

class VTKFILTERSCORE_EXPORT vtkMultiObjectMassProperties : public vtkPolyDataAlgorithm
{
public:
  ///@{
  /**
   * Standard methods for construction, type and printing.
   */
  static vtkMultiObjectMassProperties* New();
  vtkTypeMacro(vtkMultiObjectMassProperties, vtkPolyDataAlgorithm);
  void PrintSelf(ostream& os, vtkIndent indent) override;
  ///@}

  ///@{
  /**
   * Indicate whether to skip the validity check (the first part of the
   * algorithm). By default this is off; however even if enabled validity
   * skipping will only occur if a vtkIdTypeArray named "ObjectIds" is also
   * provided on input to the filter.
   */
  vtkSetMacro(SkipValidityCheck, vtkTypeBool);
  vtkGetMacro(SkipValidityCheck, vtkTypeBool);
  vtkBooleanMacro(SkipValidityCheck, vtkTypeBool);
  ///@}

  /**
   * Return the number of objects identified. This is valid only after the
   * filter executes. Check the ObjectValidity array which indicates which of
   * these identified objects are valid. Invalid objects may have incorrect
   * volume and area values.
   */
  vtkIdType GetNumberOfObjects() { return this->NumberOfObjects; }

  /**
   * Return whether all objects are valid or not. This is valid only after the
   * filter executes.
   */
  vtkTypeBool GetAllValid() { return this->AllValid; }

  /**
   * Return the summed volume of all objects. This is valid only after the
   * filter executes.
   */
  double GetTotalVolume() { return this->TotalVolume; }

  /**
   * Return the summed area of all objects. This is valid only after the
   * filter executes.
   */
  double GetTotalArea() { return this->TotalArea; }

protected:
  vtkMultiObjectMassProperties();
  ~vtkMultiObjectMassProperties() override;

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

  // Data members supporting API
  vtkTypeBool SkipValidityCheck;
  vtkTypeBool AllValid;
  double TotalVolume;
  double TotalArea;

  // Internal data members supporting algorithm execution
  vtkIdType NumberOfObjects; // number of objects identified
  vtkIdTypeArray* ObjectIds; // for each input polygon, the object id that the polygon is in

  vtkUnsignedCharArray* ObjectValidity; // is it a valid object?
  vtkDoubleArray* ObjectVolumes;        // what is the object volume (if valid)?
  vtkDoubleArray* ObjectAreas;          // what is the total object area?

  vtkIdList* CellNeighbors; // avoid repetitive new/delete
  vtkIdList* Wave;          // processing wave
  vtkIdList* Wave2;

  // Connected traversal to identify objects
  void TraverseAndMark(
    vtkPolyData* output, vtkIdType* objectIds, vtkDataArray* valid, unsigned char* orient);

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

#endif
