
/***********************************************************
 *  Node-Positioning for General Trees, by John Q. Walker II
 *
 *  Initiated by calling procedure TreePosition().
 **********************************************************/

#include <stdlib.h>

/*----------------------------------------------------------
 * Implementation dependent: Set the values for each of
 * these variables.
 *--------------------------------------------------------*/
#define NODE_WIDTH          20  /* Width of a node?       */
#define NODE_HEIGHT         10  /* Height of a node?      */
#define FRAME_THICKNESS     1   /* Fixed-sized node frame?*/
#define SUBTREE_SEPARATION  5   /* Gap between subtrees?  */
#define SIBLING_SEPARATION  4   /* Gap between siblings?  */
#define LEVEL_SEPARATION    5   /* Gap between levels?    */
#define MAXIMUM_DEPTH       10  /* Biggest tree?          */

/*----------------------------------------------------------
 * Implementation dependent: The structure for one node
 * - The first 4 pointers must be set for each node before
 *   this algorithm is called.
 * - The X and Y coordinates must be set for only the apex
 *   of the tree upon entry; they will be set by this
 *   algorithm for all the other nodes.
 * - The next three elements are used only for the duration
 *   of the algorithm.
 * - Actual contents of the node depend on your application.
 *--------------------------------------------------------*/
typedef int COORD;              /* X,Y coordinate type    */

typedef struct node {
    struct node *parent;        /* ptr: node's parent     */
    struct node *offspring;     /* ptr: leftmost child    */
    struct node *leftsibling;   /* ptr: sibling on left   */
    struct node *rightsibling;  /* ptr: sibling on right  */
    COORD  xCoordinate;         /* node's current x coord */
    COORD  yCoordinate;         /* node's current y coord */

    struct node *prev;          /* ptr: lefthand neighbor */
    float  flPrelim;            /* preliminary x coord    */
    float  flModifier;          /* temporary modifier     */

    char   info[80];            /* pick your contents!    */
} *PNODE;                       /* ptr: a node structure  */

/*----------------------------------------------------------
 * Global variables used by the algorithm
 *--------------------------------------------------------*/
typedef enum { FALSE, TRUE } BOOL;
typedef enum { FRAME, NO_FRAME } FRAME_TYPE;
typedef enum { NORTH, SOUTH, EAST, WEST } ROOT_ORIENTATION;

typedef struct prev_node {      /* one list element       */
    PNODE pPreviousNode;        /* ptr: previous at level */
    struct prev_node *pNextLevel;   /* ptr: next element  */
} *PPREVIOUS_NODE;

static FRAME_TYPE FrameType = FRAME;    /* Show a frame   */
static ROOT_ORIENTATION RootOrientation = NORTH; /* At top*/
static PPREVIOUS_NODE pLevelZero = (PPREVIOUS_NODE)0;

static COORD xTopAdjustment;    /* How to adjust the apex */
static COORD yTopAdjustment;    /* How to adjust the apex */
static float flMeanWidth;       /* Ave. width of 2 nodes  */

#define FIRST_TIME  (0)         /* recursive proc flag    */

/*----------------------------------------------------------
 * Implemented as macros, but could be implemented as
 * procedures depending on your particular node structures
 *--------------------------------------------------------*/
#define FirstChild(node)     ((PNODE)((node)->offspring))
#define LeftSibling(node)    ((PNODE)((node)->leftsibling))
#define RightSibling(node)   ((PNODE)((node)->rightsibling))
#define Parent(node)         ((PNODE)((node)->parent))
#define LeftNeighbor(node)   ((PNODE)((node)->prev))
#define IsLeaf(node)          \
               (((node)&&(!((node)->offspring)))?TRUE:FALSE)
#define HasChild(node)        \
                  (((node)&&((node)->offspring))?TRUE:FALSE)
#define HasLeftSibling(node)  \
                (((node)&&((node)->leftsibling))?TRUE:FALSE)
