00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "render.h"
00019
00020 #define PIC_COORDS_PER_LINE (16)
00021
00022 static box BB;
00023 static int BezierSubdivision = 10;
00024 static int Rot;
00025 static int onetime = TRUE;
00026 static double Scale;
00027 static double Fontscale;
00028
00029
00030 static const char *EscComment = ".\\\" ";
00031
00032 typedef struct grcontext_t {
00033 char *color, *font;
00034 double size;
00035 } grcontext_t;
00036
00037 #define STACKSIZE 8
00038 static grcontext_t S[STACKSIZE];
00039 static int SP = 0;
00040
00041 static char picgen_msghdr[] = "dot picgen: ";
00042 static void unsupported(char *s)
00043 {
00044 agerr(AGWARN, "%s%s unsupported\n", picgen_msghdr, s);
00045 }
00046 static void warn(char *s)
00047 {
00048 agerr(AGWARN, "%s%s\n", picgen_msghdr, s);
00049 }
00050
00051 #undef MAX
00052 #ifndef MAX
00053 # define MAX(a,b) (((a)>(b))? (a) : (b))
00054 #endif
00055 #undef MIN
00056 #ifndef MIN
00057 # define MIN(a,b) (((a)<(b))? (a) : (b))
00058 #endif
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082 typedef struct {
00083 char trname[3], *psname;
00084 } fontinfo;
00085
00086 static fontinfo fonttab[] = {
00087 {"AB", "AvantGarde-Demi"},
00088 {"AI", "AvantGarde-BookOblique"},
00089 {"AR", "AvantGarde-Book"},
00090 {"AX", "AvantGarde-DemiOblique"},
00091 {"B ", "Times-Bold"},
00092 {"BI", "Times-BoldItalic"},
00093 {"CB", "Courier-Bold"},
00094 {"CO", "Courier"},
00095 {"CX", "Courier-BoldOblique"},
00096 {"H ", "Helvetica"},
00097 {"HB", "Helvetica-Bold"},
00098 {"HI", "Helvetica-Oblique"},
00099 {"HX", "Helvetica-BoldOblique"},
00100 {"Hb", "Helvetica-Narrow-Bold"},
00101 {"Hi", "Helvetica-Narrow-Oblique"},
00102 {"Hr", "Helvetica-Narrow"},
00103 {"Hx", "Helvetica-Narrow-BoldOblique"},
00104 {"I ", "Times-Italic"},
00105 {"KB", "Bookman-Demi"},
00106 {"KI", "Bookman-LightItalic"},
00107 {"KR", "Bookman-Light"},
00108 {"KX", "Bookman-DemiItalic"},
00109 {"NB", "NewCenturySchlbk-Bold"},
00110 {"NI", "NewCenturySchlbk-Italic"},
00111 {"NR", "NewCenturySchlbk-Roman"},
00112 {"NX", "NewCenturySchlbk-BoldItalic"},
00113 {"PA", "Palatino-Roman"},
00114 {"PB", "Palatino-Bold"},
00115 {"PI", "Palatino-Italic"},
00116 {"PX", "Palatino-BoldItalic"},
00117 {"R ", "Times-Roman"},
00118 {"S ", "Symbol"},
00119 {"ZD", "ZapfDingbats"},
00120 {"\000\000", (char *) 0}
00121 };
00122
00123 static char *picfontname(char *psname)
00124 {
00125 char *rv;
00126 fontinfo *p;
00127
00128 for (p = fonttab; p->psname; p++)
00129 if (streq(p->psname, psname))
00130 break;
00131 if (p->psname)
00132 rv = p->trname;
00133 else {
00134 agerr(AGERR, "%s%s is not a troff font\n", picgen_msghdr, psname);
00135
00136 if ((rv = strrchr(psname, '-'))) {
00137 *rv = '\0';
00138 rv = picfontname(psname);
00139 } else
00140 rv = "R";
00141 }
00142 return rv;
00143 }
00144
00145 static char *pic_fcoord(char *buf, pointf pf)
00146 {
00147 sprintf(buf, "(%.5f,%.5f)", Scale * pf.x, Scale * pf.y);
00148 return buf;
00149 }
00150
00151 static char *pic_coord(char *buf, point p)
00152 {
00153 return pic_fcoord(buf, cvt2ptf(p));
00154 }
00155
00156 static void pic_reset(void)
00157 {
00158 onetime = TRUE;
00159 }
00160
00161 static void pic_begin_job(FILE * ofp, graph_t * g, char **lib, char *user,
00162 char *info[], point pages)
00163 {
00164
00165 if (onetime && (pages.x * pages.y > 1)) {
00166 unsupported("pagination");
00167 onetime = FALSE;
00168 }
00169 fprintf(Output_file, "%s Creator: %s version %s (%s)\n",
00170 EscComment, info[0], info[1], info[2]);
00171 fprintf(Output_file, "%s For: %s\n", EscComment, user);
00172 fprintf(Output_file, "%s Title: %s\n", EscComment, g->name);
00173 }
00174
00175 static void pic_begin_graph(GVC_t * gvc, graph_t * g, box bb, point pb)
00176 {
00177 BB = bb;
00178
00179 fprintf(Output_file,
00180 "%s save point size and font\n.nr .S \\n(.s\n.nr DF \\n(.f\n",
00181 EscComment);
00182 }
00183
00184 static void pic_end_graph(void)
00185 {
00186 fprintf(Output_file,
00187 "%s restore point size and font\n.ps \\n(.S\n.ft \\n(DF\n",
00188 EscComment);
00189 }
00190
00191 static void pic_begin_page(graph_t * g, point page, double scale, int rot,
00192 point offset)
00193 {
00194 double height, width;
00195
00196 if (onetime && rot && (rot != 90)) {
00197 unsupported("rotation");
00198 onetime = FALSE;
00199 }
00200 Rot = rot;
00201 height = PS2INCH((double) (BB.UR.y) - (double) (BB.LL.y));
00202 width = PS2INCH((double) (BB.UR.x) - (double) (BB.LL.x));
00203 Scale = scale;
00204 if (Rot == 90) {
00205 double temp = width;
00206 width = height;
00207 height = temp;
00208 }
00209 fprintf(Output_file, ".PS %.5f %.5f\n", width, height);
00210 EscComment = "#";
00211 fprintf(Output_file,
00212 "%s to change drawing size, multiply the width and height on the .PS line above and the number on the two lines below (rounded to the nearest integer) by a scale factor\n",
00213 EscComment);
00214 if (width > 0.0) {
00215 Fontscale = log10(width);
00216 Fontscale += 3.0 - (int) Fontscale;
00217 } else
00218 Fontscale = 3.0;
00219 Fontscale = pow(10.0, Fontscale);
00220 fprintf(Output_file, ".nr SF %.0f\nscalethickness = %.0f\n", Fontscale,
00221 Fontscale);
00222 fprintf(Output_file,
00223 "%s don't change anything below this line in this drawing\n",
00224 EscComment);
00225 fprintf(Output_file,
00226 "%s non-fatal run-time pic version determination, version 2\n",
00227 EscComment);
00228 fprintf(Output_file,
00229 "boxrad=2.0 %s will be reset to 0.0 by gpic only\n",
00230 EscComment);
00231 fprintf(Output_file, "scale=1.0 %s required for comparisons\n",
00232 EscComment);
00233 fprintf(Output_file,
00234 "%s boxrad is now 0.0 in gpic, else it remains 2.0\n",
00235 EscComment);
00236 fprintf(Output_file,
00237 "%s dashwid is 0.1 in 10th Edition, 0.05 in DWB 2 and in gpic\n",
00238 EscComment);
00239 fprintf(Output_file,
00240 "%s fillval is 0.3 in 10th Edition (fill 0 means black), 0.5 in gpic (fill 0 means white), undefined in DWB 2\n",
00241 EscComment);
00242 fprintf(Output_file,
00243 "%s fill has no meaning in DWB 2, gpic can use fill or filled, 10th Edition uses fill only\n",
00244 EscComment);
00245 fprintf(Output_file,
00246 "%s DWB 2 doesn't use fill and doesn't define fillval\n",
00247 EscComment);
00248 fprintf(Output_file,
00249 "%s reset works in gpic and 10th edition, but isn't defined in DWB 2\n",
00250 EscComment);
00251 fprintf(Output_file, "%s DWB 2 compatibility definitions\n",
00252 EscComment);
00253 fprintf(Output_file,
00254 "if boxrad > 1.0 && dashwid < 0.075 then X\n\tfillval = 1;\n\tdefine fill Y Y;\n\tdefine solid Y Y;\n\tdefine reset Y scale=1.0 Y;\nX\n");
00255 fprintf(Output_file, "reset %s set to known state\n", EscComment);
00256 fprintf(Output_file, "%s GNU pic vs. 10th Edition d\\(e'tente\n",
00257 EscComment);
00258 fprintf(Output_file,
00259 "if fillval > 0.4 then X\n\tdefine setfillval Y fillval = 1 - Y;\n\tdefine bold Y thickness 2 Y;\n");
00260 fprintf(Output_file,
00261 "\t%s if you use gpic and it barfs on encountering \"solid\",\n",
00262 EscComment);
00263 fprintf(Output_file,
00264 "\t%s\tinstall a more recent version of gpic or switch to DWB or 10th Edition pic;\n",
00265 EscComment);
00266 fprintf(Output_file,
00267 "\t%s\tsorry, the groff folks changed gpic; send any complaint to them;\n",
00268 EscComment);
00269 fprintf(Output_file,
00270 "X else Z\n\tdefine setfillval Y fillval = Y;\n\tdefine bold Y Y;\n\tdefine filled Y fill Y;\nZ\n");
00271 fprintf(Output_file,
00272 "%s arrowhead has no meaning in DWB 2, arrowhead = 7 makes filled arrowheads in gpic and in 10th Edition\n",
00273 EscComment);
00274 fprintf(Output_file,
00275 "%s arrowhead is undefined in DWB 2, initially 1 in gpic, 2 in 10th Edition\n",
00276 EscComment);
00277 fprintf(Output_file, "arrowhead = 7 %s not used by graphviz\n",
00278 EscComment);
00279 fprintf(Output_file,
00280 "%s GNU pic supports a boxrad variable to draw boxes with rounded corners; DWB and 10th Ed. do not\n",
00281 EscComment);
00282 fprintf(Output_file, "boxrad = 0 %s no rounded corners in graphviz\n",
00283 EscComment);
00284 fprintf(Output_file,
00285 "%s GNU pic supports a linethick variable to set line thickness; DWB and 10th Ed. do not\n",
00286 EscComment);
00287 fprintf(Output_file, "linethick = 0; oldlinethick = linethick\n");
00288 fprintf(Output_file,
00289 "%s .PS w/o args causes GNU pic to scale drawing to fit 8.5x11 paper; DWB does not\n",
00290 EscComment);
00291 fprintf(Output_file,
00292 "%s maxpsht and maxpswid have no meaning in DWB 2.0, set page boundaries in gpic and in 10th Edition\n",
00293 EscComment);
00294 fprintf(Output_file,
00295 "%s maxpsht and maxpswid are predefined to 11.0 and 8.5 in gpic\n",
00296 EscComment);
00297 fprintf(Output_file, "maxpsht = %f\nmaxpswid = %f\n", height, width);
00298 fprintf(Output_file, "Dot: [\n");
00299 fprintf(Output_file,
00300 "define attrs0 %% %%; define unfilled %% %%; define rounded %% %%; define diagonals %% %%\n");
00301 }
00302
00303 static void pic_end_page(void)
00304 {
00305 fprintf(Output_file, "]\n.PE\n");
00306 EscComment = ".\\\" ";
00307 assert(SP == 0);
00308 }
00309
00310 static void pic_begin_node(node_t * n)
00311 {
00312 fprintf(Output_file, "%s\t%s\n", EscComment, n->name);
00313 }
00314
00315 static void pic_begin_edge(edge_t * e)
00316 {
00317 fprintf(Output_file, "%s\t%s -> %s\n", EscComment, e->tail->name,
00318 e->head->name);
00319 }
00320
00321 static void pic_begin_context(void)
00322 {
00323 fprintf(Output_file, "{\n");
00324 if (SP == STACKSIZE - 1)
00325 warn("stk ovfl");
00326 else {
00327 SP++;
00328 S[SP] = S[SP - 1];
00329 fprintf(Output_file, "define attrs%d %% %%\n", SP);
00330 }
00331 }
00332
00333 static void pic_end_context(void)
00334 {
00335 if (SP == 0)
00336 warn("stk undfl");
00337 else {
00338 SP--;
00339 fprintf(Output_file, "}\n");
00340
00341 if (S[SP + 1].font
00342 && (!(S[SP].font) || strcmp(S[SP + 1].font, S[SP].font)))
00343 fprintf(Output_file, ".ft %s\n", picfontname(S[SP].font));
00344 if (S[SP + 1].size != S[SP].size) {
00345 int sz;
00346
00347 if ((sz = (int) (S[SP].size * Scale)) < 1)
00348 sz = 1;
00349 fprintf(Output_file, ".ps %d*\\n(SFu/%.0fu\n", sz, Fontscale);
00350 }
00351 fprintf(Output_file, "linethick = oldlinethick\n");
00352 }
00353 }
00354
00355 static void pic_set_font(char *name, double size)
00356 {
00357 if (name && (!(S[SP].font) || strcmp(S[SP].font, name))) {
00358 S[SP].font = name;
00359 fprintf(Output_file, ".ft %s\n", picfontname(name));
00360 }
00361 if (size != S[SP].size) {
00362 int sz;
00363
00364 S[SP].size = size;
00365 if ((sz = (int) (size * Scale)) < 1)
00366 sz = 1;
00367 fprintf(Output_file, ".ps %d*\\n(SFu/%.0fu\n", sz, Fontscale);
00368 }
00369 }
00370
00371 static char *pic_string(char *s)
00372 {
00373 static char *buf = NULL;
00374 static int bufsize = 0;
00375 int pos = 0;
00376 char *p;
00377
00378 if (!buf) {
00379 bufsize = 64;
00380 buf = N_GNEW(bufsize, char);
00381 }
00382
00383 p = buf;
00384 while (*s) {
00385 if (pos > (bufsize - 8)) {
00386 bufsize *= 2;
00387 buf = grealloc(buf, bufsize);
00388 p = buf + pos;
00389 }
00390 if (*s == '\015') {
00391
00392
00393 s++;
00394 continue;
00395 }
00396 if (*s == '\\') {
00397 strcpy(p, "\\(rs");
00398 p += 4;
00399 pos += 4;
00400 s++;
00401 continue;
00402 }
00403 *p++ = *s++;
00404 pos++;
00405 }
00406 *p = '\0';
00407 return buf;
00408 }
00409
00410 static void pic_textpara(point p, textpara_t * para)
00411 {
00412 pointf pf;
00413 short flag = 0;
00414 double fontsz = S[SP].size;
00415
00416 switch (para->just) {
00417 case 'l':
00418 p.x = p.x;
00419 break;
00420 case 'r':
00421 p.x = p.x - para->width;
00422 break;
00423 default:
00424 case 'n':
00425 p.x = p.x - para->width / 2;
00426 break;
00427 }
00428 pf = cvt2ptf(p);
00429 #ifdef NOTDEF
00430
00431 pf.y -= fontsz / (5.0 * POINTS_PER_INCH);
00432 #endif
00433
00434 pf.y += fontsz / (3.0 * POINTS_PER_INCH);
00435 pf.x += para->width / (2.0 * POINTS_PER_INCH);
00436 if (!(S[SP].size)) {
00437 pic_set_font(S[SP].font, fontsz);
00438 for (flag = SP; ((S[flag].size = fontsz), flag); flag--)
00439 ;
00440 }
00441 if (fontsz != S[SP].size) {
00442
00443 flag = 1;
00444 pic_begin_context();
00445 pic_set_font(S[SP - 1].font, fontsz);
00446 }
00447 fprintf(Output_file, "\"%s\" at (%.5f,%.5f);\n",
00448 pic_string(para->str), Scale * pf.x, Scale * pf.y);
00449 if (flag)
00450 pic_end_context();
00451 }
00452
00453 static void pic_set_color(char *name)
00454 {
00455 gvcolor_t color;
00456
00457 S[SP].color = name;
00458 colorxlate(name, &color, HSVA_DOUBLE);
00459
00460 fprintf(Output_file, "setfillval %f\n", color.u.HSVA[2]);
00461 }
00462
00463 static void pic_set_style(char **s)
00464 {
00465 const char *line, *p;
00466 char skip = 0;
00467 char buf[BUFSIZ];
00468
00469 buf[0] = '\0';
00470 fprintf(Output_file, "define attrs%d %%", SP);
00471 while ((p = line = *s++)) {
00472 while (*p)
00473 p++;
00474 p++;
00475 while (*p) {
00476 if (!strcmp(line, "setlinewidth")) {
00477 long n = atol(p);
00478
00479 sprintf(buf,
00480 "oldlinethick = linethick;linethick = %ld * scalethickness / %.0f\n",
00481 n, Fontscale / Scale);
00482 skip = 1;
00483 } else
00484 fprintf(Output_file, " %s", p);
00485 while (*p)
00486 p++;
00487 p++;
00488 }
00489 if (!skip)
00490 fprintf(Output_file, " %s", line);
00491 skip = 0;
00492 }
00493 fprintf(Output_file, " %%\n");
00494 fprintf(Output_file, "%s", buf);
00495 }
00496
00497 static void pic_ellipse(point p, int rx, int ry, int filled)
00498 {
00499 pointf pf;
00500
00501 pf = cvt2ptf(p);
00502 fprintf(Output_file,
00503 "ellipse attrs%d %swid %.5f ht %.5f at (%.5f,%.5f);\n", SP,
00504 filled ? "fill " : "", Scale * PS2INCH(2 * rx),
00505 Scale * PS2INCH(2 * ry), Scale * pf.x, Scale * pf.y);
00506 }
00507
00508 static void point_list_out(point * A, int n, int close)
00509 {
00510 int j;
00511 char buf[SMALLBUF];
00512
00513 for (j = 0; j < n; j++)
00514 fprintf(Output_file, "P%d: %s\n", j, pic_coord(buf, A[j]));
00515 for (j = 0; j + 1 < n; j++)
00516 fprintf(Output_file, "move to P%d; line attrs%d to P%d\n", j, SP,
00517 j + 1);
00518 if (close)
00519 fprintf(Output_file, "move to P%d; line attrs%d to P0\n", n - 1,
00520 SP);
00521 }
00522
00523 static void pic_polygon(point * A, int n, int filled)
00524 {
00525
00526 if ((n == 4) && (((A[0].x == A[1].x) && (A[0].y == A[3].y)
00527 && (A[1].y == A[2].y) && (A[2].x == A[3].x))
00528 || ((A[0].y == A[1].y) && (A[0].x == A[3].x)
00529 && (A[1].x == A[2].x) && (A[2].y == A[3].y))
00530 )) {
00531 pointf pf1, pf2;
00532
00533 pf1 = cvt2ptf(A[0]);
00534 pf2 = cvt2ptf(A[2]);
00535 if (filled) {
00536 gvcolor_t color;
00537
00538 colorxlate(S[SP].color, &color, HSVA_DOUBLE);
00539 fprintf(Output_file, "setfillval %f\n", color.u.HSVA[2]);
00540 }
00541 fprintf(Output_file, "box attrs%d %swid %.5f ht %.5f at (%.5f,%.5f);\n", SP, filled ? "fill " : "", Scale * fabs(pf1.x - pf2.x), Scale * fabs(pf1.y - pf2.y),
00542 Scale * (pf1.x + pf2.x) / 2.0, Scale * (pf1.y + pf2.y) / 2.0);
00543 return;
00544 }
00545 if (onetime && filled) {
00546 unsupported("shape fill");
00547 onetime = FALSE;
00548 }
00549 point_list_out(A, n, TRUE);
00550 }
00551
00552 static void pic_polyline(point * A, int n)
00553 {
00554 point_list_out(A, n, FALSE);
00555 }
00556
00557 static void pic_usershape(usershape_t *us, boxf b, point *A, int n, boolean filled)
00558 {
00559
00560
00561
00562 fprintf(Output_file, "define %s {\n", us->name);
00563 fprintf(Output_file, "}\n%s\n", us->name);
00564 }
00565
00566 static void pic_bezier(point * A, int n, int arrow_at_start,
00567 int arrow_at_end, int filled)
00568 {
00569 pointf V[4], p;
00570 int i, j, m, step;
00571 char buf[SMALLBUF];
00572
00573 if (arrow_at_start || arrow_at_end)
00574 warn("not supposed to be making arrows here!");
00575 V[3] = cvt2ptf(A[0]);
00576 for (i = m = 0; i + 3 < n; i += 3) {
00577 V[0] = V[3];
00578 for (j = 1; j <= 3; j++)
00579 V[j] = cvt2ptf(A[i + j]);
00580 p = Bezier(V, 3, 0.0, NULL, NULL);
00581 if (!i)
00582 fprintf(Output_file, "P0: %s\n", pic_fcoord(buf, p));
00583 for (step = 1; step <= BezierSubdivision; step++) {
00584 p = Bezier(V, 3, (double) step / BezierSubdivision, NULL,
00585 NULL);
00586 ++m;
00587 fprintf(Output_file, "P%d: %s\n", m, pic_fcoord(buf, p));
00588 }
00589 }
00590 for (i = 0; i + 2 <= m; i += 2)
00591 fprintf(Output_file, "move to P%d; line attrs%d to P%d then to P%d\n", i, SP, i + 1, i + 2);
00592 }
00593
00594 static void pic_comment(char *str)
00595 {
00596 fprintf(Output_file, "'\\\" %s\n", str);
00597 }
00598
00599 codegen_t PIC_CodeGen = {
00600 pic_reset,
00601 pic_begin_job, 0,
00602 pic_begin_graph, pic_end_graph,
00603 pic_begin_page, pic_end_page,
00604 0, 0,
00605 0, 0,
00606 0, 0,
00607 0, 0,
00608 pic_begin_node, 0,
00609 pic_begin_edge, 0,
00610 pic_begin_context, pic_end_context,
00611 0, 0,
00612 pic_set_font, pic_textpara,
00613 pic_set_color, pic_set_color, pic_set_style,
00614 pic_ellipse, pic_polygon,
00615 pic_bezier, pic_polyline,
00616 0,
00617 pic_comment,
00618 pic_usershape
00619 };