diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /graphics/tree/tex.c |
Initial commit
Diffstat (limited to 'graphics/tree/tex.c')
-rw-r--r-- | graphics/tree/tex.c | 670 |
1 files changed, 670 insertions, 0 deletions
diff --git a/graphics/tree/tex.c b/graphics/tree/tex.c new file mode 100644 index 0000000000..10234f6e25 --- /dev/null +++ b/graphics/tree/tex.c @@ -0,0 +1,670 @@ + + +/* + * 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++; + } + } +} |