URI:
       tdraw.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tdraw.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 }