#define HasRightSibling(node) \
               (((node)&&((node)->rightsibling))?TRUE:FALSE)


static PNODE GetPrevNodeAtLevel (unsigned nLevelNbr)
{
    /*------------------------------------------------------
     * List Manipulation: Return pointer to previous node at
     * this level
     *----------------------------------------------------*/
    PPREVIOUS_NODE pTempNode;   /* used in the for-loop   */
    unsigned  i = 0;            /* level counter          */

    for (pTempNode = pLevelZero; (pTempNode);
         pTempNode = pTempNode->pNextLevel) {
        if (i++ == nLevelNbr)
            /* Reached desired level.  Return its pointer */
            return (pTempNode->pPreviousNode);
    }
    return ((PNODE)0);  /* No pointer yet for this level. */
}

static BOOL SetPrevNodeAtLevel (unsigned nLevelNbr,
                                PNODE pThisNode)
{
    /*------------------------------------------------------
     * List Manipulation: Set the list element to the
     * previous node at this level
     *----------------------------------------------------*/

    PPREVIOUS_NODE pTempNode;   /* used in the for-loop   */
    PPREVIOUS_NODE pNewNode;    /* newly-allocated memory */
    unsigned  i = 0;            /* level counter          */

    for (pTempNode = pLevelZero; (pTempNode);
         pTempNode = pTempNode->pNextLevel) {
        if (i++ == nLevelNbr) {
            /* Reached desired level.  Return its pointer */
            pTempNode->pPreviousNode = pThisNode;
            return (TRUE);
        }
        else if (pTempNode->pNextLevel==(PPREVIOUS_NODE)0) {
            /* Looks like we need a new level: add it.    */
            /* We need to keep going--should be the next. */
            pNewNode = (PPREVIOUS_NODE)
                       malloc(sizeof(struct prev_node));
            if (pNewNode) {
                pNewNode->pPreviousNode = (PNODE)0;
                pNewNode->pNextLevel = (PPREVIOUS_NODE)0;
                pTempNode->pNextLevel = pNewNode;
            }
            else return (FALSE);    /* The malloc failed. */
        }
    }

    /* Should only get here if pLevelZero is 0.           */
    pLevelZero = (PPREVIOUS_NODE)
                 malloc(sizeof(struct prev_node));
    if (pLevelZero) {
        pLevelZero->pPreviousNode = pThisNode;
        pLevelZero->pNextLevel = (PPREVIOUS_NODE)0;
        return (TRUE);
    }
    else return (FALSE);        /* The malloc() failed.   */
}

static void InitPrevNodeAtLevel (void)
{
    /*------------------------------------------------------
     * List Manipulation: Initialize the list of the
     * previous node at each level to all zeros.
     *----------------------------------------------------*/

    PPREVIOUS_NODE pTempNode = pLevelZero;  /* the start  */

    for ( ; (pTempNode); pTempNode = pTempNode->pNextLevel)
        pTempNode->pPreviousNode = (PNODE)0;
}

static BOOL CheckExtentsRange(long lxTemp, long lyTemp)
{
    /*------------------------------------------------------
     * Insert your own code here, to check when the
     * tree drawing is going to be too big.  My region is no
     * more that 64000 units square.
     *----------------------------------------------------*/

    if ((labs(lxTemp) > 32000) || (labs(lyTemp) > 32000))
        return (FALSE);
    else
        return (TRUE);
}

