/* * With the -u option, TeX \special commands are used to include * PostScript commands into the PS file created by the PS driver * (dvi2ps, psdvi, dvips, dvitops, or whatever). The driver should * not attempt repositioning or anything -- just copy to its output. * Some drivers may not be willing to do this, and others may need * the \special to begin with a certain keyword before they will do it. * Here is a list of those keywords I know about: * "ps-string " * "ps-string=" * "ps::" * "pstext=" * The following define is for this keyword; it might have to be set * to one of the above. (My version of dvi2ps does not require a * keyword.) */ #ifndef SKEY #define SKEY "ps::" #endif /* * Dimensions are in terms of em and ex (of the current font), with * basic values assuming cmr 10, which has 1em = 10pt, 1ex = 4.31pt. * * Each character or space is reckoned to be 1/2 em, which is the * width of a digit in the cmr fonts. However, each `col' unit in * the tree is a half-space wide -- 1/4 em, that is. * * The normal width of rules is .4pt, which becomes .04em. * The height of lines is that of a TeX \strut: 8.5pt above the * baseline and 3.5pt below, and in ex units: * 8.5pt/4.31=1.972ex * 3.5pt/4.31=0.81206ex * The horizontal bars made of "underlines" have vertical dimension * starting 3.5pt below the baseline and going up .4pt to: * 3.1pt/4.31=0.71925ex * The ones made of "overlines" go to 8.5pt and start .4pt lower: * 8.1pt/4.31=1.87935ex * Rule width is .4pt/4.31 = .09280742ex. */ float hskip = 0; /* amount to hskip before next box or bar */ dohskip () { /* must emit this even when hskip is 0 to get out of * vertical mode before an \hbox */ printf ("\\hskip %.2fem", hskip); hskip = 0; } /* record keeping for lines connecting discontinuous constituents */ int m_ulabel[11]; int d_ulabel[11]; /* * Keep track of the attributes from the \M and \D label * commands. For the first of a matching pair, save our treeid * in the ulabel array and issue PS commands to define the current * (x,y) coordinates with names corresponding to the treeid. For * the second of a matching pair, issue PS commands to draw a * line back to the previous location. */ setiattrib(node, is_daughter) TREE node; int *is_daughter; { int type = node->type; int iattrib = 0; if (type == VBAR && (iattrib = has(node,'D')) ) { /* this is a daughter label */ *is_daughter = TRUE; /* ... unless we're in an inverted tree */ /*if (node->mother && node->mother->type == OBAR)*/ if (has(node,'U') == 'i') *is_daughter = FALSE; if (iattrib == '+') iattrib = 10; else iattrib -= '0'; d_ulabel[iattrib] = node->treeid; /* if known already, get old treeid in iattrib, otherwise * note with "-1" that current location must be defined */ if (m_ulabel[iattrib]) iattrib = m_ulabel[iattrib]; else iattrib = -1; } else if ((type==HBAR || type==VBAR || type==OBAR) && (iattrib = has(node,'M'))) { /* this is a mother label */ *is_daughter = FALSE; /* ... unless we're an OBAR: */ if (type == OBAR) *is_daughter = TRUE; if (type == VBAR && has(node,'U') == 'i') *is_daughter = TRUE; if (iattrib == '+') iattrib = 10; else iattrib -= '0'; m_ulabel[iattrib] = node->treeid; if (d_ulabel[iattrib]) iattrib = d_ulabel[iattrib]; else iattrib = -1; } if (debug_opt) printf("%% note iattrib %d\n", iattrib); return(iattrib); } /* TeX height and depth to be used in current line for strut, etc. */ float h, d; /* used only in following routine, except also set in print_tex_tree */ int curr_row = -1; /* look at entire row to see what the smallest height and depth we can * get away with are */ static set_hd (node) TREE node; { int row_type = 0; int top_row = node->row; if (top_row == curr_row) return; curr_row = top_row; while (node && (node->row == top_row)) { row_type |= node->type; node = node->right; } if (row_type & NODENAME) { if (debug_opt) printf("%% Next row needs a full size strut.\n"); h = 1.972; d = .812; } else { /* Make rows without text for node names about 2/3 of normal size. */ if (debug_opt) printf("%% Setting height + depth to 2 ex's.\n"); h = 1.5; d = .5; } } /* Generate PS code for Bezier curve connecting discontinuous * constituents (needs work). This has to be independent of scale * or translation of coordinate system, since the PS driver may * have changed these. */ curvegen(sf, is_daughter) char *sf; int is_daughter; { if (is_daughter) printf("\\lower%1.3fex",d); else printf("\\raise%1.3fex",h); printf("\\hbox{\\special{%scurrentpoint ",SKEY); /* Daughter is at, say, (DX,DY) and mother at (MX,MY); * if we're now at the daughter, we need to calculate two * inflexion points, somehow. (Even though the PS variable * is called "mom", if we're at a mother node, it's actually * a daughter.) */ if (is_daughter) { printf("currentpoint pop mom%sy ",sf); /* (DX,MY) */ printf("mom%sx currentpoint exch pop ",sf); /* (MX, (DY+MY)/2 ) */ printf("mom%sy add 2 div ",sf); } else { /* We're at the mother, and the first inflexion point has to bend * us down, then the second has be up above the daughter so we hit * it going more or less downward. */ /* cpx */ printf("currentpoint pop "); /* cpy 2 mul dy sub */ printf("currentpoint exch pop 2 mul mom%sy sub ",sf); /* (MX, MY*2 - DY) */ /* dx */ printf("mom%sx ",sf); /* dy 2 mul cpy sub */ printf("mom%sy 2 mul currentpoint exch pop sub ",sf); /* (DX, DY*2 - MY) */ /* dx dy */ } /* and wind up at (DX,DY) or (MX,MY) */ printf("mom%sx mom%sy curveto stroke ",sf,sf); printf("moveto}}%%\n"); } char m[4], n[4]; /* m and n are suffixes for PS identifiers initialized here for * later use. m is for local lines; n is for discontinuous * constituent lines requested through labels */ setsuffix(s, id) char *s; int id; { s[0] = 'a'; s[1] = 'a'; s[2] = 'a'; s[3] = '\0'; s[0] += id / (26*26); s[1] += (id / 26) % 26; s[2] += id % 26; } char *whichbar[] = { "", "\\big\\Vert", "\\big\\downarrow", "\\big\\Downarrow", "\\big\\uparrow", "\\big\\Uparrow", "\\big\\updownarrow", "\\diamondsuit", "\\triangle", "" }; char *whichobar[] = { "", "\\big\\Vert", "\\big\\uparrow", "\\big\\Uparrow", "\\big\\downarrow", "\\big\\Downarrow", "\\big\\updownarrow", "\\diamondsuit", "\\nabla", "" }; int greyness; /* The vertical bars start horizontally half way through a * character space and go .4pt further to the right. For the * -u option, issue PS to draw a line to the previously defined * position under the node name above. (For upside down trees, * the position over the node name below is unfortunately not yet * defined -- haven't figured out what to do about that -- not * sure I care enough.) */ texvbar(node, iattrib, r, is_daughter) TREE node; int iattrib, is_daughter; float r; { TREE mom = node->mother; int do_a_line = FALSE, do_a_bar = FALSE, do_a_tbar = FALSE, do_nothing = FALSE, def_a_sister = FALSE, def_an_osister = FALSE, def_a_mother = FALSE, do_a_triangle = FALSE, do_a_box = FALSE, do_an_obox = FALSE; do_a_line = (utex_opt && mom && mom->type == HBAR); if (mom && mom->row > node->row) do_a_line = FALSE; def_an_osister = (!has(node,'O') && utex_opt && mom && mom->type == OBAR); if (def_an_osister) { def_a_sister = TRUE; if (has(node,'S') == 'f' || has(node,'S') == 'o') mom->left = node; if (has(node,'S') == 'o') def_a_mother = TRUE; } if (do_a_line && has(node,'T') && !has(node,'B')) { if (has(node,'S') == 'f' || has(node,'S') == 'o') def_a_sister = TRUE; if (has(node,'S') == 'o' || has(node,'S') == 'l') do_a_triangle = TRUE; if (!do_a_triangle && !def_a_sister) do_nothing = TRUE; } if (has(node,'O') || has(node,'P')) do_nothing = TRUE; if (iattrib && utex_opt) do_nothing = FALSE; if (utex_opt && iattrib == -1) def_a_mother = TRUE; do_a_bar = (!do_a_line && !do_nothing && !def_an_osister); if (has(node,'B')) do_a_bar = TRUE; if (do_a_bar && has(node,'T')) { if (has(node,'S') == 'f' || has(node,'S') == 'o') { if (mom && mom->type == OBAR) do_an_obox = TRUE; /*else if (mom && node == mom->daughter) do_a_box = TRUE;*/ else /*if (mom && node == mom->daughter)*/ do_a_box = TRUE; do_nothing = TRUE; } else do_nothing = TRUE; } if (has(node,'O') || has(node,'P')) do_a_bar = FALSE; if (do_a_bar && (!utex_opt || has(node,'B')) && whichbar[b][0]) { do_a_tbar = TRUE; do_a_bar = FALSE; } hskip += .25; if (do_a_box || do_an_obox) { TREE first = node, last; int len; while (node->sister && node->mother == node->sister->mother && has(node,'S') != 'l' && has(node,'S') != 'o' && node->sister->type == VBAR) { node->mother = NULL; node = node->sister; } last = node; last->mother = NULL; if (first != last) { len = last->col + last->l - first->col; r = len; r /= COLMUL; } dohskip(); printf ("\\vrule width.04em"); hskip -= .04; if (greyness) { printf ("\\xleaders\\hbox to .%dem{\\hss$\\", greyness); if (do_a_box) printf("Down"); else printf("Up"); printf ("arrow$\\hss}\\hskip%.2fem\n", r/2 - .50 - .04); } else { if (do_a_box) printf ("\\vrule width%.2fem height%1.3fex depth%1.3fex%%\n", r/2 - .50 - .04, .09281 - d, d); else printf ("\\vrule width%.2fem height%1.3fex depth%1.3fex%%\n", r/2 - .50 - .04, h, .09281 - h); } if (first != last) hskip -= (r/2 - .50); printf ("\\vrule width.04em"); } if (do_nothing) { hskip += .25; return; } dohskip(); if (do_a_bar) { printf ("\\vrule width.04em%%\n"); hskip -= .04; } if (do_a_line) { setsuffix(m, mom->treeid); printf("\\lower%1.3fex\\hbox{\\special{%scurrentpoint",d,SKEY); } if (def_an_osister) { setsuffix(m, node->treeid); printf("\\raise%1.3fex\\hbox{\\special{%scurrentpoint",h,SKEY); } if (def_a_sister) printf(" /sis%sy exch def /sis%sx exch def}}%%\n",m,m); if (do_a_triangle) { if (def_a_sister) { hskip += r/2 - .75; dohskip(); hskip += .25; printf("\\lower%1.3fex\\hbox{",d); printf("\\special{%scurrentpoint",SKEY); } printf(" sis%sx sis%sy lineto mom%sx mom%sy lineto",m,m,m,m); printf(" closepath"); if (greyness) printf(" .%d setgray fill 0 setgray", greyness); else printf(" stroke"); } if (do_a_line && !do_a_tbar && !def_a_sister && !do_a_triangle && !do_a_bar && !has(node,'O') && !has(node,'P')) printf(" mom%sx mom%sy lineto stroke",m,m); if (do_a_line && (do_a_triangle || !def_a_sister)) { printf(" moveto}}%%\n"); } if (def_a_mother) { if (def_an_osister) { hskip += r/2 - .75; dohskip(); hskip += .25; is_daughter = FALSE; } setsuffix(n, node->treeid); if (is_daughter) printf("\\lower%1.3fex",d); else printf("\\raise%1.3fex",h); printf("\\hbox{\\special{%s currentpoint",SKEY); printf(" /mom%sy exch def /mom%sx exch def}}%%\n",n,n); } if (utex_opt && iattrib > 0) { setsuffix(n, iattrib); curvegen(n, is_daughter); } if (do_a_tbar) { hskip -= .25 - .04; dohskip(); printf("\\hbox to 0.5em{\\hss$%s$\\hss}%%\n", (has(node,'U') == 'i')? whichobar[b] : whichbar[b]); hskip -= .25 + .04; } hskip += .25; } /* Horizontal bars also start half way through a space. * They have a vertical bar in the center and extend .4pt * further than half way through a character space at the * end to cap the vertical bar that will come below. * For the -u option, instead of rules, generate PS code * to remember the coordinates under the node name above, so * later can draw line from daughters back to here. */ texhbar(node, iattrib, r, rm, is_daughter) TREE node; int iattrib, is_daughter; float r, rm; { TREE mom = node->mother; if (utex_opt && !has(node,'B')) { /* REVISE HERE */ /* rm/2 + (r - rm)/2 = rm/2 + r/2 - rm/2 = r/2 */ hskip += rm/2 + .25; dohskip (); if (debug_opt) printf("%% Here is mom [%d]\n", node->treeid); printf("\\raise%1.3fex\\hbox{\\special{%scurrentpoint",h,SKEY); setsuffix(m, node->treeid); printf(" /mom%sy exch def /mom%sx exch def}}%%\n",m,m); if (iattrib > 0) { setsuffix(n, iattrib); curvegen(n, is_daughter); } hskip += (r - rm)/2 - .25; } else { float w; hskip += .25; dohskip (); if (b == 9 && r >= 6) printf("\\hbox to %.2fem{\\downbracefill}%%\n", r/2 - .50 + .04); else if (!node->mother) printf ("\\vrule width%.2fem height%1.3fex depth%1.3fex%%\n", r/2 - .50 + .04, .09281 - d, d); else { if ((w = rm/2) > .01) printf ("\\vrule width%.2fem height%1.3fex depth%1.3fex%%\n", w, .09281 - d, d); printf ("\\vrule width.04em"); if ((w = r/2 - w - .50) > .01) printf ("\\vrule width%.2fem height%1.3fex depth%1.3fex%%\n", w, .09281 - d, d); } hskip += .25 -.04; } } texobar(node, iattrib, r, rm, is_daughter) TREE node; int iattrib, is_daughter; float r, rm; { /* "left" is actually a pointer up to first daughter */ if (utex_opt && !has(node,'B') && node->left && node->left->mother == node && node->left->type == VBAR) { TREE sis = node->left; hskip += rm/2 + .25; dohskip (); if (debug_opt) printf("%% Here is bottom pt [%d]\n", node->treeid); printf("\\lower%1.3fex\\hbox{\\special{%scurrentpoint",d,SKEY); if (iattrib == -1) { setsuffix(n, node->treeid); printf(" currentpoint /mom%sy exch def /mom%sx exch def",n,n); } while (sis && sis->mother == node) { if (has(sis,'O') || has(sis,'P')) { sis = sis->sister; continue; } setsuffix(m, sis->treeid); if (has(node,'T')) { if (has(node,'S') == 'f' || has(node,'S') == 'l' || has(node,'S') == 'o') printf(" sis%sx sis%sy lineto",m,m); } else printf(" currentpoint sis%sx sis%sy lineto moveto",m,m); if (has(node,'T') && has(node,'S') == 'o') printf(" mom%sx mom%sy lineto",m,m); sis = sis->sister; } if (has(node,'T')) { printf(" closepath"); if (greyness) printf(" .%d setgray fill 0 setgray", greyness); else printf(" stroke"); } else printf(" stroke"); printf(" moveto}}%%\n"); if (iattrib > 0) { setsuffix(n, iattrib); curvegen(n, is_daughter); } hskip += (r - rm)/2 - .25; } else { float w; hskip += .25; dohskip (); if (b == 9 && r >= 6) printf("\\hbox to %.2fem{\\upbracefill}%%\n", r/2 - .50 + .04); else if (node->mother && node->right) { if ((w = rm/2) > .01) printf ("\\vrule width%.2fem height%1.3fex depth%1.3fex%%\n", w, h, .09281 - h); printf ("\\vrule width.04em"); if ((w = r/2 - w - .50) > .01) printf ("\\vrule width%.2fem height%1.3fex depth%1.3fex%%\n", w, h, .09281 - h); } else printf ("\\vrule width%.2fem height%1.3fex depth%1.3fex%%\n", r/2 - .50 + .04, h, .09281 - h); hskip += .25 - .04; } } texnodename(node, r) TREE node; float r; { if (has(node,'P')) hskip += r/2; else { dohskip (); if (has(node,'R')) printf ("\\hbox to %.2fem{\\hss{%s}}%%\n", r/2, node->n); else printf ("\\hbox to %.2fem{\\hss{%s}\\hss}%%\n", r/2, node->n); } } /* emit TeX code for a bar or a box containing a node name */ boxitup (node) TREE node; { float r = node->l, rm; int iattrib = 0; int is_daughter; int i; TREE mom = node->mother; if (b = has(node,'B')) { if (b == '+') b = 9; else if (isdigit(b)) b -= '0'; else b = 0; greyness = b; } else { for (b = black_opt; b > 10; b /= 10) ; for (greyness = black_opt; greyness >= 100; greyness /= 10); } if (greyness > 9) greyness = 100 - greyness; else if (greyness) greyness = 10 - greyness; if (mom && node != mom->daughter && node->sister) mom = NULL; /* set height and depth for raising and lowering and for strut * at end of line */ set_hd(node); iattrib = setiattrib(node, &is_daughter); /* halve width to compensate for doubling col values */ r /= COLMUL; i = node->mid; if (COLMUL > 2) i /= (COLMUL/2); rm = i; if (COLMUL > 1) rm /= 2; /* now generate TeX code for VBARs, HBARs, and NODENAMEs */ switch(node->type) { case VBAR: texvbar(node, iattrib, r, is_daughter); break; case HBAR: texhbar(node, iattrib, r, rm, is_daughter); break; case OBAR: texobar(node, iattrib, r, rm, is_daughter); break; case NODENAME: texnodename(node, r); break; } } /* Like preceding routine tex(), except collect spaces and * emit globs of TeX \hskip, \hbox, and \vrule commands (for * respectively space, node names, and tree lines). */ tex(tree) TREE tree; { int row = tree->row, col = 0, i; hskip = (float) indent / 2; /* mark all labels as undefined */ for (i = 0; i < 11; i++) { m_ulabel[i] = 0; d_ulabel[i] = 0; } /* make sure set_hd looks at first row */ curr_row = -1; /* Each line of the tree will be a paragraph, so prevent white space * breaking up segments of vertical rules; put a strut at the * end of each line to make it high enough. I'm not using regular * \strut commands, because my own TeX code is not careful about * keeping \strut defined appropriately for the font in use. */ printf ("\n\n{\\parskip=0pt\\offinterlineskip%%\n"); while (1) { if (!tree) { printf ("\\vrule width0em height%1.3fex depth%1.3fex",h,d); printf ("\\par}\n"); bufp = buf; /* can reuse string buffer for next tree */ /* (could free malloc'd memory for tree nodes, too) */ return; } if (tree->row > row) { /* Put a strut at the end of each line. The height and * depth were determined by set_hd, called by boxitup. */ printf ("\\vrule width0em height%1.3fex depth%1.3fex",h,d); /* prevent page breaks in midst of tree */ printf ("\\par\\penalty10000\n"); row++; col = 0; hskip = (float) indent / 2; } else if ((tree->row == row) && (tree->col == col)) { boxitup(tree); col += tree->l; tree = tree->right; } else { /* to advance one column, hskip 1/4 em, which is only 1/2 * en space, since we doubled all the col values */ hskip +=.50/COLMUL; col++; } } }