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

  Program:   Visualization Toolkit
  Module:    vtkOBBTree.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   vtkOBBTree
 * @brief   generate oriented bounding box (OBB) tree
 *
 * vtkOBBTree is an object to generate oriented bounding box (OBB) trees.
 * An oriented bounding box is a bounding box that does not necessarily line
 * up along coordinate axes. The OBB tree is a hierarchical tree structure
 * of such boxes, where deeper levels of OBB confine smaller regions of space.
 *
 * To build the OBB, a recursive, top-down process is used. First, the root OBB
 * is constructed by finding the mean and covariance matrix of the cells (and
 * their points) that define the dataset. The eigenvectors of the covariance
 * matrix are extracted, giving a set of three orthogonal vectors that define
 * the tightest-fitting OBB. To create the two children OBB's, a split plane
 * is found that (approximately) divides the number cells in half. These are
 * then assigned to the children OBB's. This process then continues until
 * the MaxLevel ivar limits the recursion, or no split plane can be found.
 *
 * A good reference for OBB-trees is Gottschalk & Manocha in Proceedings of
 * Siggraph `96.
 *
 * @warning
 * Since this algorithms works from a list of cells, the OBB tree will only
 * bound the "geometry" attached to the cells if the convex hull of the
 * cells bounds the geometry.
 *
 * @warning
 * Long, skinny cells (i.e., cells with poor aspect ratio) may cause
 * unsatisfactory results. This is due to the fact that this is a top-down
 * implementation of the OBB tree, requiring that one or more complete cells
 * are contained in each OBB. This requirement makes it hard to find good
 * split planes during the recursion process. A bottom-up implementation would
 * go a long way to correcting this problem.
 *
 * @sa
 * vtkLocator vtkCellLocator vtkPointLocator
*/

#ifndef vtkOBBTree_h
#define vtkOBBTree_h

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

class vtkMatrix4x4;

// Special class defines node for the OBB tree
//

//
class VTKFILTERSGENERAL_EXPORT vtkOBBNode { //;prevent man page generation
public:
  vtkOBBNode();
  ~vtkOBBNode();

  double Corner[3]; //center point of this node
  double Axes[3][3]; //the axes defining the OBB - ordered from long->short
  vtkOBBNode *Parent; //parent node; nullptr if root
  vtkOBBNode **Kids; //two children of this node; nullptr if leaf
  vtkIdList *Cells; //list of cells in node
  void DebugPrintTree( int level, double *leaf_vol, int *minCells,
                       int *maxCells );

private:
  vtkOBBNode(const vtkOBBNode& other) = delete;
  vtkOBBNode& operator=(const vtkOBBNode& rhs) = delete;
};

//

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

  /**
   * Construct with automatic computation of divisions, averaging
   * 25 cells per octant.
   */
  static vtkOBBTree *New();

  // Re-use any superclass signatures that we don't override.
  using vtkAbstractCellLocator::IntersectWithLine;

  /**
   * Take the passed line segment and intersect it with the data set.
   * This method assumes that the data set is a vtkPolyData that describes
   * a closed surface, and the intersection points that are returned in
   * 'points' alternate between entrance points and exit points.
   * The return value of the function is 0 if no intersections were found,
   * -1 if point 'a0' lies inside the closed surface, or +1 if point 'a0'
   * lies outside the closed surface.
   * Either 'points' or 'cellIds' can be set to nullptr if you don't want
   * to receive that information.
   */
  int IntersectWithLine(const double a0[3], const double a1[3],
                        vtkPoints *points, vtkIdList *cellIds) override;

  /**
   * Return the first intersection of the specified line segment with
   * the OBB tree, as well as information about the cell which the
   * line segment intersected.
   */
  int IntersectWithLine(double a0[3], double a1[3], double tol,
                        double& t, double x[3], double pcoords[3],
                        int &subId, vtkIdType &cellId, vtkGenericCell *cell) override;

  /**
   * Compute an OBB from the list of points given. Return the corner point
   * and the three axes defining the orientation of the OBB. Also return
   * a sorted list of relative "sizes" of axes for comparison purposes.
   */
  static void ComputeOBB(vtkPoints *pts, double corner[3], double max[3],
                  double mid[3], double min[3], double size[3]);

  /**
   * Compute an OBB for the input dataset using the cells in the data.
   * Return the corner point and the three axes defining the orientation
   * of the OBB. Also return a sorted list of relative "sizes" of axes for
   * comparison purposes.
   */
  void ComputeOBB(vtkDataSet *input, double corner[3], double max[3],
                  double mid[3], double min[3], double size[3]);

  /**
   * Determine whether a point is inside or outside the data used to build
   * this OBB tree.  The data must be a closed surface vtkPolyData data set.
   * The return value is +1 if outside, -1 if inside, and 0 if undecided.
   */
  int InsideOrOutside(const double point[3]);

  /**
   * Returns true if nodeB and nodeA are disjoint after optional
   * transformation of nodeB with matrix XformBtoA
   */
  int DisjointOBBNodes( vtkOBBNode *nodeA, vtkOBBNode *nodeB,
                        vtkMatrix4x4 *XformBtoA );

  /**
   * Returns true if line intersects node.
   */
  int LineIntersectsNode( vtkOBBNode *pA, const double b0[3], const double b1[3] );

  /**
   * Returns true if triangle (optionally transformed) intersects node.
   */
  int TriangleIntersectsNode( vtkOBBNode *pA,
                              double p0[3], double p1[3],
                              double p2[3], vtkMatrix4x4 *XformBtoA );

  /**
   * For each intersecting leaf node pair, call function.
   * OBBTreeB is optionally transformed by XformBtoA before testing.
   */
  int IntersectWithOBBTree( vtkOBBTree *OBBTreeB, vtkMatrix4x4 *XformBtoA,
                            int(*function)( vtkOBBNode *nodeA,
                                            vtkOBBNode *nodeB,
                                            vtkMatrix4x4 *Xform,
                                            void *arg ),
                            void *data_arg );

  //@{
  /**
   * Satisfy locator's abstract interface, see vtkLocator.
   */
  void FreeSearchStructure() override;
  void BuildLocator() override;
  //@}

  /**
   * Create polygonal representation for OBB tree at specified level. If
   * level < 0, then the leaf OBB nodes will be gathered. The aspect ratio (ar)
   * and line diameter (d) are used to control the building of the
   * representation. If a OBB node edge ratio's are greater than ar, then the
   * dimension of the OBB is collapsed (OBB->plane->line). A "line" OBB will be
   * represented either as two crossed polygons, or as a line, depending on
   * the relative diameter of the OBB compared to the diameter (d).
   */
  void GenerateRepresentation(int level, vtkPolyData *pd) override;

protected:
  vtkOBBTree();
  ~vtkOBBTree() override;

  // Compute an OBB from the list of cells given.  This used to be
  // public but should not have been.  A public call has been added
  // so that the functionality can be accessed.
  void ComputeOBB(vtkIdList *cells, double corner[3], double max[3],
                       double mid[3], double min[3], double size[3]);

  vtkOBBNode *Tree;
  void BuildTree(vtkIdList *cells, vtkOBBNode *parent, int level);
  vtkPoints *PointsList;
  int *InsertedPoints;
  int OBBCount;

  void DeleteTree(vtkOBBNode *OBBptr);
  void GeneratePolygons(vtkOBBNode *OBBptr, int level, int repLevel,
                        vtkPoints* pts, vtkCellArray *polys);

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

#endif
