Listing 1

/***********************************************************
 *  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  */
}