static void TreeMeanNodeSize (PNODE pLeftNode,
                              PNODE pRightNode)
{
    /*------------------------------------------------------
     * Write your own code for this procedure if your
     * rendered nodes will have variable sizes.
     *------------------------------------------------------
     * Here I add the width of the contents of the
     * right half of the pLeftNode to the left half of the
     * right node.  Since the size of the contents for all
     * nodes is currently the same, this module computes the
     * following trivial computation.
     *----------------------------------------------------*/

    flMeanWidth = (float)0.0;   /* Initialize this global */

    switch (RootOrientation) {
        case NORTH:
        case SOUTH:
            if (pLeftNode) {
                flMeanWidth = flMeanWidth +
                              (float)(NODE_WIDTH/2);
                if (FrameType != NO_FRAME)
                    flMeanWidth = flMeanWidth +
                                  (float)FRAME_THICKNESS;
            }
            if (pRightNode) {
                flMeanWidth = flMeanWidth +
                              (float)(NODE_WIDTH/2);
                if (FrameType != NO_FRAME)
                    flMeanWidth = flMeanWidth +
                                  (float)FRAME_THICKNESS;
            }
            break;
        case EAST :
        case WEST :
            if (pLeftNode) {
                flMeanWidth = flMeanWidth +
                              (float)(NODE_HEIGHT/2);
                if (FrameType != NO_FRAME)
                    flMeanWidth = flMeanWidth +
                                  (float)FRAME_THICKNESS;
            }
            if (pRightNode) {
                flMeanWidth = flMeanWidth +
                              (float)(NODE_HEIGHT/2);
                if (FrameType != NO_FRAME)
                    flMeanWidth = flMeanWidth +
                                  (float)FRAME_THICKNESS;
            }
            break;
    }
}

static PNODE TreeGetLeftmost(PNODE pThisNode,
                             unsigned nCurrentLevel,
                             unsigned nSearchDepth)
{
    /*------------------------------------------------------
     * Determine the leftmost descendant of a node at a
     * given depth.  This is implemented using a post-order
     * walk of the subtree under pThisNode, down to the
     * level of nSearchDepth.  If we've searched to the
     * proper distance, return the currently leftmost node.
     * Otherwise, recursively look at the progressively
     * lower levels.
     *----------------------------------------------------*/

    PNODE pLeftmost;    /* leftmost descendant at level   */
    PNODE pRightmost;   /* rightmost offspring in search  */

    if (nCurrentLevel == nSearchDepth)
        pLeftmost = pThisNode;  /* searched far enough.   */
    else if (IsLeaf(pThisNode))
        pLeftmost = 0;  /* This node has no descendants   */
    else {  /* Do a post-order walk of the subtree.       */
        for (pLeftmost = TreeGetLeftmost(pRightmost =
                                FirstChild(pThisNode),
                                nCurrentLevel + 1,
                                nSearchDepth)
            ;
            (pLeftmost==0) && (HasRightSibling(pRightmost))
            ;
            pLeftmost = TreeGetLeftmost(pRightmost =
                                RightSibling(pRightmost),
                                nCurrentLevel + 1,
                                nSearchDepth)
           ) { /* Nothing inside this for-loop. */ }
    }
    return (pLeftmost);
}

