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

  Program:   Visualization Toolkit
  Module:    vtkSphereTree.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   vtkSphereTree
 * @brief   class to build and traverse sphere trees
 *
 * vtkSphereTree is a helper class used to build and traverse sphere
 * trees. Various types of trees can be constructed for different VTK
 * dataset types, as well well as different approaches to organize
 * the tree into hierarchies.
 *
 * Typically building a complete sphere tree consists of two parts: 1)
 * creating spheres for each cell in the dataset, then 2) creating an
 * organizing hierarchy. The structure of the hierarchy varies depending on
 * the topological characteristics of the dataset.
 *
 * Once the tree is constructed, various geometric operations are available
 * for quickly selecting cells based on sphere tree operations; for example,
 * process all cells intersecting a plane (i.e., use the sphere tree to identify
 * candidate cells for plane intersection).
 *
 * This class does not necessarily create optimal sphere trees because
 * some of its requirements (fast build time, provide simple reference
 * code, a single bounding sphere per cell, etc.) precludes optimal
 * performance. It is also oriented to computing on cells versus the
 * classic problem of collision detection for polygonal models. For
 * more information you want to read Gareth Bradshaw's PhD thesis
 * "Bounding Volume Hierarchies for Level-of-Detail Collision
 * Handling" which does a nice job of laying out the challenges and
 * important algorithms relative to sphere trees and BVH (bounding
 * volume hierarchies).
 *
 * @sa
 * vtkSphereTreeFilter vtkPlaneCutter
 */

#ifndef vtkSphereTree_h
#define vtkSphereTree_h

#include "vtkCommonExecutionModelModule.h" // For export macro
#include "vtkObject.h"
#include "vtkPlane.h" // to specify the cutting plane

class vtkDoubleArray;
class vtkDataArray;
class vtkIdList;
class vtkDataSet;
class vtkStructuredGrid;
class vtkUnstructuredGrid;
class vtkTimeStamp;
struct vtkSphereTreeHierarchy;

#define VTK_MAX_SPHERE_TREE_RESOLUTION 10
#define VTK_MAX_SPHERE_TREE_LEVELS 20

// VTK Class proper
class VTKCOMMONEXECUTIONMODEL_EXPORT vtkSphereTree : public vtkObject
{
public:
  /**
   * Instantiate the sphere tree.
   */
  static vtkSphereTree* New();

  //@{
  /**
   * Standard type related macros and PrintSelf() method.
   */
  vtkTypeMacro(vtkSphereTree, vtkObject);
  void PrintSelf(ostream& os, vtkIndent indent) override;
  //@}

  //@{
  /**
   * Specify the dataset from which to build the sphere tree.
   */
  virtual void SetDataSet(vtkDataSet*);
  vtkGetObjectMacro(DataSet, vtkDataSet);
  //@}

  //@{
  /**
   * Build the sphere tree (if necessary) from the data set specified. The
   * build time is recorded so the sphere tree will only build if something has
   * changed. An alternative method is available to both set the dataset and
   * then build the sphere tree.
   */
  void Build();
  void Build(vtkDataSet* input);
  //@}

  //@{
  /**
   * Control whether the tree hierarchy is built. If not, then just
   * cell spheres are created (one for each cell).
   */
  vtkSetMacro(BuildHierarchy, bool);
  vtkGetMacro(BuildHierarchy, bool);
  vtkBooleanMacro(BuildHierarchy, bool);
  //@}

  //@{
  /**
   * Methods for cell selection based on a geometric query. Internally
   * different methods are used depending on the dataset type. The array
   * returned is set to non-zero for each cell that intersects the geometric
   * entity. SelectPoint marks all cells with a non-zero value that may
   * contain a point. SelectLine marks all cells that may intersect an
   * infinite line. SelectPlane marks all cells that may intersect with an
   * infinite plane.
   */
  const unsigned char* SelectPoint(double point[3], vtkIdType& numSelected);
  const unsigned char* SelectLine(double origin[3], double ray[3], vtkIdType& numSelected);
  const unsigned char* SelectPlane(double origin[3], double normal[3], vtkIdType& numSelected);
  //@}

