summaryrefslogtreecommitdiff
path: root/graphics/tree/tex.c
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /graphics/tree/tex.c
Initial commit
Diffstat (limited to 'graphics/tree/tex.c')
-rw-r--r--graphics/tree/tex.c670
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++;
+ }
+ }
+}