static void TreeApportion (PNODE pThisNode,
                           unsigned nCurrentLevel)
{
    /*------------------------------------------------------
     * Clean up the positioning of small sibling subtrees.
     * Subtrees of a node are formed independently and
     * placed as close together as possible.  By requiring
     * that the subtrees be rigid at the time they are put
     * together, we avoid the undesirable effects that can
     * accrue from positioning nodes rather than subtrees.
     *----------------------------------------------------*/

    PNODE pLeftmost;            /* leftmost at given level*/
    PNODE pNeighbor;            /* node left of pLeftmost */
    PNODE pAncestorLeftmost;    /* ancestor of pLeftmost  */
    PNODE pAncestorNeighbor;    /* ancestor of pNeighbor  */
    PNODE pTempPtr;             /* loop control pointer   */
    unsigned i;                 /* loop control           */
    unsigned nCompareDepth;     /* depth of comparison    */
                                /* within this proc       */
    unsigned nDepthToStop;      /* depth to halt          */
    unsigned nLeftSiblings;     /* nbr of siblings to the */
             /* left of pThisNode, including pThisNode,   */
             /* til the ancestor of pNeighbor             */
    float flLeftModsum;         /* sum of ancestral mods  */
    float flRightModsum;        /* sum of ancestral mods  */
    float flDistance;           /* difference between     */
          /* where pNeighbor thinks pLeftmost should be   */
          /* and where pLeftmost actually is              */
    float flPortion;            /* proportion of          */
          /* flDistance to be added to each sibling       */

    pLeftmost = FirstChild(pThisNode);
    pNeighbor = LeftNeighbor(pLeftmost);

    nCompareDepth = 1;
    nDepthToStop = MAXIMUM_DEPTH - nCurrentLevel;

    while ((pLeftmost) && (pNeighbor) &&
           (nCompareDepth <= nDepthToStop)) {

        /* Compute the location of pLeftmost and where it */
        /* should be with respect to pNeighbor.           */
        flRightModsum = flLeftModsum = (float)0.0;
        pAncestorLeftmost = pLeftmost;
        pAncestorNeighbor = pNeighbor;
        for (i = 0; (i < nCompareDepth); i++) {
            pAncestorLeftmost = Parent(pAncestorLeftmost);
            pAncestorNeighbor = Parent(pAncestorNeighbor);
            flRightModsum = flRightModsum +
                            pAncestorLeftmost->flModifier;
            flLeftModsum  = flLeftModsum  +
                            pAncestorNeighbor->flModifier;
        }

        /* Determine the flDistance to be moved, and apply*/
        /* it to "pThisNode's" subtree.  Apply appropriate*/
        /* portions to smaller interior subtrees          */

        /* Set the global mean width of these two nodes   */
        TreeMeanNodeSize(pLeftmost, pNeighbor);

        flDistance = (pNeighbor->flPrelim +
                      flLeftModsum +
                      (float)SUBTREE_SEPARATION +
                      (float)flMeanWidth) -
                     (pLeftmost->flPrelim + flRightModsum);

        if (flDistance > (float)0.0) {
            /* Count the interior sibling subtrees        */
            nLeftSiblings = 0;
            for (pTempPtr = pThisNode;
                  (pTempPtr) &&
                  (pTempPtr != pAncestorNeighbor);
                 pTempPtr = LeftSibling(pTempPtr)) {
                nLeftSiblings++;
            }

            if (pTempPtr) {
                /* Apply portions to appropriate          */
                /* leftsibling subtrees.                  */
                flPortion = flDistance/(float)nLeftSiblings;
                for (pTempPtr = pThisNode;
                     (pTempPtr != pAncestorNeighbor);
                     pTempPtr = LeftSibling(pTempPtr)) {
                    pTempPtr->flPrelim =
                         pTempPtr->flPrelim + flDistance;
                    pTempPtr->flModifier =
                         pTempPtr->flModifier + flDistance;
                    flDistance = flDistance - flPortion;
                }
            }
            else {
                /* Don't need to move anything--it needs  */
                /* to be done by an ancestor because      */
                /* pAncestorNeighbor and                  */
                /* pAncestorLeftmost are not siblings of  */
                /* each other.                            */
                return;
            }
        }   /* end of the while                           */

        /* Determine the leftmost descendant of pThisNode */
        /* at the next lower level to compare its         */
        /* positioning against that of its pNeighbor.     */
        nCompareDepth++;
        if (IsLeaf(pLeftmost))
            pLeftmost = TreeGetLeftmost(pThisNode, 0,
                                         nCompareDepth);
        else
            pLeftmost = FirstChild(pLeftmost);
        pNeighbor = LeftNeighbor(pLeftmost);
    }
}