  //@{
  /**
   * Methods for cell selection based on a geometric query. Internally
   * different methods are used depending on the dataset type. The method
   * pupulates an vtkIdList with cell ids that may satisfy the geometric
   * query (the user must provide a vtkLdList which the methods fill in).
   * SelectPoint lists all cells with a non-zero value that may contain a
   * point. SelectLine lists all cells that may intersect an infinite
   * line. SelectPlane lists all cells that may intersect with an infinite
   * plane.
   */
  void SelectPoint(double point[3], vtkIdList* cellIds);
  void SelectLine(double origin[3], double ray[3], vtkIdList* cellIds);
  void SelectPlane(double origin[3], double normal[3], vtkIdList* cellIds);
  //@}

  //@{
  /**
   * Sphere tree creation requires gathering spheres into groups. The
   * Resolution variable is a rough guide to the size of each group (the size
   * different meanings depending on the type of data (structured versus
   * unstructured). For example, in 3D structured data, blocks of resolution
   * Resolution^3 are created. By default the Resolution is three.
   */
  vtkSetClampMacro(Resolution, int, 2, VTK_MAX_SPHERE_TREE_RESOLUTION);
  vtkGetMacro(Resolution, int);
  //@}

  //@{
  /**
   * Specify the maximum number of levels for the tree. By default, the
   * number of levels is set to ten. If the number of levels is set to one or
   * less, then no hierarchy is built (i.e., just the spheres for each cell
   * are created). Note that the actual level of the tree may be less than
   * this value depending on the number of cells and Resolution factor.
   */
  vtkSetClampMacro(MaxLevel, int, 1, VTK_MAX_SPHERE_TREE_LEVELS);
  vtkGetMacro(MaxLevel, int);
  //@}

  /**
   * Get the current depth of the sphere tree. This value may change each
   * time the sphere tree is built and the branching factor (i.e.,
   * resolution) changes. Note that after building the sphere tree there are
   * [0,this->NumberOfLevels) defined levels.
   */
  vtkGetMacro(NumberOfLevels, int);

  //@{
  /**
   * Special methods to retrieve the sphere tree data. This is
   * generally used for debugging or with filters like
   * vtkSphereTreeFilter. Both methods return an array of double*
   * where four doubles represent a sphere (center + radius). In the
   * first method a sphere per cell is returned. In the second method
   * the user must also specify a level in the sphere tree (used to
   * retrieve the hierarchy of the tree). Note that null pointers can
   * be returned if the request is not consistent with the state of
   * the sphere tree.
   */
  const double* GetCellSpheres();
  const double* GetTreeSpheres(int level, vtkIdType& numSpheres);
  //@}

protected:
  vtkSphereTree();
  ~vtkSphereTree() override;

  // Data members
  vtkDataSet* DataSet;
  unsigned char* Selected;
  int Resolution;
  int MaxLevel;
  int NumberOfLevels;
  bool BuildHierarchy;

  // The tree and its hierarchy
  vtkDoubleArray* Tree;
  double* TreePtr;
  vtkSphereTreeHierarchy* Hierarchy;

  // Supporting data members
  double AverageRadius;   // average radius of cell sphere
  double SphereBounds[6]; // the dataset bounds computed from cell spheres
  vtkTimeStamp BuildTime; // time at which tree was built

  // Supporting methods
  void BuildTreeSpheres(vtkDataSet* input);
  void ExtractCellIds(const unsigned char* selected, vtkIdList* cellIds, vtkIdType numSelected);

  void BuildTreeHierarchy(vtkDataSet* input);
  void BuildStructuredHierarchy(vtkStructuredGrid* input, double* tree);
  void BuildUnstructuredHierarchy(vtkDataSet* input, double* tree);
  int SphereTreeType; // keep track of the type of tree hierarchy generated

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

#endif
