/* Author: Anthony Bloesch Title: generate.c Copyright (C) Anthony Bloesch 1993 Version: 1.0 Purpose: Take a .tlo file generated by treetex.sty and generate the corresponding .tli file. That is, an aesthetic layout of the tree specified in the .tlo file. A .tlo is expected to be of the form: TLO <- ROOT_POS (ROOT_SPEC | NODE_SPEC) {ROOT_SPEC | NODE_SPEC} ROOT_POS <- t|l ROOT_SPEC <- NODE_NAME WIDTH HEIGHT NODE_SPEC <- NODE_NAME PARENT_NAME WIDTH HEIGHT DEPTH NODE_NAME <- NAME PARENT_NAME <- NAME WIDTH <- PT_SIZE HEIGHT <- PT_SIZE DEPTH <- PT_SIZE NAME <- LETTER{LETTER} LETTER <- A|B|...|Z|a|b|...|z PT_SIZE <- NUMBER[.[NUMBER]]pt NUMBER <- DIGIT{DIGIT} DIGIT <- 0|1|...|9 For example: t 10.0pt 10.0pt root 42.2501pt 6.94444pt 0.0pt nodeA root 59.75012pt 6.94444pt 0.0pt nodeB root 66.44458pt 6.94444pt 1.94444pt nodeC nodeA 52.5001pt 6.94444pt 0.0pt */ #include #include #include #include "layout.h" #define MAX_NAME_SIZE 100 /* Maximum length of input strings. */ #define SCALE 1 /* Rasters per printer's pt (pt). */ typedef enum {eTop, eLeft} RootPos; /* Position of the root of a tree. */ static TreeNode *findNode(char *name, TreeNode **nodes, unsigned nrNodes); /* Return a pointer to the first node named in where there are nodes. If no such node exists return NULL. Pre: Post: name in nodes -> findNode'.name = name & name notin nodes -> findNode' = NULL */ static void printTree(FILE *outfile, Tree tree, unsigned height, unsigned leftWidth, unsigned rightWidth, RootPos rootPos); /* Output to as a LaTeX picture. Assume that the tree is high and has widths , centered on the top center of the root node of . The tree will be layed out so that the root will be at (i.e. rotate it if = eLeft). Pre: open(outfile) & tree != NULL Post: */ static void readTree(FILE *infile, Tree *tree, double *xSeparation, double *ySeparation, RootPos *rootPos); /* Read in the tree from and also return the desired x & y seperations and , and the desired position of the tree's root . Pre: open(infile) Post: */ int main(int argc, char **argv) { FILE *infile; char infilename[MAX_NAME_SIZE]; FILE *outfile; char outfilename[MAX_NAME_SIZE]; RootPos rootPos; Tree tree; unsigned treeHeight; unsigned treeLeftWidth; /* Width to the left of the root node. */ unsigned treeRightWidth; /* Width to the right of the root node. */ double xSeparation; /* Min x distance between nodes. */ double ySeparation; /* Min y distance between nodes. */ /* Construct filenames adding ".tlo" or ".tli" if necessary. */ if (argc == 2 && (strlen(argv[1]) <= 4 || strcmp(".tlo", &(argv[1][strlen(argv[1])-4])))) { /* No .tlo suffix. */ if (MAX_NAME_SIZE <= strlen(argv[1]) + 5) { (void)fprintf(stderr, "Filename too long.\n"); exit(1); } /* if */ (void)strcpy(infilename, argv[1]); (void)strcat(infilename, ".tlo"); (void)strcpy(outfilename, argv[1]); (void)strcat(outfilename, ".tli"); } else { /* Has .tlo suffix. */ (void)strcpy(infilename, argv[1]); (void)strcpy(outfilename, argv[1]); outfilename[strlen(argv[1])-1] = 'i'; } /* if */ /* Open input file. */ if (argc != 2 || NULL == (infile = fopen(infilename, "r"))) { (void)fprintf(stderr, "Usage: %s filename[.tlo]\n", argv[0]); exit(1); } /* if */ readTree(infile, &tree, &xSeparation, &ySeparation, &rootPos); shapeTree(tree, &treeHeight, &treeLeftWidth, &treeRightWidth, (unsigned)xSeparation, (unsigned)ySeparation); /* Open output file. */ if (NULL == (outfile = fopen(outfilename, "w"))) { (void)fprintf(stderr, "Unable to open %s for writing.\n", outfilename); exit(1); } /* if */ printTree(outfile, tree, treeHeight, treeLeftWidth, treeRightWidth, rootPos); deleteTree(tree); return 0; } /* main */ static TreeNode *findNode(char *name, TreeNode **nodes, unsigned nrNodes) { unsigned i; for (i = 0; i < nrNodes; i++) if (!strcmp(name, nodes[i]->name)) return nodes[i]; return NULL; } /* findNode */ static void printNode(FILE *outfile, TreeNode *node, int rootX, RootPos rootPos) /* Output to as part of a LaTeX picture. Assume that the top of the node is to be centered at . Position the node to be consistent with a tree root at (i.e. rotate it if = eLeft). Pre: open(outfile) & node != NULL Post: */ { switch (rootPos) { case eTop: (void)fprintf(outfile, " \\put(%d,%d){\\shownode{\\%s}}\n", rootX + node->x - (int)node->width/2, -(node->y + (int)node->height - node->depth), node->name); break; case eLeft: (void)fprintf(outfile, " \\put(%d,%d){\\shownode{\\%s}}\n", node->y, rootX + node->x - (int)node->width/2 + node->depth, node->name); break; } /* switch */ } /* printNode */ static void printLine(FILE *outfile, int x, int y, int deltaX, int deltaY, unsigned length, RootPos rootPos) /* Print a line from (, ) in the direction (, ) of length . Position the line to be consistent with a tree root at (i.e. rotate it if = eLeft). Pre: deltaX in {-1, 0, 1} & deltaY in {-1, 0, 1} Post: */ { switch (rootPos) { case eTop: (void)fprintf(outfile, " \\put(%d,%d){\\line(%d,%d){%d}}\n", x, y, deltaX, deltaY, length); break; case eLeft: (void)fprintf(outfile, " \\put(%d,%d){\\line(%d,%d){%d}}\n", -y, x, -deltaY, deltaX, length); break; } /* switch */ } /* printLine */ static void doPrintTree(FILE *outfile, Tree tree, int rootX, RootPos rootPos) /* Output to as part of a LaTeX picture. Assume that the top of the node is to be centered at . Position the tree to be consistent with a tree root at (i.e. rotate it if = eLeft). Pre: open(outfile) & tree != NULL Post: */ { unsigned i; printNode(outfile, tree, rootX, rootPos); if (tree->nrBranches == 1) { /* Down line. */ printLine(outfile, rootX + tree->x, -(tree->y + (int)tree->height), 0, -1, tree->branches[0]->y - (tree->y + (int)tree->height), rootPos); doPrintTree(outfile, tree->branches[0], rootX+tree->x, rootPos); } else if (tree->nrBranches >= 2) { /* Down line. */ printLine(outfile, rootX + tree->x, -(tree->y + (int)tree->height), 0, -1, (tree->branches[0]->y - (tree->y + (int)tree->height))/2, rootPos); /* Cross bar */ printLine(outfile, rootX + tree->x + tree->branches[0]->x, (tree->y + (int)tree->height + tree->branches[0]->y)/-2, 1, 0, tree->branches[tree->nrBranches-1]->x - tree->branches[0]->x, rootPos); /* Subnodes. */ for (i = 0; i < tree->nrBranches; i++) { /* Down line from crossbar. */ printLine(outfile, rootX + tree->x + tree->branches[i]->x, (tree->y + (int)tree->height + tree->branches[i]->y)/-2, 0, -1, (tree->branches[i]->y - (tree->y + (int)tree->height))/2, rootPos); doPrintTree(outfile, tree->branches[i], rootX+tree->x, rootPos); } /* for */ } /* if */ } /* doPrintTree */ static void printTree(FILE *outfile, Tree tree, unsigned height, unsigned leftWidth, unsigned rightWidth, RootPos rootPos) { switch (rootPos) { case eTop: (void)fprintf(outfile, "\\setlength{\\unitlength}{%.2fpt}\n\ \\begin{picture}(%u,%u)(%d,%d)\n \\thicklines\n", 1.0/SCALE, leftWidth+rightWidth, height, -leftWidth, -height); break; case eLeft: (void)fprintf(outfile, "\\setlength{\\unitlength}{%.2fpt}\n\ \\begin{picture}(%u,%u)(%d,%d)\n \\thicklines\n", 1.0/SCALE, height, leftWidth+rightWidth, 0, -leftWidth); break; } /* switch */ doPrintTree(outfile, tree, 0, rootPos); (void)fprintf(outfile, "\\end{picture}\n"); } /* printTree */ static void readTree(FILE *infile, Tree *tree, double *xSeparation, double *ySeparation, RootPos *rootPos) { TreeNode *child; unsigned i; TreeNode **nodes; unsigned nrNodes = 0; unsigned maxNodes = 10; double nodeDepth; double nodeHeight; int nodeIsRoot; char nodeName[MAX_NAME_SIZE]; double nodeWidth; TreeNode *parent; char parentName[MAX_NAME_SIZE]; int rootFound = 0; char tmpChar; /* Scratch */ double tmpFloat1; /* Scratch */ double tmpFloat2; /* Scratch */ double tmpFloat3; /* Scratch */ TreeNode **tmpNodes; /* Scratch */ char *tmpStr1; /* Scratch */ char *tmpStr2; /* Scratch */ nodes = (TreeNode **)malloc(maxNodes*sizeof(TreeNode *)); if (NULL == nodes) { (void)fprintf(stderr, "The tree is too big.\n"); exit(1); } /* if */ /* Read rootPos. */ if (1 != fscanf(infile, "%1s", &tmpChar) || !('t' == tmpChar || 'l' == tmpChar)) { (void)fprintf(stderr, "Could not read tree orientation.\n"); exit(1); } /* if */ switch (tmpChar) { case 'l': *rootPos = eLeft; break; case 't': *rootPos = eTop; break; } /* switch */ /* Read xSeparation and ySeparation. */ if (2 != fscanf(infile, "%lfpt %lfpt", &(*xSeparation), &(*ySeparation))) { (void)fprintf(stderr, "Could not read x & y separations.\n"); exit(1); } /* if */ *xSeparation *= SCALE; *ySeparation *= SCALE; /* Read nodes. */ while (EOF != fscanf(infile, "%s", nodeName)) { nodeIsRoot = !strcmp(nodeName, "root"); if (nodeIsRoot) { if (rootFound) { (void)fprintf(stderr, "Too many root nodes.\n"); exit(1); } /* if */ rootFound = 1; parentName[0] = '\0'; } else if ((TreeNode *)NULL != findNode(nodeName, nodes, nrNodes)) { /* Node already exists. */ (void)fprintf(stderr, "The node name `%s' has been used more than once.\n", nodeName); exit(1); } /* if */ else if (1 != fscanf(infile, "%s", parentName)) {/* Not root node. */ (void)fprintf(stderr, "Could not read parent of %s.\n", nodeName); exit(1); } /* if */ /* Read node width height and depth. */ if (3 != fscanf(infile, "%lfpt %lfpt %lfpt", &tmpFloat1, &tmpFloat2, &tmpFloat3)) { (void)fprintf(stderr, "Could not read width, height and depth of %s.\n", nodeName); exit(1); } /* if */ switch (*rootPos) { case eTop: nodeWidth = tmpFloat1; nodeHeight = tmpFloat2 + tmpFloat3; nodeDepth = tmpFloat3; break; case eLeft: nodeWidth = tmpFloat2 + tmpFloat3; nodeHeight = tmpFloat1; nodeDepth = tmpFloat3; break; } /* switch */ /* Add node. */ nrNodes++; if (nrNodes > maxNodes) { /* Resize array. */ maxNodes *= 2; tmpNodes = (TreeNode **)realloc(nodes, (unsigned)(maxNodes*sizeof(TreeNode *))); if (NULL == tmpNodes) { (void)fprintf(stderr, "The tree is too big.\n"); exit(1); } /* if */ if (tmpNodes != nodes) (void)free(nodes); nodes = tmpNodes; } /* if */ tmpStr1 = malloc((size_t)((strlen(nodeName)+1)*sizeof(char))); if (NULL == tmpStr1) { (void)fprintf(stderr, "The tree is too big.\n"); exit(1); } /* if */ (void)strcpy(tmpStr1, nodeName); tmpStr2 = malloc((size_t)((strlen(parentName)+1)*sizeof(char))); if (NULL == tmpStr2) { (void)fprintf(stderr, "The tree is too big.\n"); exit(1); } /* if */ (void)strcpy(tmpStr2, parentName); nodes[nrNodes-1] = newTree(tmpStr1, tmpStr2, (unsigned)(SCALE*nodeHeight), (unsigned)(SCALE*nodeWidth), (unsigned)(SCALE*nodeDepth)); if ((TreeNode *)NULL == nodes[nrNodes-1]) { (void)fprintf(stderr, "The tree is too big.\n"); exit(1); } /* if */ } /* while */ if (!rootFound) { (void)fprintf(stderr, "No root node.\n"); exit(1); } /* if */ *tree = findNode("root", nodes, nrNodes); /* The root node. */ /* Construct tree. */ for (i = 0; i < nrNodes; i++) { child = nodes[i]; if (child != *tree) { parent = findNode(child->parent, nodes, nrNodes); if ((TreeNode *)NULL == parent) { (void)fprintf(stderr, "The parent (%s) of node %s does not exist.\n", child->parent, parent->name); exit(1); } /* if */ if (!addBranch(child, parent)) { (void)fprintf(stderr, "The tree is too big.\n"); exit(1); } /* if */ } /* if */ } /* for */ } /* readTree */