static BOOL TreeFirstWalk(PNODE pThisNode,
                          unsigned nCurrentLevel)
{
    /*------------------------------------------------------
     * In a first post-order walk, every node of the tree is
     * assigned a preliminary x-coordinate (held in field
     * node->flPrelim).  In addition, internal nodes are
     * given modifiers, which will be used to move their
     * children to the right (held in field
     * node->flModifier).
     * Returns: TRUE if no errors, otherwise returns FALSE.
     *----------------------------------------------------*/

    PNODE pLeftmost;            /* left- & rightmost      */
    PNODE pRightmost;           /* children of a node.    */
    float flMidpoint;           /* midpoint between left- */
                                /* & rightmost children   */

    /* Set up the pointer to previous node at this level  */
    pThisNode->prev = GetPrevNodeAtLevel(nCurrentLevel);

    /* Now we're it--the previous node at this level      */
    if (!(SetPrevNodeAtLevel(nCurrentLevel, pThisNode)))
        return (FALSE);        /* Can't allocate element  */

    /* Clean up old values in a node's flModifier         */
    pThisNode->flModifier = (float)0.0;

    if ((IsLeaf(pThisNode)) ||
        (nCurrentLevel == MAXIMUM_DEPTH)) {
        if (HasLeftSibling(pThisNode)) {
            /*---------------------------------------------
             * Determine the preliminary x-coordinate
             *   based on:
             * - preliminary x-coordinate of left sibling,
             * - the separation between sibling nodes, and
             * - mean width of left sibling & current node.
             *--------------------------------------------*/
            /* Set the mean width of these two nodes      */
            TreeMeanNodeSize(LeftSibling(pThisNode),
                             pThisNode);

            pThisNode->flPrelim =
                        (pThisNode->leftsibling->flPrelim) +
                        (float)SIBLING_SEPARATION +
                        flMeanWidth;
        }
        else    /* no sibling on the left to worry about  */
            pThisNode->flPrelim = (float)0.0;
    }
    else {
        /* Position the leftmost of the children          */
        if (TreeFirstWalk(pLeftmost = pRightmost =
                          FirstChild(pThisNode),
                          nCurrentLevel + 1)) {
            /* Position each of its siblings to its right */
            while (HasRightSibling(pRightmost)) {
                if (TreeFirstWalk(pRightmost =
                                  RightSibling(pRightmost),
                                  nCurrentLevel + 1)) {
                }
                else return (FALSE); /* malloc() failed   */
            }

            /* Calculate the preliminary value between    */
            /* the children at the far left and right     */
            flMidpoint = (pLeftmost->flPrelim +
                          pRightmost->flPrelim)/(2.0);

            /* Set global mean width of these two nodes   */
            TreeMeanNodeSize(LeftSibling(pThisNode),
                             pThisNode);

            if (HasLeftSibling(pThisNode)) {
                pThisNode->flPrelim =
                       (pThisNode->leftsibling->flPrelim) +
                                 (float)SIBLING_SEPARATION +
                                 flMeanWidth;

                pThisNode->flModifier =
                     pThisNode->flPrelim - flMidpoint;
                TreeApportion(pThisNode, nCurrentLevel);
            }
            else pThisNode->flPrelim = flMidpoint;
        }
        else return (FALSE); /* Couldn't get an element   */
    }
    return (TRUE);
}

