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

  Program:   Visualization Toolkit
  Module:    vtkCosmicTreeLayoutStrategy.h

=========================================================================*/
/*----------------------------------------------------------------------------
 Copyright (c) Sandia Corporation
 See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.
----------------------------------------------------------------------------*/
/**
 * @class   vtkCosmicTreeLayoutStrategy
 * @brief   tree layout strategy reminiscent of astronomical systems
 *
 *
 * This layout strategy takes an input tree and places all the children of a
 * node into a containing circle. The placement is such that each child
 * placed can be represented with a circle tangent to the containing circle
 * and (usually) 2 other children. The interior of the circle is left empty
 * so that graph edges drawn on top of the tree will not obfuscate the tree.
 * However, when one child is much larger than all the others, it may
 * encroach on the center of the containing circle; that's OK, because it's
 * large enough not to be obscured by edges drawn atop it.
 *
 * @par Thanks:
 * Thanks to the galaxy and David Thompson hierarchically nested inside it
 * for inspiring this layout strategy.
*/

#ifndef vtkCosmicTreeLayoutStrategy_h
#define vtkCosmicTreeLayoutStrategy_h

#include "vtkInfovisLayoutModule.h" // For export macro
#include "vtkGraphLayoutStrategy.h"

class vtkDoubleArray;
class vtkDataArray;
class vtkPoints;
class vtkTree;

class VTKINFOVISLAYOUT_EXPORT vtkCosmicTreeLayoutStrategy : public vtkGraphLayoutStrategy
{
public:
  static vtkCosmicTreeLayoutStrategy* New();
  void PrintSelf( ostream& os, vtkIndent indent ) override;
  vtkTypeMacro(vtkCosmicTreeLayoutStrategy,vtkGraphLayoutStrategy);

  /**
   * Perform the layout.
   */
  void Layout() override;

  //@{
  /**
   * Should node size specifications be obeyed at leaf nodes only or
   * (with scaling as required to meet constraints) at every node in
   * the tree?
   * This defaults to true, so that leaf nodes are scaled according to
   * the size specification provided, and the parent node sizes are
   * calculated by the algorithm.
   */
  vtkSetMacro(SizeLeafNodesOnly,int);
  vtkGetMacro(SizeLeafNodesOnly,int);
  vtkBooleanMacro(SizeLeafNodesOnly,int);
  //@}

  //@{
  /**
   * How many levels of the tree should be laid out?
   * For large trees, you may wish to set the root and maximum depth
   * in order to retrieve the layout for the visible portion of the tree.
   * When this value is zero or negative, all nodes below and including
   * the LayoutRoot will be presented.
   * This defaults to 0.
   */
  vtkSetMacro(LayoutDepth,int);
  vtkGetMacro(LayoutDepth,int);
  //@}

  //@{
  /**
   * What is the top-most tree node to lay out?
   * This node will become the largest containing circle in the layout.
   * Use this in combination with SetLayoutDepth to retrieve the
   * layout of a subtree of interest for rendering.
   * Setting LayoutRoot to a negative number signals that the root node
   * of the tree should be used as the root node of the layout.
   * This defaults to -1.
   */
  vtkSetMacro(LayoutRoot,vtkIdType);
  vtkGetMacro(LayoutRoot,vtkIdType);
  //@}

  //@{
  /**
   * Set the array to be used for sizing nodes.
   * If this is set to an empty string or nullptr (the default),
   * then all leaf nodes (or all nodes, when SizeLeafNodesOnly is false)
   * will be assigned a unit size.
   */
  vtkSetStringMacro(NodeSizeArrayName);
  vtkGetStringMacro(NodeSizeArrayName);
  //@}

protected:

  /// How are node sizes specified?
  enum RadiusMode
  {
    NONE,     //!< No node sizes specified... unit radius is assumed.
    LEAVES,   //!< Only leaf node sizes specified... parents are calculated during layout.
    ALL       //!< All node sizes specified (overconstrained, so a scale factor for each parent is calculated during layout).
  };

  vtkCosmicTreeLayoutStrategy();
  ~vtkCosmicTreeLayoutStrategy() override;

  /**
   * Recursive routine used to lay out tree nodes. Called from Layout().
   */
  void LayoutChildren(
    vtkTree* tree, vtkPoints* newPoints, vtkDoubleArray* radii, vtkDoubleArray* scale,
    vtkIdType root, int depth, RadiusMode mode );

  /**
   * Recursive routine that adds each parent node's (x,y) position to its children.
   * This must be done only after all the children have been laid out at the origin
   * since we will not know the parent's position until after the child radii have
   * been determined.
   */
  void OffsetChildren(
    vtkTree* tree, vtkPoints* pts, vtkDoubleArray* radii, vtkDoubleArray* scale,
    double parent[4], vtkIdType root, int depth, RadiusMode mode );

  /**
   * Create an array to hold radii, named appropriately (depends on \a NodeSizeArrayName)
   * and initialized to either (a) -1.0 for each node or (b) a deep copy of an existing array.
   * @param numVertices  The number of vertices on the tree.
   * @param initialValue The starting value of each node's radius. Only used when \a inputRadii is nullptr.
   * @param inputRadii   Either nullptr or the address of another array to be copied into the output array
   * @retval             The array of node radii to be set on the output
   */
  vtkDoubleArray* CreateRadii( vtkIdType numVertices, double initialValue, vtkDataArray* inputRadii );

  /**
   * Create an array to hold scale factors, named appropriately (depends on \a NodeSizeArrayName)
   * and initialized to -1.0.
   * @param numVertices The number of vertices on the tree.
   * @retval            The array of node scale factors to be set on the output
   */
  vtkDoubleArray* CreateScaleFactors( vtkIdType numVertices );

  int SizeLeafNodesOnly;
  int LayoutDepth;
  vtkIdType LayoutRoot;
  char* NodeSizeArrayName;

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

#endif // vtkCosmicTreeLayoutStrategy_h
