plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

draw.c (10800B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <bio.h>
      4 #include <ctype.h>
      5 #include "../common/common.h"
      6 #include "tr2post.h"
      7 
      8 BOOLEAN drawflag = FALSE;
      9 BOOLEAN	inpath = FALSE;			/* TRUE if we're putting pieces together */
     10 
     11 void
     12 cover(double x, double y) {
     13 }
     14 
     15 void
     16 drawspline(Biobuf *Bp, int flag) {	/* flag!=1 connect end points */
     17 	int x[100], y[100];
     18 	int i, N;
     19 /*
     20  *
     21  * Spline drawing routine for Postscript printers. The complicated stuff is
     22  * handled by procedure Ds, which should be defined in the library file. I've
     23  * seen wrong implementations of troff's spline drawing, so fo the record I'll
     24  * write down the parametric equations and the necessary conversions to Bezier
     25  * cubic splines (as used in Postscript).
     26  *
     27  *
     28  * Parametric equation (x coordinate only):
     29  *
     30  *
     31  *	    (x2 - 2 * x1 + x0)    2                    (x0 + x1)
     32  *	x = ------------------ * t   + (x1 - x0) * t + ---------
     33  *		    2					   2
     34  *
     35  *
     36  * The coefficients in the Bezier cubic are,
     37  *
     38  *
     39  *	A = 0
     40  *	B = (x2 - 2 * x1 + x0) / 2
     41  *	C = x1 - x0
     42  *
     43  *
     44  * while the current point is,
     45  *
     46  *	current-point = (x0 + x1) / 2
     47  *
     48  * Using the relationships given in the Postscript manual (page 121) it's easy to
     49  * see that the control points are given by,
     50  *
     51  *
     52  *	x0' = (x0 + 5 * x1) / 6
     53  *	x1' = (x2 + 5 * x1) / 6
     54  *	x2' = (x1 + x2) / 2
     55  *
     56  *
     57  * where the primed variables are the ones used by curveto. The calculations
     58  * shown above are done in procedure Ds using the coordinates set up in both
     59  * the x[] and y[] arrays.
     60  *
     61  * A simple test of whether your spline drawing is correct would be to use cip
     62  * to draw a spline and some tangent lines at appropriate points and then print
     63  * the file.
     64  *
     65  */
     66 
     67 	for (N=2; N<sizeof(x)/sizeof(x[0]); N++)
     68 		if (Bgetfield(Bp, 'd', &x[N], 0)<=0 || Bgetfield(Bp, 'd', &y[N], 0)<=0)
     69 			break;
     70 
     71 	x[0] = x[1] = hpos;
     72 	y[0] = y[1] = vpos;
     73 
     74 	for (i = 1; i < N; i++) {
     75 		x[i+1] += x[i];
     76 		y[i+1] += y[i];
     77 	}
     78 
     79 	x[N] = x[N-1];
     80 	y[N] = y[N-1];
     81 
     82 	for (i = ((flag!=1)?0:1); i < ((flag!=1)?N-1:N-2); i++) {
     83 		endstring();
     84 		if (pageon())
     85 			Bprint(Bstdout, "%d %d %d %d %d %d Ds\n", x[i], y[i], x[i+1], y[i+1], x[i+2], y[i+2]);
     86 /*		if (dobbox == TRUE) {		/* could be better */
     87 /*	    		cover((double)(x[i] + x[i+1])/2,(double)-(y[i] + y[i+1])/2);
     88 /*	    		cover((double)x[i+1], (double)-y[i+1]);
     89 /*	    		cover((double)(x[i+1] + x[i+2])/2, (double)-(y[i+1] + y[i+2])/2);
     90 /*		}
     91  */
     92 	}
     93 
     94 	hpos = x[N];			/* where troff expects to be */
     95 	vpos = y[N];
     96 }
     97 
     98 void
     99 draw(Biobuf *Bp) {
    100 
    101 	int r, x1, y1, x2, y2, i;
    102 	int d1, d2;
    103 
    104 	drawflag = TRUE;
    105 	r = Bgetrune(Bp);
    106 	switch(r) {
    107 	case 'l':
    108 		if (Bgetfield(Bp, 'd', &x1, 0)<=0 || Bgetfield(Bp, 'd', &y1, 0)<=0 || Bgetfield(Bp, 'r', &i, 0)<=0) {
    109 			error(FATAL, "draw line function, destination coordinates not found.\n");
    110 			return;
    111 		}
    112 
    113 		endstring();
    114 		if (pageon())
    115 			Bprint(Bstdout, "%d %d %d %d Dl\n", hpos, vpos, hpos+x1, vpos+y1);
    116 		hpos += x1;
    117 		vpos += y1;
    118 		break;
    119 	case 'c':
    120 		if (Bgetfield(Bp, 'd', &d1, 0)<=0) {
    121 			error(FATAL, "draw circle function, diameter coordinates not found.\n");
    122 			return;
    123 		}
    124 
    125 		endstring();
    126 		if (pageon())
    127 			Bprint(Bstdout, "%d %d %d %d De\n", hpos, vpos, d1, d1);
    128 		hpos += d1;
    129 		break;
    130 	case 'e':
    131 		if (Bgetfield(Bp, 'd', &d1, 0)<=0 || Bgetfield(Bp, 'd', &d2, 0)<=0) {
    132 			error(FATAL, "draw ellipse function, diameter coordinates not found.\n");
    133 			return;
    134 		}
    135 
    136 		endstring();
    137 		if (pageon())
    138 			Bprint(Bstdout, "%d %d %d %d De\n", hpos, vpos, d1, d2);
    139 		hpos += d1;
    140 		break;
    141 	case 'a':
    142 		if (Bgetfield(Bp, 'd', &x1, 0)<=0 || Bgetfield(Bp, 'd', &y1, 0)<=0 || Bgetfield(Bp, 'd', &x2, 0)<=0 || Bgetfield(Bp, 'd', &y2, 0)<=0) {
    143 			error(FATAL, "draw arc function, coordinates not found.\n");
    144 			return;
    145 		}
    146 
    147 		endstring();
    148 		if (pageon())
    149 			Bprint(Bstdout, "%d %d %d %d %d %d Da\n", hpos, vpos, x1, y1, x2, y2);
    150 		hpos += x1 + x2;
    151 		vpos += y1 + y2;
    152 		break;
    153 	case 'q':
    154 		drawspline(Bp, 1);
    155 		break;
    156 	case '~':
    157 		drawspline(Bp, 2);
    158 		break;
    159 	default:
    160 		error(FATAL, "unknown draw function <%c>\n", r);
    161 		return;
    162 	}
    163 }
    164 
    165 void
    166 beginpath(char *buf, int copy) {
    167 
    168 /*
    169  * Called from devcntrl() whenever an "x X BeginPath" command is read. It's used
    170  * to mark the start of a sequence of drawing commands that should be grouped
    171  * together and treated as a single path. By default the drawing procedures in
    172  * *drawfile treat each drawing command as a separate object, and usually start
    173  * with a newpath (just as a precaution) and end with a stroke. The newpath and
    174  * stroke isolate individual drawing commands and make it impossible to deal with
    175  * composite objects. "x X BeginPath" can be used to mark the start of drawing
    176  * commands that should be grouped together and treated as a single object, and
    177  * part of what's done here ensures that the PostScript drawing commands defined
    178  * in *drawfile skip the newpath and stroke, until after the next "x X DrawPath"
    179  * command. At that point the path that's been built up can be manipulated in
    180  * various ways (eg. filled and/or stroked with a different line width).
    181  *
    182  * Color selection is one of the options that's available in parsebuf(),
    183  * so if we get here we add *colorfile to the output file before doing
    184  * anything important.
    185  *
    186  */
    187 	if (inpath == FALSE) {
    188 		endstring();
    189 	/*	getdraw();	*/
    190 	/*	getcolor(); */
    191 		Bprint(Bstdout, "gsave\n");
    192 		Bprint(Bstdout, "newpath\n");
    193 		Bprint(Bstdout, "%d %d m\n", hpos, vpos);
    194 		Bprint(Bstdout, "/inpath true def\n");
    195 		if ( copy == TRUE )
    196 			Bprint(Bstdout, "%s\n", buf);
    197 		inpath = TRUE;
    198 	}
    199 }
    200 
    201 static void parsebuf(char*);
    202 
    203 void
    204 drawpath(char *buf, int copy) {
    205 
    206 /*
    207  *
    208  * Called from devcntrl() whenever an "x X DrawPath" command is read. It marks the
    209  * end of the path started by the last "x X BeginPath" command and uses whatever
    210  * has been passed along in *buf to manipulate the path (eg. fill and/or stroke
    211  * the path). Once that's been done the drawing procedures are restored to their
    212  * default behavior in which each drawing command is treated as an isolated path.
    213  * The new version (called after "x X DrawPath") has copy set to FALSE, and calls
    214  * parsebuf() to figure out what goes in the output file. It's a feeble attempt
    215  * to free users and preprocessors (like pic) from having to know PostScript. The
    216  * comments in parsebuf() describe what's handled.
    217  *
    218  * In the early version a path was started with "x X BeginObject" and ended with
    219  * "x X EndObject". In both cases *buf was just copied to the output file, and
    220  * was expected to be legitimate PostScript that manipulated the current path.
    221  * The old escape sequence will be supported for a while (for Ravi), and always
    222  * call this routine with copy set to TRUE.
    223  *
    224  *
    225  */
    226 
    227 	if ( inpath == TRUE ) {
    228 		if ( copy == TRUE )
    229 			Bprint(Bstdout, "%s\n", buf);
    230 		else
    231 			parsebuf(buf);
    232 		Bprint(Bstdout, "grestore\n");
    233 		Bprint(Bstdout, "/inpath false def\n");
    234 /*		reset();		*/
    235 		inpath = FALSE;
    236 	}
    237 }
    238 
    239 
    240 /*****************************************************************************/
    241 
    242 static void
    243 parsebuf(char *buf)
    244 {
    245 	char	*p = (char*)0;			/* usually the next token */
    246 	char *q;
    247 	int		gsavelevel = 0;		/* non-zero if we've done a gsave */
    248 
    249 /*
    250  *
    251  * Simple minded attempt at parsing the string that followed an "x X DrawPath"
    252  * command. Everything not recognized here is simply ignored - there's absolutely
    253  * no error checking and what was originally in buf is clobbered by strtok().
    254  * A typical *buf might look like,
    255  *
    256  *	gray .9 fill stroke
    257  *
    258  * to fill the current path with a gray level of .9 and follow that by stroking the
    259  * outline of the path. Since unrecognized tokens are ignored the last example
    260  * could also be written as,
    261  *
    262  *	with gray .9 fill then stroke
    263  *
    264  * The "with" and "then" strings aren't recognized tokens and are simply discarded.
    265  * The "stroke", "fill", and "wfill" force out appropriate PostScript code and are
    266  * followed by a grestore. In otherwords changes to the grahics state (eg. a gray
    267  * level or color) are reset to default values immediately after the stroke, fill,
    268  * or wfill tokens. For now "fill" gets invokes PostScript's eofill operator and
    269  * "wfill" calls fill (ie. the operator that uses the non-zero winding rule).
    270  *
    271  * The tokens that cause temporary changes to the graphics state are "gray" (for
    272  * setting the gray level), "color" (for selecting a known color from the colordict
    273  * dictionary defined in *colorfile), and "line" (for setting the line width). All
    274  * three tokens can be extended since strncmp() makes the comparison. For example
    275  * the strings "line" and "linewidth" accomplish the same thing. Colors are named
    276  * (eg. "red"), but must be appropriately defined in *colorfile. For now all three
    277  * tokens must be followed immediately by their single argument. The gray level
    278  * (ie. the argument that follows "gray") should be a number between 0 and 1, with
    279  * 0 for black and 1 for white.
    280  *
    281  * To pass straight PostScript through enclose the appropriate commands in double
    282  * quotes. Straight PostScript is only bracketed by the outermost gsave/grestore
    283  * pair (ie. the one from the initial "x X BeginPath") although that's probably
    284  * a mistake. Suspect I may have to change the double quote delimiters.
    285  *
    286  */
    287 
    288 	for( ; p != nil ; p = q ) {
    289 		if( q = strchr(p, ' ') ) {
    290 			*q++ = '\0';
    291 		}
    292 
    293 		if ( gsavelevel == 0 ) {
    294 			Bprint(Bstdout, "gsave\n");
    295 			gsavelevel++;
    296 		}
    297 		if ( strcmp(p, "stroke") == 0 ) {
    298 			Bprint(Bstdout, "closepath stroke\ngrestore\n");
    299 			gsavelevel--;
    300 		} else if ( strcmp(p, "openstroke") == 0 ) {
    301 			Bprint(Bstdout, "stroke\ngrestore\n");
    302 			gsavelevel--;
    303 		} else if ( strcmp(p, "fill") == 0 ) {
    304 			Bprint(Bstdout, "eofill\ngrestore\n");
    305 			gsavelevel--;
    306 		} else if ( strcmp(p, "wfill") == 0 ) {
    307 			Bprint(Bstdout, "fill\ngrestore\n");
    308 			gsavelevel--;
    309 		} else if ( strcmp(p, "sfill") == 0 ) {
    310 			Bprint(Bstdout, "eofill\ngrestore\ngsave\nstroke\ngrestore\n");
    311 			gsavelevel--;
    312 		} else if ( strncmp(p, "gray", strlen("gray")) == 0 ) {
    313 			if( q ) {
    314 				p = q;
    315 				if ( q = strchr(p, ' ') )
    316 					*q++ = '\0';
    317 				Bprint(Bstdout, "%s setgray\n", p);
    318 			}
    319 		} else if ( strncmp(p, "color", strlen("color")) == 0 ) {
    320 			if( q ) {
    321 				p = q;
    322 				if ( q = strchr(p, ' ') )
    323 					*q++ = '\0';
    324 				Bprint(Bstdout, "/%s setcolor\n", p);
    325 			}
    326 		} else if ( strncmp(p, "line", strlen("line")) == 0 ) {
    327 			if( q ) {
    328 				p = q;
    329 				if ( q = strchr(p, ' ') )
    330 					*q++ = '\0';
    331 				Bprint(Bstdout, "%s resolution mul 2 div setlinewidth\n", p);
    332 			}
    333 		} else if ( strncmp(p, "reverse", strlen("reverse")) == 0 )
    334 			Bprint(Bstdout, "reversepath\n");
    335 		else if ( *p == '"' ) {
    336 			for ( ; gsavelevel > 0; gsavelevel-- )
    337 				Bprint(Bstdout, "grestore\n");
    338 			if ( q != nil )
    339 				*--q = ' ';
    340 			if ( (q = strchr(p, '"')) != nil ) {
    341 				*q++ = '\0';
    342 				Bprint(Bstdout, "%s\n", p);
    343 			}
    344 		}
    345 	}
    346 
    347 	for ( ; gsavelevel > 0; gsavelevel-- )
    348 		Bprint(Bstdout, "grestore\n");
    349 
    350 }