static BOOL TreeSecondWalk(PNODE pThisNode,
                           unsigned nCurrentLevel)
{
    /*------------------------------------------------------
     * During a second pre-order walk, each node is given a
     * final x-coordinate by summing its preliminary
     * x-coordinate and the modifiers of all the node's
     * ancestors.  The y-coordinate depends on the height of
     * the tree.  (The roles of x and y are reversed for
     * RootOrientations of EAST or WEST.)
     * Returns: TRUE if no errors, otherwise returns FALSE.
     *----------------------------------------------------*/

    BOOL  bResult = TRUE;       /* assume innocent        */
    long  lxTemp, lyTemp;       /* hold calculations here */
    float flNewModsum;          /* local modifier value   */
    static float flModsum = (float)0.0;

    if (nCurrentLevel <= MAXIMUM_DEPTH) {
        flNewModsum = flModsum; /* Save the current value */
        switch (RootOrientation) {
            case NORTH:
                lxTemp = (long)xTopAdjustment +
                  (long)(pThisNode->flPrelim + flModsum);
                lyTemp = (long)yTopAdjustment +
                  (long)(nCurrentLevel * LEVEL_SEPARATION);
                break;
            case SOUTH:
                lxTemp = (long)xTopAdjustment +
                  (long)(pThisNode->flPrelim + flModsum);
                lyTemp = (long)yTopAdjustment -
                  (long)(nCurrentLevel * LEVEL_SEPARATION);
                break;
            case EAST:
                lxTemp = (long)xTopAdjustment +
                  (long)(nCurrentLevel * LEVEL_SEPARATION);
                lyTemp = (long)yTopAdjustment -
                  (long)(pThisNode->flPrelim + flModsum);
                break;
            case WEST:
                lxTemp = (long)xTopAdjustment -
                  (long)(nCurrentLevel * LEVEL_SEPARATION);
                lyTemp = (long)yTopAdjustment -
                  (long)(pThisNode->flPrelim + flModsum);
                break;
        }

        if (CheckExtentsRange(lxTemp, lyTemp)) {
            /* The values are within the allowable range  */
            pThisNode->xCoordinate = (COORD)lxTemp;
            pThisNode->yCoordinate = (COORD)lyTemp;

            if (HasChild(pThisNode)) {
                /* Apply the flModifier value for this    */
                /* node to all its offspring.             */
                flModsum = flNewModsum =
                        flNewModsum + pThisNode->flModifier;
                bResult = TreeSecondWalk(
                   FirstChild(pThisNode),nCurrentLevel + 1);
                flNewModsum = flNewModsum -
                              pThisNode->flModifier;
            }

            if ((HasRightSibling(pThisNode)) && (bResult)) {
                flModsum = flNewModsum;
                bResult = TreeSecondWalk(
                    RightSibling(pThisNode), nCurrentLevel);
            }
        }
        else bResult = FALSE;   /* outside of extents     */
    }
    return (bResult);
}

static BOOL TreePosition(PNODE pApexNode)
{
    /*------------------------------------------------------
     * Determine the coordinates for each node in a tree.
     * Input: Pointer to the apex node of the tree
     * Assumption: The x & y coordinates of the apex node
     * are already correct, since the tree underneath it
     * will be positioned with respect to those coordinates.
     * Returns: TRUE if no errors, otherwise returns FALSE.
     *----------------------------------------------------*/

    if (pApexNode) {
        /* Initialize list of previous node at each level */
        InitPrevNodeAtLevel();

        /* Generate the properly-placed tree nodes.       */
        /* TreeFirstWalk: a post-order walk               */
        /* TreeSecondWalk: a pre-order walk               */
        if (TreeFirstWalk (pApexNode, FIRST_TIME)) {
            /* Determine how to adjust the nodes with     */
            /* respect to the location of the apex of the */
            /* tree being positioned.                     */
            switch (RootOrientation) {
                case NORTH:
                case SOUTH:
                   /* Create the adjustment from x-coord  */
                   xTopAdjustment = pApexNode->xCoordinate -
                            (COORD)(pApexNode->flPrelim);
                   yTopAdjustment = pApexNode->yCoordinate;
                   break;
                case EAST :
                case WEST :
                   /* Create the adjustment from y-coord  */
                   xTopAdjustment = pApexNode->xCoordinate;
                   yTopAdjustment = pApexNode->yCoordinate +
                            (COORD)(pApexNode->flPrelim);
                   break;
            }
            return (TreeSecondWalk(pApexNode, FIRST_TIME));
        }
        else return (FALSE); /* Couldn't get an element   */
    }
    else return (TRUE); /* Easy: null pointer was passed  */
}
