/misc/src/release/graphviz-2.18-1/src/graphviz-2.18/lib/common/shapes.c

Go to the documentation of this file.
00001 /* $id: shapes.c,v 1.82 2007/12/24 04:50:36 ellson Exp $ $Revision: 1.97 $ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /**********************************************************
00005 *      This software is part of the graphviz package      *
00006 *                http://www.graphviz.org/                 *
00007 *                                                         *
00008 *            Copyright (c) 1994-2004 AT&T Corp.           *
00009 *                and is licensed under the                *
00010 *            Common Public License, Version 1.0           *
00011 *                      by AT&T Corp.                      *
00012 *                                                         *
00013 *        Information and Software Systems Research        *
00014 *              AT&T Research, Florham Park NJ             *
00015 **********************************************************/
00016 
00017 #include "render.h"
00018 #include "htmltable.h"
00019 #include <limits.h>
00020 
00021 #define RBCONST 12
00022 #define RBCURVE .5
00023 
00024 static port Center = { {0, 0}, -1, 0, 0, 0, 1, 0, 0 };
00025 
00026 #define ATTR_SET(a,n) ((a) && (*(agxget(n,a->index)) != '\0'))
00027   /* Default point size = 0.05 inches or 3.6 points */
00028 #define DEF_POINT 0.05
00029   /* Minimum point size = 0.0003 inches or 0.02 points
00030    * This will make the radius 0.01 points, which is the smallest
00031    * non-zero number output by gvprintnum in gvdevice.c
00032    */
00033 #define MIN_POINT 0.0003
00034   /* extra null character needed to avoid style emitter from thinking
00035    * there are arguments.
00036    */
00037 static char *point_style[3] = { "invis\0", "filled\0", 0 };
00038 
00039 /* forward declarations of functions used in shapes tables */
00040 
00041 static void poly_init(node_t * n);
00042 static void poly_free(node_t * n);
00043 static port poly_port(node_t * n, char *portname, char *);
00044 static boolean poly_inside(inside_t * inside_context, pointf p);
00045 static int poly_path(node_t* n, port* p, int side, box rv[], int *kptr);
00046 static void poly_gencode(GVJ_t * job, node_t * n);
00047 
00048 static void record_init(node_t * n);
00049 static void record_free(node_t * n);
00050 static port record_port(node_t * n, char *portname, char *);
00051 static boolean record_inside(inside_t * inside_context, pointf p);
00052 static int record_path(node_t* n, port* p, int side, box rv[], int *kptr);
00053 static void record_gencode(GVJ_t * job, node_t * n);
00054 
00055 static void point_init(node_t * n);
00056 static void point_gencode(GVJ_t * job, node_t * n);
00057 static boolean point_inside(inside_t * inside_context, pointf p);
00058 
00059 static boolean epsf_inside(inside_t * inside_context, pointf p);
00060 static void epsf_gencode(GVJ_t * job, node_t * n);
00061 
00062 /* polygon descriptions.  "polygon" with 0 sides takes all user control */
00063 
00064 /*                             regul perip sides orien disto skew */
00065 static polygon_t p_polygon = { FALSE, 1, 0, 0., 0., 0. };
00066 
00067 /* builtin polygon descriptions */
00068 static polygon_t p_ellipse = { FALSE, 1, 1, 0., 0., 0. };
00069 static polygon_t p_circle = { TRUE, 1, 1, 0., 0., 0. };
00070 static polygon_t p_egg = { FALSE, 1, 1, 0., -.3, 0. };
00071 static polygon_t p_triangle = { FALSE, 1, 3, 0., 0., 0. };
00072 static polygon_t p_box = { FALSE, 1, 4, 0., 0., 0. };
00073 static polygon_t p_plaintext = { FALSE, 0, 4, 0., 0., 0. };
00074 static polygon_t p_diamond = { FALSE, 1, 4, 45., 0., 0. };
00075 static polygon_t p_trapezium = { FALSE, 1, 4, 0., -.4, 0. };
00076 static polygon_t p_parallelogram = { FALSE, 1, 4, 0., 0., .6 };
00077 static polygon_t p_house = { FALSE, 1, 5, 0., -.64, 0. };
00078 static polygon_t p_pentagon = { FALSE, 1, 5, 0., 0., 0. };
00079 static polygon_t p_hexagon = { FALSE, 1, 6, 0., 0., 0. };
00080 static polygon_t p_septagon = { FALSE, 1, 7, 0., 0., 0. };
00081 static polygon_t p_octagon = { FALSE, 1, 8, 0., 0., 0. };
00082 static polygon_t p_note = { FALSE, 1, 4, 0., 0., 0., DOGEAR };
00083 static polygon_t p_tab = { FALSE, 1, 4, 0., 0., 0., TAB };
00084 static polygon_t p_folder = { FALSE, 1, 4, 0., 0., 0., FOLDER };
00085 static polygon_t p_box3d = { FALSE, 1, 4, 0., 0., 0., BOX3D };
00086 static polygon_t p_component = { FALSE, 1, 4, 0., 0., 0., COMPONENT };
00087 
00088 /* redundant and undocumented builtin polygons */
00089 static polygon_t p_doublecircle = { TRUE, 2, 1, 0., 0., 0. };
00090 static polygon_t p_invtriangle = { FALSE, 1, 3, 180., 0., 0. };
00091 static polygon_t p_invtrapezium = { FALSE, 1, 4, 180., -.4, 0. };
00092 static polygon_t p_invhouse = { FALSE, 1, 5, 180., -.64, 0. };
00093 static polygon_t p_doubleoctagon = { FALSE, 2, 8, 0., 0., 0. };
00094 static polygon_t p_tripleoctagon = { FALSE, 3, 8, 0., 0., 0. };
00095 static polygon_t p_Mdiamond =
00096     { FALSE, 1, 4, 45., 0., 0., DIAGONALS | AUXLABELS };
00097 static polygon_t p_Msquare = { TRUE, 1, 4, 0., 0., 0., DIAGONALS };
00098 static polygon_t p_Mcircle =
00099     { TRUE, 1, 1, 0., 0., 0., DIAGONALS | AUXLABELS };
00100 
00101 #define IS_BOX(n) (ND_shape(n)->polygon == &p_box)
00102 
00103 /* True if style requires processing through node_round_corners. */
00104 #define SPECIAL_CORNERS(style) \
00105         ((style) & (ROUNDED | DIAGONALS | DOGEAR | TAB | FOLDER | BOX3D | COMPONENT))
00106 
00107 /*
00108  * every shape has these functions:
00109  *
00110  * void         SHAPE_init(node_t *n)
00111  *                      initialize the shape (usually at least its size).
00112  * void         SHAPE_free(node_t *n)
00113  *                      free all memory used by the shape
00114  * port         SHAPE_port(node_t *n, char *portname)
00115  *                      return the aiming point and slope (if constrained)
00116  *                      of a port.
00117  * int          SHAPE_inside(inside_t *inside_context, pointf p, edge_t *e);
00118  *                      test if point is inside the node shape which is
00119  *                      assumed convex.
00120  *                      the point is relative to the node center.  the edge
00121  *                      is passed in case the port affects spline clipping.
00122  * int          SHAPE_path(node *n, edge_t *e, int pt, box path[], int *nbox)
00123  *                      create a path for the port of e that touches n,
00124  *                      return side
00125  * void         SHAPE_gencode(GVJ_t *job, node_t *n)
00126  *                      generate graphics code for a node.
00127  *
00128  * some shapes, polygons in particular, use additional shape control data *
00129  *
00130  */
00131 
00132 static shape_functions poly_fns = {
00133     poly_init,
00134     poly_free,
00135     poly_port,
00136     poly_inside,
00137     poly_path,
00138     poly_gencode
00139 };
00140 static shape_functions point_fns = {
00141     point_init,
00142     poly_free,
00143     poly_port,
00144     point_inside,
00145     NULL,
00146     point_gencode
00147 };
00148 static shape_functions record_fns = {
00149     record_init,
00150     record_free,
00151     record_port,
00152     record_inside,
00153     record_path,
00154     record_gencode
00155 };
00156 static shape_functions epsf_fns = {
00157     epsf_init,
00158     epsf_free,
00159     poly_port,
00160     epsf_inside,
00161     NULL,
00162     epsf_gencode
00163 };
00164 
00165 static shape_desc Shapes[] = {  /* first entry is default for no such shape */
00166     {"box", &poly_fns, &p_box},
00167     {"polygon", &poly_fns, &p_polygon},
00168     {"ellipse", &poly_fns, &p_ellipse},
00169     {"oval", &poly_fns, &p_ellipse},
00170     {"circle", &poly_fns, &p_circle},
00171     {"point", &point_fns, &p_circle},
00172     {"egg", &poly_fns, &p_egg},
00173     {"triangle", &poly_fns, &p_triangle},
00174     {"none", &poly_fns, &p_plaintext},
00175     {"plaintext", &poly_fns, &p_plaintext},
00176     {"diamond", &poly_fns, &p_diamond},
00177     {"trapezium", &poly_fns, &p_trapezium},
00178     {"parallelogram", &poly_fns, &p_parallelogram},
00179     {"house", &poly_fns, &p_house},
00180     {"pentagon", &poly_fns, &p_pentagon},
00181     {"hexagon", &poly_fns, &p_hexagon},
00182     {"septagon", &poly_fns, &p_septagon},
00183     {"octagon", &poly_fns, &p_octagon},
00184     {"note", &poly_fns, &p_note},
00185     {"tab", &poly_fns, &p_tab},
00186     {"folder", &poly_fns, &p_folder},
00187     {"box3d", &poly_fns, &p_box3d},
00188     {"component", &poly_fns, &p_component},
00189     {"rect", &poly_fns, &p_box},
00190     {"rectangle", &poly_fns, &p_box},
00191     {"doublecircle", &poly_fns, &p_doublecircle},
00192     {"doubleoctagon", &poly_fns, &p_doubleoctagon},
00193     {"tripleoctagon", &poly_fns, &p_tripleoctagon},
00194     {"invtriangle", &poly_fns, &p_invtriangle},
00195     {"invtrapezium", &poly_fns, &p_invtrapezium},
00196     {"invhouse", &poly_fns, &p_invhouse},
00197     {"Mdiamond", &poly_fns, &p_Mdiamond},
00198     {"Msquare", &poly_fns, &p_Msquare},
00199     {"Mcircle", &poly_fns, &p_Mcircle},
00200 /*  *** shapes other than polygons  *** */
00201     {"record", &record_fns, NULL},
00202     {"Mrecord", &record_fns, NULL},
00203     {"epsf", &epsf_fns, NULL},
00204     {NULL, NULL, NULL}
00205 };
00206 
00207 static void unrecognized(node_t * n, char *p)
00208 {
00209     agerr(AGWARN, "node %s, port %s unrecognized\n", n->name, p);
00210 }
00211 
00212 static double quant(double val, double q)
00213 {
00214     int i;
00215     i = val / q;
00216     if (i * q + .00001 < val)
00217         i++;
00218     return i * q;
00219 }
00220 
00221 static int same_side(pointf p0, pointf p1, pointf L0, pointf L1)
00222 {
00223     int s0, s1;
00224     double a, b, c;
00225 
00226     /* a x + b y = c */
00227     a = -(L1.y - L0.y);
00228     b = (L1.x - L0.x);
00229     c = a * L0.x + b * L0.y;
00230 
00231     s0 = (a * p0.x + b * p0.y - c >= 0);
00232     s1 = (a * p1.x + b * p1.y - c >= 0);
00233     return (s0 == s1);
00234 }
00235 
00236 static
00237 void pencolor(GVJ_t * job, node_t * n)
00238 {
00239     char *color;
00240 
00241     color = late_nnstring(n, N_color, "");
00242     if (color[0])
00243         gvrender_set_pencolor(job, color);
00244     else
00245         gvrender_set_pencolor(job, DEFAULT_COLOR);
00246 }
00247 
00248 static
00249 char* findPen(node_t * n)
00250 {
00251     char *color;
00252 
00253     color = late_nnstring(n, N_color, "");
00254     if (color[0])
00255         return color;
00256     else
00257         return DEFAULT_COLOR;
00258 }
00259 
00260 static
00261 char *findFillDflt(node_t * n, char* dflt)
00262 {
00263     char *color;
00264 
00265     color = late_nnstring(n, N_fillcolor, "");
00266     if (!color[0]) {
00267         /* for backward compatibilty, default fill is same as pen */
00268         color = late_nnstring(n, N_color, "");
00269         if (!color[0]) {
00270 #ifdef WITH_CODEGENS
00271             if (Output_lang == MIF) color = "black";
00272             else
00273 #endif
00274             color = dflt;
00275         }
00276     }
00277     return color;
00278 }
00279 
00280 static
00281 char *findFill (node_t * n)
00282 {
00283     return (findFillDflt (n, DEFAULT_FILL));
00284 }
00285 
00286 static char **checkStyle(node_t * n, int *flagp)
00287 {
00288     char *style;
00289     char **pstyle = 0;
00290     int istyle = 0;
00291     polygon_t *poly;
00292 
00293     style = late_nnstring(n, N_style, "");
00294     if (style[0]) {
00295         char **pp;
00296         char **qp;
00297         char *p;
00298         pp = pstyle = parse_style(style);
00299         while ((p = *pp)) {
00300             if (streq(p, "filled")) {
00301                 istyle |= FILLED;
00302                 pp++;
00303             } else if (streq(p, "rounded")) {
00304                 istyle |= ROUNDED;
00305                 qp = pp; /* remove rounded from list passed to renderer */
00306                 do {
00307                     qp++;
00308                     *(qp-1) = *qp;
00309                 } while (*qp);
00310             } else if (streq(p, "diagonals")) {
00311                 istyle |= DIAGONALS;
00312                 qp = pp; /* remove diagonals from list passed to renderer */
00313                 do {
00314                     qp++;
00315                     *(qp-1) = *qp;
00316                 } while (*qp);
00317             } else if (streq(p, "invis")) {
00318                 istyle |= INVISIBLE;
00319                 pp++;
00320             }
00321             else pp++;
00322         }
00323     }
00324     if ((poly = ND_shape(n)->polygon))
00325         istyle |= poly->option;
00326 
00327     *flagp = istyle;
00328     return pstyle;
00329 }
00330 
00331 static int stylenode(GVJ_t * job, node_t * n)
00332 {
00333     char **pstyle, *s;
00334     int istyle, penwidth;
00335 
00336     if ((pstyle = checkStyle(n, &istyle)))
00337         gvrender_set_style(job, pstyle);
00338 
00339     if (N_penwidth && ((s=agxget(n, N_penwidth->index)) && s[0])) {
00340         penwidth = late_double(n, N_penwidth, 1.0, 0.0);
00341         gvrender_set_penwidth(job, penwidth);
00342     }
00343 
00344     return istyle;
00345 }
00346 
00347 static void Mcircle_hack(GVJ_t * job, node_t * n)
00348 {
00349     double x, y;
00350     pointf AF[2], p, coord;
00351 
00352     y = .7500;
00353     x = .6614;                  /* x^2 + y^2 = 1.0 */
00354     p.y = y * ND_ht_i(n) / 2.0;
00355     p.x = ND_rw_i(n) * x;       /* assume node is symmetric */
00356 
00357     P2PF(ND_coord_i(n), coord);
00358     AF[0] = add_pointfs(p, coord);
00359     AF[1].y = AF[0].y;
00360     AF[1].x = AF[0].x - 2 * p.x;
00361     gvrender_polyline(job, AF, 2);
00362     AF[0].y -= 2 * p.y;
00363     AF[1].y = AF[0].y;
00364     gvrender_polyline(job, AF, 2);
00365 }
00366 
00367 static pointf interpolate(double t, pointf p0, pointf p1)
00368 {
00369     pointf rv;
00370     rv.x = p0.x + t * (p1.x - p0.x);
00371     rv.y = p0.y + t * (p1.y - p0.y);
00372     return rv;
00373 }
00374 
00375 void round_corners(GVJ_t * job, char* fillc, char* penc, pointf * AF, 
00376                         int sides, int style)
00377 {
00378     pointf *B, C[4], *D, p0, p1;
00379     double d, dx, dy, t;
00380     int i, seg, mode;
00381 
00382     if (style & DIAGONALS)
00383         mode = DIAGONALS;
00384     else if (style & (DOGEAR | TAB | FOLDER | BOX3D | COMPONENT))
00385         mode = style & (DOGEAR | TAB | FOLDER | BOX3D | COMPONENT);
00386     else
00387         mode = ROUNDED;
00388     B = N_NEW(4 * sides + 4, pointf);
00389     i = 0;
00390     for (seg = 0; seg < sides; seg++) {
00391         p0 = AF[seg];
00392         if (seg < sides - 1)
00393             p1 = AF[seg + 1];
00394         else
00395             p1 = AF[0];
00396         dx = p1.x - p0.x;
00397         dy = p1.y - p0.y;
00398         d = sqrt(dx * dx + dy * dy);
00399         /*t = ((mode == ROUNDED)? RBCONST / d : .5); */
00400         t = RBCONST / d;
00401         if (style & (BOX3D | COMPONENT))
00402                 t /= 3;
00403         else if (style & DOGEAR)
00404                 t /= 2;
00405         if (mode != ROUNDED)
00406             B[i++] = p0;
00407         if (mode == ROUNDED)
00408             B[i++] = interpolate(RBCURVE * t, p0, p1);
00409         B[i++] = interpolate(t, p0, p1);
00410         B[i++] = interpolate(1.0 - t, p0, p1);
00411         if (mode == ROUNDED)
00412             B[i++] = interpolate(1.0 - RBCURVE * t, p0, p1);
00413     }
00414     B[i++] = B[0];
00415     B[i++] = B[1];
00416     B[i++] = B[2];
00417 
00418     switch (mode) {
00419     case ROUNDED:
00420         if (style & FILLED) {
00421             int j = 0;
00422             pointf* pts = N_GNEW(2*sides,pointf);
00423             gvrender_begin_context(job);
00424             gvrender_set_pencolor (job, fillc);
00425             gvrender_set_fillcolor (job, fillc);
00426             for (seg = 0; seg < sides; seg++) {
00427                 pts[j++] = B[4 * seg + 1];
00428                 pts[j++] = B[4 * seg + 2];
00429             }
00430             gvrender_polygon(job, pts, 2*sides, TRUE);
00431             free (pts);
00432             for (seg = 0; seg < sides; seg++) {
00433                 gvrender_beziercurve(job, B + 4 * seg + 2, 4, FALSE, FALSE, TRUE);
00434             }
00435             gvrender_end_context(job);
00436         }
00437         gvrender_set_pencolor(job, penc);
00438         for (seg = 0; seg < sides; seg++) {
00439             gvrender_polyline(job, B + 4 * seg + 1, 2);
00440             gvrender_beziercurve(job, B + 4 * seg + 2, 4, FALSE, FALSE, FALSE);
00441         }
00442         break;
00443     case DIAGONALS:
00444         /* diagonals are weird.  rewrite someday. */
00445         gvrender_set_pencolor(job, penc);
00446         if (style & FILLED)
00447             gvrender_set_fillcolor(job, fillc); /* emit fill color */
00448         gvrender_polygon(job, AF, sides, style & FILLED);
00449 
00450         for (seg = 0; seg < sides; seg++) {
00451 #ifdef NOTDEF
00452             C[0] = B[3 * seg];
00453             C[1] = B[3 * seg + 3];
00454             gvrender_polyline(job, C, 2);
00455 #endif
00456             C[0] = B[3 * seg + 2];
00457             C[1] = B[3 * seg + 4];
00458             gvrender_polyline(job, C, 2);
00459         }
00460         break;
00461     case DOGEAR:
00462         gvrender_set_pencolor(job, penc);
00463         if (style & FILLED)
00464             gvrender_set_fillcolor(job, fillc); /* emit fill color */
00465         /* Add the cutoff edge. */
00466         D = N_NEW(sides + 1, pointf);
00467         for (seg = 1; seg < sides; seg++)
00468             D[seg] = AF[seg];
00469         D[0] = B[3 * (sides - 1) + 4];
00470         D[sides] = B[3 * (sides - 1) + 2];
00471         gvrender_polygon(job, D, sides + 1, style & FILLED);
00472         free(D);
00473 
00474         /* Draw the inner edge. */
00475         seg = sides - 1;
00476         C[0] = B[3 * seg + 2];
00477         C[1] = B[3 * seg + 4];
00478         C[2].x = C[1].x + (C[0].x - B[3 * seg + 3].x);
00479         C[2].y = C[1].y + (C[0].y - B[3 * seg + 3].y);
00480         gvrender_polyline(job, C + 1, 2);
00481         C[1] = C[2];
00482         gvrender_polyline(job, C, 2);
00483         break;
00484     case TAB:
00485       /*
00486        * Adjust the perimeter for the protrusions.
00487        *
00488        *  D[3] +--+ D[2]
00489        *       |  |          B[1]
00490        *  B[3] +  +----------+--+ AF[0]=B[0]=D[0]
00491        *       |  B[2]=D[1]     |
00492        *  B[4] +                |
00493        *       |                |
00494        *  B[5] +                |
00495        *       +----------------+
00496        *
00497        */
00498         gvrender_set_pencolor(job, penc);
00499         if (style & FILLED)
00500             gvrender_set_fillcolor(job, fillc); /* emit fill color */
00501         /* Add the tab edges. */
00502         D = N_NEW(sides + 2, pointf);
00503         D[0] = AF[0];
00504         D[1] = B[2];
00505         D[2].x = B[2].x + (B[3].x - B[4].x) / 3;
00506         D[2].y = B[2].y + (B[3].y - B[4].y) / 3;
00507         D[3].x = B[3].x + (B[3].x - B[4].x) / 3;
00508         D[3].y = B[3].y + (B[3].y - B[4].y) / 3;
00509         for (seg = 4; seg < sides + 2; seg++)
00510             D[seg] = AF[seg - 2];
00511         gvrender_polygon(job, D, sides + 2, style & FILLED);
00512         free(D);
00513 
00514 
00515         /* Draw the inner edge. */
00516         C[0] = B[3];
00517         C[1] = B[2];
00518         gvrender_polyline(job, C, 2);
00519       break;
00520     case FOLDER:
00521       /*
00522        * Adjust the perimeter for the protrusions.
00523        *
00524        *            D[2] +----+ D[1]
00525        *  B[3]=         /      \
00526        *  D[4] +--+----+     +  + AF[0]=B[0]=D[0]
00527        *       |  B[2] D[3] B[1]|
00528        *  B[4] +                |
00529        *       |                |
00530        *  B[5] +                |
00531        *       +----------------+
00532        *
00533        */
00534       gvrender_set_pencolor(job, penc);
00535       if (style & FILLED)
00536           gvrender_set_fillcolor(job, fillc); /* emit fill color */
00537       /* Add the folder edges. */
00538       D = N_NEW(sides + 3, pointf);
00539       D[0] = AF[0];
00540       D[1].x = AF[0].x - (AF[0].x - B[1].x) / 4;
00541       D[1].y = AF[0].y + (B[3].y - B[4].y) / 3;
00542       D[2].x = AF[0].x - 2 * (AF[0].x - B[1].x);
00543       D[2].y = D[1].y;
00544       D[3].x = AF[0].x - 2.25 * (AF[0].x - B[1].x);
00545       D[3].y = B[3].y;
00546       D[4].x = B[3].x;
00547       D[4].y = B[3].y;
00548       for (seg = 4; seg < sides + 3; seg++)
00549           D[seg] = AF[seg - 3];
00550       gvrender_polygon(job, D, sides + 3, style & FILLED);
00551       free(D);
00552         break;
00553     case BOX3D:
00554         assert(sides == 4);
00555         gvrender_set_pencolor(job, penc);
00556         if (style & FILLED)
00557             gvrender_set_fillcolor(job, fillc); /* emit fill color */
00558         /* Adjust for the cutoff edges. */
00559         D = N_NEW(sides + 2, pointf);
00560         D[0] = AF[0];
00561         D[1] = B[2];
00562         D[2] = B[4];
00563         D[3] = AF[2];
00564         D[4] = B[8];
00565         D[5] = B[10];
00566         gvrender_polygon(job, D, sides + 2, style & FILLED);
00567         free(D);
00568 
00569         /* Draw the inner vertices. */
00570         C[0].x = B[1].x + (B[11].x - B[0].x);
00571         C[0].y = B[1].y + (B[11].y - B[0].y);
00572         C[1] = B[4];
00573         gvrender_polyline(job, C, 2);
00574         C[1] = B[8];
00575         gvrender_polyline(job, C, 2);
00576         C[1] = B[0];
00577         gvrender_polyline(job, C, 2);
00578         break;
00579     case COMPONENT:
00580         assert(sides == 4);
00581         gvrender_set_pencolor(job, penc);
00582         if (style & FILLED)
00583             gvrender_set_fillcolor(job, fillc); /* emit fill color */
00584         /*
00585          * Adjust the perimeter for the protrusions.
00586          *
00587          *  D[1] +----------------+ D[0]
00588          *       |                |
00589          *  3+---+2               |
00590          *   |                    |
00591          *  4+---+5               |
00592          *       |                |
00593          *  7+---+6               |
00594          *   |                    |
00595          *  8+---+9               |
00596          *       |                |
00597          *     10+----------------+ D[11]
00598          *
00599          */
00600         D = N_NEW(sides + 8, pointf);
00601         D[0] = AF[0];
00602         D[1] = AF[1];
00603         D[2].x = B[3].x + (B[4].x - B[3].x);
00604         D[2].y = B[3].y + (B[4].y - B[3].y);
00605         D[3].x = D[2].x + (B[3].x - B[2].x);
00606         D[3].y = D[2].y + (B[3].y - B[2].y);
00607         D[4].x = D[3].x + (B[4].x - B[3].x);
00608         D[4].y = D[3].y + (B[4].y - B[3].y);
00609         D[5].x = D[4].x + (D[2].x - D[3].x);
00610         D[5].y = D[4].y + (D[2].y - D[3].y);
00611 
00612         D[9].x = B[6].x + (B[5].x - B[6].x);
00613         D[9].y = B[6].y + (B[5].y - B[6].y);
00614         D[8].x = D[9].x + (B[6].x - B[7].x);
00615         D[8].y = D[9].y + (B[6].y - B[7].y);
00616         D[7].x = D[8].x + (B[5].x - B[6].x);
00617         D[7].y = D[8].y + (B[5].y - B[6].y);
00618         D[6].x = D[7].x + (D[9].x - D[8].x);
00619         D[6].y = D[7].y + (D[9].y - D[8].y);
00620 
00621         D[10] = AF[2];
00622         D[11] = AF[3];
00623         gvrender_polygon(job, D, sides + 8, style & FILLED);
00624 
00625         /* Draw the internal vertices. */
00626         C[0] = D[2];
00627         C[1].x = D[2].x - (D[3].x - D[2].x);
00628         C[1].y = D[2].y - (D[3].y - D[2].y);
00629         C[2].x = C[1].x + (D[4].x - D[3].x);
00630         C[2].y = C[1].y + (D[4].y - D[3].y);
00631         C[3] = D[5];
00632         gvrender_polyline(job, C, 4);
00633         C[0] = D[6];
00634         C[1].x = D[6].x - (D[7].x - D[6].x);
00635         C[1].y = D[6].y - (D[7].y - D[6].y);
00636         C[2].x = C[1].x + (D[8].x - D[7].x);
00637         C[2].y = C[1].y + (D[8].y - D[7].y);
00638         C[3] = D[9];
00639         gvrender_polyline(job, C, 4);
00640 
00641         free(D);
00642         break;
00643     }
00644     free(B);
00645 }
00646 
00647 static void 
00648 node_round_corners(GVJ_t * job, node_t* n, pointf * AF, int sides, int style)
00649 {
00650     round_corners(job, findFill(n), findPen(n), AF, sides, style);
00651 }
00652 
00653 /*=============================poly start=========================*/
00654 
00655 /* userSize;
00656  * Return maximum size, in points, of width and height supplied
00657  * by user, if any. Return 0 otherwise.
00658  */
00659 static double userSize(node_t * n)
00660 {
00661     double w, h;
00662     w = late_double(n, N_width, 0.0, MIN_NODEWIDTH);
00663     h = late_double(n, N_height, 0.0, MIN_NODEHEIGHT);
00664     return POINTS(MAX(w, h));
00665 }
00666 
00667 shape_kind shapeOf(node_t * n)
00668 {
00669     shape_desc *sh = ND_shape(n);
00670     void (*ifn) (node_t *);
00671 
00672     if (!sh)
00673         return SH_UNSET;
00674     ifn = ND_shape(n)->fns->initfn;
00675     if (ifn == poly_init)
00676         return SH_POLY;
00677     else if (ifn == record_init)
00678         return SH_RECORD;
00679     else if (ifn == point_init)
00680         return SH_POINT;
00681     else if (ifn == epsf_init)
00682         return SH_EPSF;
00683     else
00684         return SH_UNSET;
00685 }
00686 
00687 boolean isPolygon(node_t * n)
00688 {
00689     return (ND_shape(n) && (ND_shape(n)->fns->initfn == poly_init));
00690 }
00691 
00692 static void poly_init(node_t * n)
00693 {
00694     pointf dimen, min_bb, bb;
00695     point imagesize;
00696     pointf P, Q, R;
00697     pointf *vertices;
00698     char *p, *sfile;
00699     double temp, alpha, beta, gamma;
00700     double orientation, distortion, skew;
00701     double sectorangle, sidelength, skewdist, gdistortion, gskew;
00702     double angle, sinx, cosx, xmax, ymax, scalex, scaley;
00703     double width, height, marginx, marginy;
00704     int regular, peripheries, sides;
00705     int i, j, outp, labelloc;
00706     polygon_t *poly = NEW(polygon_t);
00707 
00708     regular = ND_shape(n)->polygon->regular;
00709     peripheries = ND_shape(n)->polygon->peripheries;
00710     sides = ND_shape(n)->polygon->sides;
00711     orientation = ND_shape(n)->polygon->orientation;
00712     skew = ND_shape(n)->polygon->skew;
00713     distortion = ND_shape(n)->polygon->distortion;
00714     regular |= mapbool(agget(n, "regular"));
00715 
00716     /* all calculations in floating point POINTS */
00717 
00718     /* make x and y dimensions equal if node is regular
00719      *   If the user has specified either width or height, use the max.
00720      *   Else use minimum default value.
00721      * If node is not regular, use the current width and height.
00722      */
00723     if (regular) {
00724         double sz = userSize(n);
00725         if (sz > 0.0)
00726             width = height = sz;
00727         else {
00728             width = ND_width(n);
00729             height = ND_height(n);
00730             width = height = POINTS(MIN(width, height));
00731         }
00732     } else {
00733         width = POINTS(ND_width(n));
00734         height = POINTS(ND_height(n));
00735     }
00736 
00737     peripheries = late_int(n, N_peripheries, peripheries, 0);
00738     orientation += late_double(n, N_orientation, 0.0, -360.0);
00739     if (sides == 0) {           /* not for builtins */
00740         skew = late_double(n, N_skew, 0.0, -100.0);
00741         sides = late_int(n, N_sides, 4, 0);
00742         distortion = late_double(n, N_distortion, 0.0, -100.0);
00743     }
00744 
00745     /* get label dimensions */
00746     dimen = ND_label(n)->dimen;
00747 
00748     /* minimal whitespace around label */
00749     if ((dimen.x > 0.0) || (dimen.y > 0.0)) {
00750         /* padding */
00751         if ((p = agget(n, "margin"))) {
00752             i = sscanf(p, "%lf,%lf", &marginx, &marginy);
00753             if (marginx < 0) marginx = 0;
00754             if (marginy < 0) marginy = 0;
00755             if (i > 0) {
00756                 dimen.x += 2 * POINTS(marginx);
00757                 if (i > 1)
00758                     dimen.y += 2 * POINTS(marginy);
00759                 else
00760                     dimen.y += 2 * POINTS(marginx);
00761             } else
00762                 PAD(dimen);
00763         } else
00764             PAD(dimen);
00765     }
00766     /* quantization */
00767     if ((temp = GD_drawing(n->graph)->quantum) > 0.0) {
00768         temp = POINTS(temp);
00769         dimen.x = quant(dimen.x, temp);
00770         dimen.y = quant(dimen.y, temp);
00771     }
00772 
00773     imagesize.x = imagesize.y = 0;
00774     if (ND_shape(n)->usershape) {
00775             /* custom requires a shapefile
00776              * not custom is an adaptable user shape such as a postscript
00777              * function.
00778              */
00779         if (streq(ND_shape(n)->name, "custom")) {
00780             sfile = agget(n, "shapefile");
00781             imagesize = gvusershape_size(n->graph, sfile);
00782             if ((imagesize.x == -1) && (imagesize.y == -1)) {
00783                 agerr(AGWARN,
00784                       "No or improper shapefile=\"%s\" for node \"%s\"\n",
00785                       (sfile ? sfile : "<nil>"), n->name);
00786                 imagesize.x = imagesize.y = 0;
00787             }
00788             else {
00789                 GD_has_images(n->graph) = TRUE;
00790                 imagesize.x += 2; /* some fixed padding */
00791                 imagesize.y += 2;
00792             }
00793         }
00794     }
00795     else  if ((sfile = agget(n, "image"))) {
00796         imagesize = gvusershape_size(n->graph, sfile);
00797         if ((imagesize.x == -1) && (imagesize.y == -1)) {
00798             agerr(AGWARN,
00799                 "No or improper image=\"%s\" for node \"%s\"\n",
00800                 (sfile ? sfile : "<nil>"), n->name);
00801             imagesize.x = imagesize.y = 0;
00802         }
00803         else {
00804             GD_has_images(n->graph) = TRUE;
00805             imagesize.x += 2; /* some fixed padding */
00806             imagesize.y += 2;
00807         }
00808     }
00809 
00810     /* initialize node bb to labelsize */
00811     bb.x = MAX(dimen.x, imagesize.x);
00812     bb.y = MAX(dimen.y, imagesize.y);
00813 
00814     /* I don't know how to distort or skew ellipses in postscript */
00815     /* Convert request to a polygon with a large number of sides */
00816     if ((sides <= 2) && ((distortion != 0.) || (skew != 0.))) {
00817         sides = 120;
00818     }
00819 
00820     /* extra sizing depends on if label is centered vertically */
00821     p = agget(n, "labelloc");
00822     if (p && p[0] == 't')
00823         labelloc = +1;
00824     else if (p && p[0] == 'b')
00825         labelloc = -1;
00826     else
00827         labelloc = 0;
00828 
00829     if (sides == 4 && (ROUND(orientation) % 90) == 0
00830                && distortion == 0. && skew == 0.) {
00831         /* for regular boxes the fit should be exact */
00832     } else {
00833         /* for all other shapes, compute the inner ellipse
00834            and then pad for that  */
00835         temp = bb.y * SQRT2;
00836         /* if there is height to spare and the label is centered vertically */
00837         if (height > temp && labelloc == 0) {
00838             bb.x *= sqrt(1. / (1. - SQR(bb.y / height)));
00839             bb.y = height;
00840         }
00841         else {
00842             bb.x *= SQRT2;
00843             bb.y = temp;
00844         }
00845 #if 0
00846         if (sides > 2) {
00847             temp = cos(M_PI / sides);
00848             bb.x /= temp;
00849             bb.y /= temp;
00850             /* FIXME - for odd-sided polygons, e.g. triangles, there
00851             would be a better fit with some vertical adjustment of the shape */
00852         }
00853 #endif
00854     }
00855 
00856     /* at this point, bb is the minimum size of node that can hold the label */
00857     min_bb = bb;
00858 
00859     /* increase node size to width/height if needed */
00860     if (mapbool(late_string(n, N_fixed, "false"))) {
00861         if ((width < bb.x) || (height < bb.y))
00862             agerr(AGWARN,
00863                   "node '%s', graph '%s' size too small for label\n",
00864                   n->name, n->graph->name);
00865         bb.x = width;
00866         bb.y = height;
00867     }
00868     else {
00869         bb.x = width = MAX(width, bb.x);
00870         bb.y = height = MAX(height, bb.y);
00871     }
00872 
00873     /* If regular, make dimensions the same.
00874      * Need this to guarantee final node size is regular.
00875      */
00876     if (regular) {
00877         bb.x = bb.y = MAX(bb.x, bb.y);
00878     }
00879 
00880     /* adjust text horizontal justification */
00881     if (!mapbool(late_string(n, N_nojustify, "false"))) {
00882         temp = bb.x - min_bb.x;
00883         if (dimen.x < imagesize.x)
00884             temp += imagesize.x - dimen.x;
00885         if (temp > 0) 
00886             ND_label(n)->d.x = temp;
00887     }
00888 
00889     /* adjust text vertical location */
00890     temp = bb.y - min_bb.y;
00891     if (dimen.y < imagesize.y)
00892         temp += imagesize.y - dimen.y;
00893     if (temp > 0) {
00894         if (labelloc < 0)
00895             ND_label(n)->d.y = -temp;
00896         else if (labelloc > 0)
00897             ND_label(n)->d.y = temp;
00898         else
00899             ND_label(n)->d.y = 0;
00900     }
00901 
00902     outp = peripheries;
00903     if (peripheries < 1)
00904         outp = 1;
00905     if (sides < 3) {            /* ellipses */
00906         sides = 2;
00907         vertices = N_NEW(outp * sides, pointf);
00908         P.x = bb.x / 2.;
00909         P.y = bb.y / 2.;
00910         vertices[0].x = -P.x;
00911         vertices[0].y = -P.y;
00912         vertices[1] = P;
00913         if (peripheries > 1) {
00914             for (j = 1, i = 2; j < peripheries; j++) {
00915                 P.x += GAP;
00916                 P.y += GAP;
00917                 vertices[i].x = -P.x;
00918                 vertices[i].y = -P.y;
00919                 i++;
00920                 vertices[i].x = P.x;
00921                 vertices[i].y = P.y;
00922                 i++;
00923             }
00924             bb.x = 2. * P.x;
00925             bb.y = 2. * P.y;
00926         }
00927     } else {
00928         vertices = N_NEW(outp * sides, pointf);
00929         sectorangle = 2. * M_PI / sides;
00930         sidelength = sin(sectorangle / 2.);
00931         skewdist = hypot(fabs(distortion) + fabs(skew), 1.);
00932         gdistortion = distortion * SQRT2 / cos(sectorangle / 2.);
00933         gskew = skew / 2.;
00934         angle = (sectorangle - M_PI) / 2.;
00935         sincos(angle, &sinx, &cosx);
00936         R.x = .5 * cosx;
00937         R.y = .5 * sinx;
00938         xmax = ymax = 0.;
00939         angle += (M_PI - sectorangle) / 2.;
00940         for (i = 0; i < sides; i++) {
00941 
00942             /*next regular vertex */
00943             angle += sectorangle;
00944             sincos(angle, &sinx, &cosx);
00945             R.x += sidelength * cosx;
00946             R.y += sidelength * sinx;
00947 
00948             /*distort and skew */
00949             P.x = R.x * (skewdist + R.y * gdistortion) + R.y * gskew;
00950             P.y = R.y;
00951 
00952             /*orient P.x,P.y */
00953             alpha = RADIANS(orientation) + atan2(P.y, P.x);
00954             sincos(alpha, &sinx, &cosx);
00955             P.x = P.y = hypot(P.x, P.y);
00956             P.x *= cosx;
00957             P.y *= sinx;
00958 
00959             /*scale for label */
00960             P.x *= bb.x;
00961             P.y *= bb.y;
00962 
00963             /*find max for bounding box */
00964             xmax = MAX(fabs(P.x), xmax);
00965             ymax = MAX(fabs(P.y), ymax);
00966 
00967             /* store result in array of points */
00968             vertices[i] = P;
00969         }
00970 
00971         /* apply minimum dimensions */
00972         xmax *= 2.;
00973         ymax *= 2.;
00974         bb.x = MAX(width, xmax);
00975         bb.y = MAX(height, ymax);
00976         scalex = bb.x / xmax;
00977         scaley = bb.y / ymax;
00978 
00979         for (i = 0; i < sides; i++) {
00980             P = vertices[i];
00981             P.x *= scalex;
00982             P.y *= scaley;
00983             vertices[i] = P;
00984         }
00985 
00986         if (peripheries > 1) {
00987             Q = vertices[(sides - 1)];
00988             R = vertices[0];
00989             beta = atan2(R.y - Q.y, R.x - Q.x);
00990             for (i = 0; i < sides; i++) {
00991 
00992                 /*for each vertex find the bisector */
00993                 P = Q;
00994                 Q = R;
00995                 R = vertices[(i + 1) % sides];
00996                 alpha = beta;
00997                 beta = atan2(R.y - Q.y, R.x - Q.x);
00998                 gamma = (alpha + M_PI - beta) / 2.;
00999 
01000                 /*find distance along bisector to */
01001                 /*intersection of next periphery */
01002                 temp = GAP / sin(gamma);
01003 
01004                 /*convert this distance to x and y */
01005                 sincos((alpha - gamma), &sinx, &cosx);
01006                 sinx *= temp;
01007                 cosx *= temp;
01008 
01009                 /*save the vertices of all the */
01010                 /*peripheries at this base vertex */
01011                 for (j = 1; j < peripheries; j++) {
01012                     Q.x += cosx;
01013                     Q.y += sinx;
01014                     vertices[i + j * sides] = Q;
01015                 }
01016             }
01017             for (i = 0; i < sides; i++) {
01018                 P = vertices[i + (peripheries - 1) * sides];
01019                 bb.x = MAX(2. * fabs(P.x), bb.x);
01020                 bb.y = MAX(2. * fabs(P.y), bb.y);
01021             }
01022         }
01023     }
01024     poly->regular = regular;
01025     poly->peripheries = peripheries;
01026     poly->sides = sides;
01027     poly->orientation = orientation;
01028     poly->skew = skew;
01029     poly->distortion = distortion;
01030     poly->vertices = vertices;
01031 
01032     ND_width(n) = PS2INCH(bb.x);
01033     ND_height(n) = PS2INCH(bb.y);
01034     ND_shape_info(n) = (void *) poly;
01035 }
01036 
01037 static void poly_free(node_t * n)
01038 {
01039     polygon_t *p = ND_shape_info(n);
01040 
01041     if (p) {
01042         free(p->vertices);
01043         free(p);
01044     }
01045 }
01046 
01047 #define GET_PORT_BOX(n,e) ((n) == (e)->head ? ED_head_port(e).bp : ED_tail_port(e).bp)
01048 
01049 static boolean poly_inside(inside_t * inside_context, pointf p)
01050 {
01051     static node_t *lastn;       /* last node argument */
01052     static polygon_t *poly;
01053     static int last, outp, sides;
01054     static pointf O;            /* point (0,0) */
01055     static pointf *vertex;
01056     static double xsize, ysize, scalex, scaley, box_URx, box_URy;
01057 
01058     int i, i1, j, s;
01059     pointf P, Q, R;
01060     box *bp = inside_context->s.bp;
01061     node_t *n = inside_context->s.n;
01062 
01063     P = ccwrotatepf(p, 90*GD_rankdir(n->graph));
01064 
01065     /* Quick test if port rectangle is target */
01066     if (bp) {
01067         box bbox = *bp;
01068         return INSIDE(P, bbox);
01069     }
01070 
01071     if (n != lastn) {
01072         poly = (polygon_t *) ND_shape_info(n);
01073         vertex = poly->vertices;
01074         sides = poly->sides;
01075 
01076         /* get point and node size adjusted for rankdir=LR */
01077         if (GD_flip(n->graph)) {
01078             ysize = ND_lw_i(n) + ND_rw_i(n);
01079             xsize = ND_ht_i(n);
01080         } else {
01081             xsize = ND_lw_i(n) + ND_rw_i(n);
01082             ysize = ND_ht_i(n);
01083         }
01084 
01085         /* scale */
01086         if (xsize == 0.0)
01087             xsize = 1.0;
01088         if (ysize == 0.0)
01089             ysize = 1.0;
01090         scalex = POINTS(ND_width(n)) / xsize;
01091         scaley = POINTS(ND_height(n)) / ysize;
01092         box_URx = POINTS(ND_width(n)) / 2.0;
01093         box_URy = POINTS(ND_height(n)) / 2.0;
01094 
01095         /* index to outer-periphery */
01096         outp = (poly->peripheries - 1) * sides;
01097         if (outp < 0)
01098             outp = 0;
01099         lastn = n;
01100     }
01101 
01102     /* scale */
01103     P.x *= scalex;
01104     P.y *= scaley;
01105 
01106     /* inside bounding box? */
01107     if ((fabs(P.x) > box_URx) || (fabs(P.y) > box_URy))
01108         return FALSE;
01109 
01110     /* ellipses */
01111     if (sides <= 2)
01112         return (hypot(P.x / box_URx, P.y / box_URy) < 1.);
01113 
01114     /* use fast test in case we are converging on a segment */
01115     i = last % sides;           /*in case last left over from larger polygon */
01116     i1 = (i + 1) % sides;
01117     Q = vertex[i + outp];
01118     R = vertex[i1 + outp];
01119     if (!(same_side(P, O, Q, R)))
01120         return FALSE;
01121     if ((s = same_side(P, Q, R, O)) && (same_side(P, R, O, Q)))
01122         return TRUE;
01123     for (j = 1; j < sides; j++) {
01124         if (s) {
01125             i = i1;
01126             i1 = (i + 1) % sides;
01127         } else {
01128             i1 = i;
01129             i = (i + sides - 1) % sides;
01130         }
01131         if (!(same_side(P, O, vertex[i + outp], vertex[i1 + outp]))) {
01132             last = i;
01133             return FALSE;
01134         }
01135     }
01136     last = i;                   /* in case next edge is to same side */
01137     return TRUE;
01138 }
01139 
01140 /* poly_path:
01141  * Generate box path from port to border.
01142  * Store boxes in rv and number of boxes in kptr.
01143  * side gives preferred side of bounding box for last node.
01144  * Return actual side. Returning 0 indicates nothing done.
01145  */
01146 static int poly_path(node_t* n, port* p, int side, box rv[], int *kptr)
01147 {
01148     side = 0;
01149 
01150     if (ND_label(n)->html && ND_has_port(n)) {
01151         side = html_path(n, p, side, rv, kptr);
01152     }
01153     return side;
01154 }
01155 
01156 /* invflip_side:
01157  */
01158 static int invflip_side (int side, int rankdir)
01159 {
01160     switch (rankdir) {
01161     case RANKDIR_TB:
01162         break;
01163     case RANKDIR_BT:
01164         switch (side) {
01165         case TOP:
01166             side = BOTTOM;
01167             break;
01168         case BOTTOM:
01169             side = TOP;
01170             break;
01171         default:
01172             break;
01173         }
01174         break;
01175     case RANKDIR_LR:
01176         switch (side) {
01177         case TOP:
01178             side = RIGHT;
01179             break;
01180         case BOTTOM:
01181             side = LEFT;
01182             break;
01183         case LEFT:
01184             side = TOP;
01185             break;
01186         case RIGHT:
01187             side = BOTTOM;
01188             break;
01189         }
01190         break;
01191     case RANKDIR_RL:
01192         switch (side) {
01193         case TOP:
01194             side = RIGHT;
01195             break;
01196         case BOTTOM:
01197             side = LEFT;
01198             break;
01199         case LEFT:
01200             side = BOTTOM;
01201             break;
01202         case RIGHT:
01203             side = TOP;
01204             break;
01205         }
01206         break;
01207     }
01208     return side;
01209 }
01210 
01211 /* invflip_angle:
01212  */
01213 static double invflip_angle (double angle, int rankdir)
01214 {
01215     switch (rankdir) {
01216     case RANKDIR_TB:
01217         break;
01218     case RANKDIR_BT:
01219         angle *= -1; 
01220         break;
01221     case RANKDIR_LR:
01222         angle -= M_PI * 0.5;
01223         break;
01224     case RANKDIR_RL:
01225         if (angle == M_PI)
01226             angle = -0.5 * M_PI;
01227         else if (angle == M_PI * 0.75)
01228             angle = -0.25 * M_PI;
01229         else if (angle == M_PI * 0.5)
01230             angle = 0;
01231         else if (angle == M_PI * 0.25)
01232             angle = angle;
01233         else if (angle == 0)
01234             angle = M_PI * 0.5;
01235         else if (angle == M_PI * -0.25)
01236             angle = M_PI * 0.75;
01237         else if (angle == M_PI * -0.5)
01238             angle = M_PI;
01239         else if (angle == M_PI * -0.75)
01240             angle = angle;
01241         break;
01242     }
01243     return angle;
01244 }
01245 
01246 /* compassPoint:
01247  * Compute compass points for non-trivial shapes.
01248  * It finds where the ray ((0,0),(x,y)) hits the boundary and
01249  * return it.
01250  * Assumes ictxt and ictxt->n are non-NULL.
01251  */
01252 static point 
01253 compassPoint(inside_t* ictxt, double y, double x)
01254 {
01255     point  p;
01256     pointf curve[4];  /* bezier control points for a straight line */
01257     node_t* n = ictxt->s.n;
01258 
01259     curve[0].x = 0;
01260     curve[0].y = 0;
01261     curve[1].x = x/3;
01262     curve[1].y = y/3;
01263     curve[2].x = 2*x/3;
01264     curve[2].y = 2*y/3;
01265     curve[3].x = x;
01266     curve[3].y = y;
01267 
01268     bezier_clip(ictxt, ND_shape(n)->fns->insidefn, curve, 1);
01269 
01270     p.x = ROUND(curve[0].x);
01271     p.y = ROUND(curve[0].y);
01272 
01273     return p;
01274 }
01275 
01276 /* compassPort:
01277  * Attach a compass point to a port pp, and fill in remaining fields.
01278  * n is the corresponding node; bp is the bounding box of the port.
01279  * compass is the compass point
01280  * Return 1 if unrecognized compass point, in which case we
01281  * use the center.
01282  * This function also finishes initializing the port structure,
01283  * even if no compass point is involved.
01284  * The sides value gives the set of sides shared by the port. This
01285  * is used with a compass point to indicate if the port is exposed, to
01286  * set the port's side value.
01287  * 
01288  * FIX: For purposes, of rankdir=BT or RL, this assumes nodes are up-down
01289  * symmetric, left-right symmetric, and convex.
01290  */
01291 static int 
01292 compassPort(node_t* n, box* bp, port* pp, char* compass, int sides, inside_t* ictxt)
01293 {
01294     box b;
01295     point p, ctr;
01296     int rv = 0;
01297     double theta = 0.0;
01298     boolean constrain = FALSE;
01299     int side = 0;
01300     boolean clip = TRUE;
01301     boolean defined;
01302 
01303     if (bp) {
01304         b = *bp;
01305         p = pointof((b.LL.x + b.UR.x) / 2, (b.LL.y + b.UR.y) / 2);
01306         defined = TRUE;
01307     } else {
01308         p.x = p.y = 0;
01309         if (GD_flip(n->graph)) {
01310             b.UR.x = ND_ht_i(n) / 2;
01311             b.LL.x = -b.UR.x;
01312             b.UR.y = ND_lw_i(n);
01313             b.LL.y = -b.UR.y;
01314         } else {
01315             b.UR.y = ND_ht_i(n) / 2;
01316             b.LL.y = -b.UR.y;
01317             b.UR.x = ND_lw_i(n);
01318             b.LL.x = -b.UR.x;
01319         }
01320         defined = FALSE;
01321     }
01322     ctr = p;
01323     if (compass && *compass) {
01324         switch (*compass++) {
01325         case 'e':
01326             p.x = b.UR.x;
01327             theta = 0.0;
01328             constrain = TRUE;
01329             defined = TRUE;
01330             clip = FALSE;
01331             side = sides & RIGHT;
01332             break;
01333         case 's':
01334             p.y = b.LL.y;
01335             constrain = TRUE;
01336             clip = FALSE;
01337             switch (*compass) {
01338             case '\0':
01339                 theta = -M_PI * 0.5;
01340                 defined = TRUE;
01341                 side = sides & BOTTOM;
01342                 break;
01343             case 'e':
01344                 theta = -M_PI * 0.25;
01345                 defined = TRUE;
01346                 if (ictxt) p = compassPoint (ictxt, -INT_MAX, INT_MAX);
01347                 else p.x = b.UR.x;
01348                 side = sides & (BOTTOM | RIGHT);
01349                 break;
01350             case 'w':
01351                 theta = -M_PI * 0.75;
01352                 defined = TRUE;
01353                 if (ictxt) p = compassPoint (ictxt, -INT_MAX, -INT_MAX);
01354                 else p.x = b.LL.x;
01355                 side = sides & (BOTTOM | LEFT);
01356                 break;
01357             default:
01358                 p.y = ctr.y;
01359                 constrain = FALSE;
01360                 clip = TRUE;
01361                 rv = 1;
01362                 break;
01363             }
01364             break;
01365         case 'w':
01366             p.x = b.LL.x;
01367             theta = M_PI;
01368             constrain = TRUE;
01369             defined = TRUE;
01370             clip = FALSE;
01371             side = sides & LEFT;
01372             break;
01373         case 'n':
01374             p.y = b.UR.y;
01375             constrain = TRUE;
01376             clip = FALSE;
01377             switch (*compass) {
01378             case '\0':
01379                 defined = TRUE;
01380                 theta = M_PI * 0.5;
01381                 side = sides & TOP;
01382                 break;
01383             case 'e':
01384                 defined = TRUE;
01385                 theta = M_PI * 0.25;
01386                 if (ictxt) p = compassPoint (ictxt, INT_MAX, INT_MAX);
01387                 else p.x = b.UR.x;
01388                 side = sides & (TOP | RIGHT);
01389                 break;
01390             case 'w':
01391                 defined = TRUE;
01392                 theta = M_PI * 0.75;
01393                 if (ictxt) p = compassPoint (ictxt, INT_MAX, -INT_MAX);
01394                 else p.x = b.LL.x;
01395                 side = sides & (TOP | LEFT);
01396                 break;
01397             default:
01398                 p.y = ctr.y;
01399                 constrain = FALSE;
01400                 clip = TRUE;
01401                 rv = 1;
01402                 break;
01403             }
01404             break;
01405         default:
01406             rv = 1;
01407             break;
01408         }
01409     }
01410     p = cwrotatep(p, 90*GD_rankdir(n->graph));
01411     pp->side = invflip_side(side, GD_rankdir(n->graph));
01412     pp->bp = bp;
01413     pp->p = p;
01414     pp->theta = invflip_angle(theta, GD_rankdir(n->graph));
01415     if ((p.x == 0) && (p.y == 0))
01416         pp->order = MC_SCALE/2;
01417     else {
01418         /* compute angle with 0 at north pole, increasing CCW */
01419         double angle = atan2(p.y,p.x) + 1.5*M_PI;
01420         if (angle >= 2*M_PI) angle -= 2*M_PI;
01421         pp->order = (int)((MC_SCALE * angle) / (2*M_PI));
01422     }
01423     pp->constrained = constrain;
01424     pp->defined = defined;
01425     pp->clip = clip;
01426     return rv;
01427 }
01428 
01429 static port poly_port(node_t * n, char *portname, char *compass)
01430 {
01431     port rv;
01432     box *bp;
01433     int  sides;    /* bitmap of which sides the port lies along */
01434 
01435     if (portname[0] == '\0')
01436         return Center;
01437 
01438     sides = BOTTOM | RIGHT | TOP | LEFT; 
01439     if ((ND_label(n)->html) && (bp = html_port(n, portname, &sides))) {
01440         if (compassPort(n, bp, &rv, compass, sides, NULL)) {
01441             agerr(AGWARN,
01442                 "node %s, port %s, unrecognized compass point '%s' - ignored\n",
01443                       n->name, portname, compass);
01444         }
01445     } 
01446     else {
01447         inside_t* ictxtp;
01448         inside_t  ictxt;
01449 
01450         if (IS_BOX(n)) ictxtp = NULL;
01451         else {
01452             ictxt.s.n = n;
01453             ictxt.s.bp = NULL;
01454             ictxtp = &ictxt;
01455         }
01456         if (compassPort(n, NULL, &rv, portname, sides, ictxtp))
01457             unrecognized(n, portname);
01458     }
01459 
01460     return rv;
01461 }
01462 
01463 /* generic polygon gencode routine */
01464 static void poly_gencode(GVJ_t * job, node_t * n)
01465 {
01466     obj_state_t *obj = job->obj;
01467     polygon_t *poly;
01468     double xsize, ysize;
01469     int i, j, peripheries, sides, style;
01470     pointf P, *vertices;
01471     static pointf *AF;
01472     static int A_size;
01473     boolean filled;
01474     boolean usershape_p;
01475     boolean pfilled; /* true if fill not handled by user shape */
01476     char *color, *name;
01477     int doMap = (obj->url || obj->explicit_tooltip);
01478 
01479     if (doMap && !(job->flags & EMIT_CLUSTERS_LAST))
01480         gvrender_begin_anchor(job, obj->url, obj->tooltip, obj->target);
01481 
01482     poly = (polygon_t *) ND_shape_info(n);
01483     vertices = poly->vertices;
01484     sides = poly->sides;
01485     peripheries = poly->peripheries;
01486     if (A_size < sides) {
01487         A_size = sides + 5;
01488         AF = ALLOC(A_size, AF, pointf);
01489     }
01490 
01491     ND_label(n)->p = ND_coord_i(n);
01492     xsize = (double)(ND_lw_i(n) + ND_rw_i(n)) / POINTS(ND_width(n));
01493     ysize = (double)ND_ht_i(n) / POINTS(ND_height(n));
01494 
01495     style = stylenode(job, n);
01496 
01497     if (ND_gui_state(n) & GUI_STATE_ACTIVE) {
01498         color = late_nnstring(n, N_activepencolor, DEFAULT_ACTIVEPENCOLOR);
01499         gvrender_set_pencolor(job, color);
01500         color = late_nnstring(n, N_activefillcolor, DEFAULT_ACTIVEFILLCOLOR);
01501         gvrender_set_fillcolor(job, color);
01502         filled = TRUE;
01503     }
01504     else if (ND_gui_state(n) & GUI_STATE_SELECTED) {
01505         color = late_nnstring(n, N_selectedpencolor, DEFAULT_SELECTEDPENCOLOR);
01506         gvrender_set_pencolor(job, color);
01507         color = late_nnstring(n, N_selectedfillcolor, DEFAULT_SELECTEDFILLCOLOR);
01508         gvrender_set_fillcolor(job, color);
01509         filled = TRUE;
01510     }
01511     else if (ND_gui_state(n) & GUI_STATE_DELETED) {
01512         color = late_nnstring(n, N_deletedpencolor, DEFAULT_DELETEDPENCOLOR);
01513         gvrender_set_pencolor(job, color);
01514         color = late_nnstring(n, N_deletedfillcolor, DEFAULT_DELETEDFILLCOLOR);
01515         gvrender_set_fillcolor(job, color);
01516         filled = TRUE;
01517     }
01518     else if (ND_gui_state(n) & GUI_STATE_VISITED) {
01519         color = late_nnstring(n, N_visitedpencolor, DEFAULT_VISITEDPENCOLOR);
01520         gvrender_set_pencolor(job, color);
01521         color = late_nnstring(n, N_visitedfillcolor, DEFAULT_VISITEDFILLCOLOR);
01522         gvrender_set_fillcolor(job, color);
01523         filled = TRUE;
01524     }
01525     else {
01526         if (style & FILLED) {
01527             gvrender_set_fillcolor(job, findFill(n)); /* emit fill color */
01528             filled = TRUE;
01529         } else {
01530             filled = FALSE;
01531         }
01532         pencolor(job, n);       /* emit pen color */
01533     }
01534 
01535     pfilled = !ND_shape(n)->usershape || streq(ND_shape(n)->name, "custom");
01536     /* if no boundary but filled, set boundary color to fill color */
01537     if ((peripheries == 0) && filled && pfilled) {
01538         char *color;
01539         peripheries = 1;
01540         color = findFill(n);
01541         if (color[0])
01542             gvrender_set_pencolor(job, color);
01543     }
01544     usershape_p = FALSE;
01545     if (ND_shape(n)->usershape) {
01546         name = ND_shape(n)->name;
01547         if (streq(name, "custom"))
01548             name = agget(n, "shapefile");
01549         usershape_p = TRUE;
01550     }
01551     else if ((name = agget(n, "image"))) {
01552         usershape_p = TRUE;
01553     }
01554     if (usershape_p) {  
01555         /* get coords of innermost periphery */
01556         for (i = 0; i < sides; i++) {
01557             P = vertices[i];
01558             AF[i].x = P.x * xsize + (double)ND_coord_i(n).x;
01559             AF[i].y = P.y * ysize + (double)ND_coord_i(n).y;
01560         }
01561         /* lay down fill first */
01562         if (filled && pfilled) {
01563             if (sides <= 2) {
01564                 gvrender_ellipse(job, AF, sides, filled);
01565                 if (style & DIAGONALS) {
01566                     Mcircle_hack(job, n);
01567                 }
01568             } else if (style & (ROUNDED | DIAGONALS)) {
01569                 node_round_corners(job, n, AF, sides, style);
01570             } else {
01571                 gvrender_polygon(job, AF, sides, filled);
01572             }
01573         }
01574         gvrender_usershape(job, name, AF, sides, filled, late_string(n, N_imagescale, "false"));
01575         filled = FALSE;  /* with user shapes, we have done the fill if needed */
01576     }
01577 
01578     for (j = 0; j < peripheries; j++) {
01579         for (i = 0; i < sides; i++) {
01580             P = vertices[i + j * sides];
01581             AF[i].x = P.x * xsize + (double)ND_coord_i(n).x;
01582             AF[i].y = P.y * ysize + (double)ND_coord_i(n).y;
01583         }
01584         if (sides <= 2) {
01585             gvrender_ellipse(job, AF, sides, filled);
01586             if (style & DIAGONALS) {
01587                 Mcircle_hack(job, n);
01588             }
01589         } else if (SPECIAL_CORNERS(style)) {
01590             node_round_corners(job, n, AF, sides, style);
01591         } else {
01592             gvrender_polygon(job, AF, sides, filled);
01593         }
01594         /* fill innermost periphery only */
01595         filled = FALSE;
01596     }
01597 
01598     emit_label(job, EMIT_NLABEL, ND_label(n));
01599     if (doMap) {
01600         if (job->flags & EMIT_CLUSTERS_LAST)
01601             gvrender_begin_anchor(job, obj->url, obj->tooltip, obj->target);
01602         gvrender_end_anchor(job);
01603     }
01604 }
01605 
01606 /*=======================end poly======================================*/
01607 
01608 /*===============================point start========================*/
01609 
01610 /* point_init:
01611  * shorthand for shape=circle, style=filled, width=0.05, label=""
01612  */
01613 static void point_init(node_t * n)
01614 {
01615     polygon_t *poly = NEW(polygon_t);
01616     int sides, outp, peripheries = ND_shape(n)->polygon->peripheries;
01617     double sz;
01618     pointf P, *vertices;
01619     int i, j;
01620     double w, h;
01621 
01622     /* set width and height, and make them equal
01623      * if user has set weight or height, use it.
01624      * if both are set, use smallest.
01625      * if neither, use default
01626      */
01627     w = late_double(n, N_width, MAXDOUBLE, MIN_POINT);
01628     h = late_double(n, N_height, MAXDOUBLE, MIN_POINT);
01629     w = MIN(w,h);
01630     if ((w == MAXDOUBLE) && (h == MAXDOUBLE)) /* neither defined */
01631         ND_width(n) = ND_height(n) = DEF_POINT;
01632     else
01633         ND_width(n) = ND_height(n) = w;
01634 
01635     sz = ND_width(n)*POINTS_PER_INCH; 
01636     peripheries = late_int(n, N_peripheries, peripheries, 0);
01637     if (peripheries < 1) outp = 1;
01638     else outp = peripheries;
01639     sides = 2;
01640     vertices = N_NEW(outp * sides, pointf);
01641     P.y = P.x = sz / 2.;
01642     vertices[0].x = -P.x;
01643     vertices[0].y = -P.y;
01644     vertices[1] = P;
01645     if (peripheries > 1) {
01646         for (j = 1, i = 2; j < peripheries; j++) {
01647             P.x += GAP;
01648             P.y += GAP;
01649             vertices[i].x = -P.x;
01650             vertices[i].y = -P.y;
01651             i++;
01652             vertices[i].x = P.x;
01653             vertices[i].y = P.y;
01654             i++;
01655         }
01656         sz = 2. * P.x;
01657     }
01658     poly->regular = 1;
01659     poly->peripheries = peripheries;
01660     poly->sides = 2;
01661     poly->orientation = 0;
01662     poly->skew = 0;
01663     poly->distortion = 0;
01664     poly->vertices = vertices;
01665 
01666     ND_height(n) = ND_width(n) = PS2INCH(sz);
01667     ND_shape_info(n) = (void *) poly;
01668 }
01669 
01670 static boolean 
01671 point_inside(inside_t* inside_context, pointf p)
01672 {
01673     static node_t *lastn;       /* last node argument */
01674     static double radius;
01675     pointf P;
01676     node_t *n = inside_context->s.n;
01677 
01678     P = ccwrotatepf(p, 90*GD_rankdir(n->graph));
01679 
01680     if (n != lastn) {
01681         int outp;
01682         polygon_t *poly = (polygon_t *) ND_shape_info(n);
01683 
01684         /* index to outer-periphery */
01685         outp = 2*(poly->peripheries - 1);
01686         if (outp < 0) outp = 0;
01687 
01688         radius = poly->vertices[outp+1].x;
01689         lastn = n;
01690     }
01691 
01692     /* inside bounding box? */
01693     if ((fabs(P.x) > radius) || (fabs(P.y) > radius))
01694         return FALSE;
01695 
01696     return (hypot(P.x, P.y) <= radius);
01697 }
01698 
01699 static void point_gencode(GVJ_t * job, node_t * n)
01700 {
01701     obj_state_t *obj = job->obj;
01702     polygon_t *poly;
01703     int i, j, sides, peripheries, style;
01704     pointf P, *vertices;
01705     static pointf *AF;
01706     static int A_size;
01707     boolean filled;
01708     char *color;
01709     int doMap = (obj->url || obj->explicit_tooltip);
01710 
01711     if (doMap && !(job->flags & EMIT_CLUSTERS_LAST))
01712         gvrender_begin_anchor(job, obj->url, obj->tooltip, obj->target);
01713 
01714     poly = (polygon_t *) ND_shape_info(n);
01715     vertices = poly->vertices;
01716     sides = poly->sides;
01717     peripheries = poly->peripheries;
01718     if (A_size < sides) {
01719         A_size = sides + 2;
01720         AF = ALLOC(A_size, AF, pointf);
01721     }
01722 
01723     checkStyle(n, &style);
01724     if (style & INVISIBLE)
01725         gvrender_set_style(job, point_style);
01726     else
01727         gvrender_set_style(job, &point_style[1]);
01728 
01729     if (ND_gui_state(n) & GUI_STATE_ACTIVE) {
01730         color = late_nnstring(n, N_activepencolor, DEFAULT_ACTIVEPENCOLOR);
01731         gvrender_set_pencolor(job, color);
01732         color = late_nnstring(n, N_activefillcolor, DEFAULT_ACTIVEFILLCOLOR);
01733         gvrender_set_fillcolor(job, color);
01734     }
01735     else if (ND_gui_state(n) & GUI_STATE_SELECTED) {
01736         color = late_nnstring(n, N_selectedpencolor, DEFAULT_SELECTEDPENCOLOR);
01737         gvrender_set_pencolor(job, color);
01738         color = late_nnstring(n, N_selectedfillcolor, DEFAULT_SELECTEDFILLCOLOR);
01739         gvrender_set_fillcolor(job, color);
01740     }
01741     else if (ND_gui_state(n) & GUI_STATE_DELETED) {
01742         color = late_nnstring(n, N_deletedpencolor, DEFAULT_DELETEDPENCOLOR);
01743         gvrender_set_pencolor(job, color);
01744         color = late_nnstring(n, N_deletedfillcolor, DEFAULT_DELETEDFILLCOLOR);
01745         gvrender_set_fillcolor(job, color);
01746     }
01747     else if (ND_gui_state(n) & GUI_STATE_VISITED) {
01748         color = late_nnstring(n, N_visitedpencolor, DEFAULT_VISITEDPENCOLOR);
01749         gvrender_set_pencolor(job, color);
01750         color = late_nnstring(n, N_visitedfillcolor, DEFAULT_VISITEDFILLCOLOR);
01751         gvrender_set_fillcolor(job, color);
01752     }
01753     else {
01754         color = findFillDflt (n, "black");
01755         gvrender_set_fillcolor(job, color); /* emit fill color */
01756         pencolor(job, n);       /* emit pen color */
01757     }
01758     filled = TRUE;
01759 
01760     /* if no boundary but filled, set boundary color to fill color */
01761     if (peripheries == 0) {
01762         peripheries = 1;
01763         if (color[0])
01764             gvrender_set_pencolor(job, color);
01765     }
01766 
01767     for (j = 0; j < peripheries; j++) {
01768         for (i = 0; i < sides; i++) {
01769             P = vertices[i + j * sides];
01770             AF[i].x = P.x + (double)ND_coord_i(n).x;
01771             AF[i].y = P.y + (double)ND_coord_i(n).y;
01772         }
01773         gvrender_ellipse(job, AF, sides, filled);
01774         /* fill innermost periphery only */
01775         filled = FALSE;
01776     }
01777 
01778     if (doMap) {
01779         if (job->flags & EMIT_CLUSTERS_LAST)
01780             gvrender_begin_anchor(job, obj->url, obj->tooltip, obj->target);
01781         gvrender_end_anchor(job);
01782     }
01783 }
01784 
01785 /* the "record" shape is a rudimentary table formatter */
01786 
01787 #define HASTEXT 1
01788 #define HASPORT 2
01789 #define HASTABLE 4
01790 #define INTEXT 8
01791 #define INPORT 16
01792 
01793 #define ISCTRL(c) ((c) == '{' || (c) == '}' || (c) == '|' || (c) == '<' || (c) == '>')
01794 
01795 static char *reclblp;
01796 
01797 static field_t*
01798 parse_reclbl(node_t * n, int LR, int flag, char *text)
01799 {
01800     field_t *fp, *rv = NEW(field_t);
01801     char *tsp, *psp, *hstsp, *hspsp, *sp;
01802     char port[SMALLBUF];
01803     int maxf, cnt, mode, wflag, ishardspace, fi;
01804     graph_t *sg = n->graph;
01805     textlabel_t *lbl = ND_label(n);
01806 
01807     fp = NULL;
01808     for (maxf = 1, cnt = 0, sp = reclblp; *sp; sp++) {
01809         if (*sp == '\\') {
01810             sp++;
01811             if (*sp && (*sp == '{' || *sp == '}' || *sp == '|' || *sp == '\\'))
01812                 continue;
01813         }
01814         if (*sp == '{')
01815             cnt++;
01816         else if (*sp == '}')
01817             cnt--;
01818         else if (*sp == '|' && cnt == 0)
01819             maxf++;
01820         if (cnt < 0)
01821             break;
01822     }
01823     rv->fld = N_NEW(maxf, field_t *);
01824     rv->LR = LR;
01825     mode = 0;
01826     fi = 0;
01827     hstsp = tsp = text, hspsp = psp = &port[0];
01828     wflag = TRUE;
01829     ishardspace = FALSE;
01830     while (wflag) {
01831         switch (*reclblp) {
01832         case '<':
01833             if (mode & (HASTABLE | HASPORT))
01834                 return NULL;
01835             if (lbl->html) goto dotext;
01836             mode |= (HASPORT | INPORT);
01837             reclblp++;
01838             break;
01839         case '>':
01840             if (lbl->html) goto dotext;
01841             if (!(mode & INPORT))
01842                 return NULL;
01843             mode &= ~INPORT;
01844             reclblp++;
01845             break;
01846         case '{':
01847             reclblp++;
01848             if (mode != 0 || !*reclblp)
01849                 return NULL;
01850             mode = HASTABLE;
01851             if (!(rv->fld[fi++] = parse_reclbl(n, NOT(LR), FALSE, text)))
01852                 return NULL;
01853             break;
01854         case '}':
01855         case '|':
01856         case '\000':
01857             if ((!*reclblp && !flag) || (mode & INPORT))
01858                 return NULL;
01859             if (!(mode & HASTABLE))
01860                 fp = rv->fld[fi++] = NEW(field_t);
01861             if (mode & HASPORT) {
01862                 if (psp > &port[0] + 1 &&
01863                     psp - 1 != hspsp && *(psp - 1) == ' ')
01864                     psp--;
01865                 *psp = '\000';
01866                 fp->id = strdup(&port[0]);
01867                 hspsp = psp = &port[0];
01868             }
01869             if (!(mode & (HASTEXT | HASTABLE)))
01870                 mode |= HASTEXT, *tsp++ = ' ';
01871             if (mode & HASTEXT) {
01872                 if (tsp > text + 1 &&
01873                     tsp - 1 != hstsp && *(tsp - 1) == ' ')
01874                     tsp--;
01875                 *tsp = '\000';
01876                 fp->lp =
01877                     make_label(sg->root, (lbl->html ? LT_HTML : LT_NONE), 
01878                         strdup(text),
01879                         lbl->fontsize,
01880                         lbl->fontname,
01881                         lbl->fontcolor);
01882                 if (lbl->html) {
01883                     if (make_html_label(sg->root, fp->lp, n))
01884                         agerr(AGPREV, "in label of node %s\n", n->name);
01885                 }
01886                 fp->LR = TRUE;
01887                 hstsp = tsp = text;
01888             }
01889             if (*reclblp) {
01890                 if (*reclblp == '}') {
01891                     reclblp++;
01892                     rv->n_flds = fi;
01893                     return rv;
01894                 }
01895                 mode = 0;
01896                 reclblp++;
01897             } else
01898                 wflag = FALSE;
01899             break;
01900         case '\\':
01901             if (*(reclblp + 1)) {
01902                 if (ISCTRL(*(reclblp + 1)))
01903                     reclblp++;
01904                 else if ((*(reclblp + 1) == ' ') && !lbl->html)
01905                     ishardspace = TRUE, reclblp++;
01906                 else {
01907                     *tsp++ = '\\';
01908                     mode |= (INTEXT | HASTEXT);
01909                     reclblp++;
01910                 }
01911             }
01912             /* falling through ... */
01913         default:
01914 dotext :
01915             if ((mode & HASTABLE) && *reclblp != ' ')
01916                 return NULL;
01917             if (!(mode & (INTEXT | INPORT)) && *reclblp != ' ')
01918                 mode |= (INTEXT | HASTEXT);
01919             if (mode & INTEXT) {
01920                 if (!(*reclblp == ' ' && !ishardspace &&
01921                       *(tsp - 1) == ' ' && !lbl->html))
01922                     *tsp++ = *reclblp;
01923                 if (ishardspace)
01924                     hstsp = tsp - 1;
01925             } else if (mode & INPORT) {
01926                 if (!(*reclblp == ' ' && !ishardspace &&
01927                       (psp == &port[0] || *(psp - 1) == ' ')))
01928                     *psp++ = *reclblp;
01929                 if (ishardspace)
01930                     hspsp = psp - 1;
01931             }
01932             reclblp++;
01933             while (*reclblp & 128)
01934                 *tsp++ = *reclblp++;
01935             break;
01936         }
01937     }
01938     rv->n_flds = fi;
01939     return rv;
01940 }
01941 
01942 static point size_reclbl(node_t * n, field_t * f)
01943 {
01944     int i;
01945     char *p;
01946     double marginx, marginy;
01947     point d, d0;
01948     pointf dimen;
01949 
01950     if (f->lp) {
01951         dimen = f->lp->dimen;
01952 
01953         /* minimal whitespace around label */
01954         if ((dimen.x > 0.0) || (dimen.y > 0.0)) {
01955             /* padding */
01956             if ((p = agget(n, "margin"))) {
01957                 i = sscanf(p, "%lf,%lf", &marginx, &marginy);
01958                 if (i > 0) {
01959                     dimen.x += 2 * POINTS(marginx);
01960                     if (i > 1)
01961                         dimen.y += 2 * POINTS(marginy);
01962                     else
01963                         dimen.y += 2 * POINTS(marginy);
01964                 } else
01965                     PAD(dimen);
01966             } else
01967                 PAD(dimen);
01968         }
01969         PF2P(dimen, d);
01970     } else {
01971         d.x = d.y = 0;
01972         for (i = 0; i < f->n_flds; i++) {
01973             d0 = size_reclbl(n, f->fld[i]);
01974             if (f->LR) {
01975                 d.x += d0.x;
01976                 d.y = MAX(d.y, d0.y);
01977             } else {
01978                 d.y += d0.y;
01979                 d.x = MAX(d.x, d0.x);
01980             }
01981         }
01982     }
01983     f->size = d;
01984     return d;
01985 }
01986 
01987 static void resize_reclbl(field_t * f, point sz, int nojustify_p)
01988 {
01989     int i, amt;
01990     double inc;
01991     point d, newsz;
01992     field_t *sf;
01993 
01994     /* adjust field */
01995     d.x = sz.x - f->size.x;
01996     d.y = sz.y - f->size.y;
01997     f->size = sz;
01998 
01999     /* adjust text */
02000     if (f->lp && !nojustify_p) {
02001         P2PF(d, f->lp->d);
02002     }
02003 
02004     /* adjust children */
02005     if (f->n_flds) {
02006 
02007         if (f->LR)
02008             inc = (double) d.x / f->n_flds;
02009         else
02010             inc = (double) d.y / f->n_flds;
02011         for (i = 0; i < f->n_flds; i++) {
02012             sf = f->fld[i];
02013             amt = ((int) ((i + 1) * inc)) - ((int) (i * inc));
02014             if (f->LR)
02015                 newsz = pointof(sf->size.x + amt, sz.y);
02016             else
02017                 newsz = pointof(sz.x, sf->size.y + amt);
02018             resize_reclbl(sf, newsz, nojustify_p);
02019         }
02020     }
02021 }
02022 
02023 /* pos_reclbl:
02024  * Assign position info for each field. Also, set
02025  * the sides attribute, which indicates which sides of the
02026  * record are accessible to the field.
02027  */
02028 static void pos_reclbl(field_t * f, point ul, int sides)
02029 {
02030     int i, last, mask;
02031 
02032     f->sides = sides;
02033     f->b.LL = pointof(ul.x, ul.y - f->size.y);
02034     f->b.UR = pointof(ul.x + f->size.x, ul.y);
02035     last = f->n_flds - 1;
02036     for (i = 0; i <= last; i++) {
02037         if (sides) {
02038             if (f->LR) {
02039                 if (i == 0) {
02040                     if (i == last) mask = TOP | BOTTOM | RIGHT | LEFT;
02041                     else mask = TOP | BOTTOM | LEFT;
02042                 }
02043                 else if (i == last) mask = TOP | BOTTOM | RIGHT;
02044                 else mask = TOP | BOTTOM;
02045             }
02046             else {
02047                 if (i == 0) {
02048                     if (i == last) mask = TOP | BOTTOM | RIGHT | LEFT;
02049                     else mask = TOP | RIGHT | LEFT;
02050                 }
02051                 else if (i == last) mask = LEFT | BOTTOM | RIGHT;
02052                 else mask = LEFT | RIGHT;
02053             }
02054         }
02055         else mask = 0;
02056         pos_reclbl(f->fld[i], ul, sides & mask);
02057         if (f->LR)
02058             ul.x = ul.x + f->fld[i]->size.x;
02059         else
02060             ul.y = ul.y - f->fld[i]->size.y;
02061     }
02062 }
02063 
02064 #ifdef DEBUG
02065 static void indent(int l)
02066 {
02067     int i;
02068     for (i = 0; i < l; i++)
02069         fputs("  ", stderr);
02070 }
02071 
02072 static void prbox(box b)
02073 {
02074     fprintf(stderr, "((%d,%d),(%d,%d))\n", b.LL.x, b.LL.y, b.UR.x, b.UR.y);
02075 }
02076 
02077 static void dumpL(field_t * info, int level)
02078 {
02079     int i;
02080 
02081     indent(level);
02082     if (info->n_flds == 0) {
02083         fprintf(stderr, "Label \"%s\" ", info->lp->text);
02084         prbox(info->b);
02085     } else {
02086         fprintf(stderr, "Tbl ");
02087         prbox(info->b);
02088         for (i = 0; i < info->n_flds; i++) {
02089             dumpL(info->fld[i], level + 1);
02090         }
02091     }
02092 }
02093 #endif
02094 
02095 /* syntax of labels: foo|bar|baz or foo|(recursive|label)|baz */
02096 static void record_init(node_t * n)
02097 {
02098     field_t *info;
02099     point ul, sz;
02100     int flip, len;
02101     char *textbuf;              /* temp buffer for storing labels */
02102     int sides = BOTTOM | RIGHT | TOP | LEFT; 
02103 
02104     /* Always use rankdir to determine how records are laid out */
02105     flip = NOT(GD_realflip(n->graph));
02106     reclblp = ND_label(n)->text;
02107     len = strlen(reclblp);
02108     textbuf = N_NEW(len + 1, char);
02109     if (!(info = parse_reclbl(n, flip, TRUE, textbuf))) {
02110         agerr(AGERR, "bad label format %s\n", ND_label(n)->text);
02111         reclblp = "\\N";
02112         info = parse_reclbl(n, flip, TRUE, textbuf);
02113     }
02114     free(textbuf);
02115 
02116     size_reclbl(n, info);
02117     sz.x = POINTS(ND_width(n));
02118     sz.y = POINTS(ND_height(n));
02119     if (mapbool(late_string(n, N_fixed, "false"))) {
02120         if ((sz.x < info->size.x) || (sz.y < info->size.y)) {
02121 /* should check that the record really won't fit, e.g., there may be no text.
02122                         agerr(AGWARN, "node '%s' size may be too small\n",
02123                                 n->name);
02124  */
02125         }
02126     } else {
02127         sz.x = MAX(info->size.x, sz.x);
02128         sz.y = MAX(info->size.y, sz.y);
02129     }
02130     resize_reclbl(info, sz, mapbool(late_string(n, N_nojustify, "false")));
02131     ul = pointof(-sz.x / 2, sz.y / 2);
02132     pos_reclbl(info, ul, sides);
02133     ND_width(n) = PS2INCH(info->size.x);
02134     ND_height(n) = PS2INCH(info->size.y);
02135     ND_shape_info(n) = (void *) info;
02136 }
02137 
02138 static void free_field (field_t* f)
02139 {
02140     int i;
02141 
02142     for (i=0; i<f->n_flds; i++ ) {
02143         free_field(f->fld[i]);
02144     }
02145 
02146     free(f->id);
02147     free_label(f->lp);
02148     free(f->fld);
02149     free(f);
02150 }
02151 
02152 static void record_free(node_t * n)
02153 {
02154     field_t *p = ND_shape_info(n);
02155 
02156     free_field (p);
02157 }
02158 
02159 static field_t *map_rec_port(field_t * f, char *str)
02160 {
02161     field_t *rv;
02162     int sub;
02163 
02164     if (f->id && (streq(f->id, str)))
02165         rv = f;
02166     else {
02167         rv = NULL;
02168         for (sub = 0; sub < f->n_flds; sub++)
02169             if ((rv = map_rec_port(f->fld[sub], str)))
02170                 break;
02171     }
02172     return rv;
02173 }
02174 
02175 static port record_port(node_t * n, char *portname, char *compass)
02176 {
02177     field_t *f;
02178     field_t *subf;
02179     port rv;
02180     int  sides;    /* bitmap of which sides the port lies along */
02181 
02182     if (portname[0] == '\0')
02183         return Center;
02184     sides = BOTTOM | RIGHT | TOP | LEFT; 
02185     f = (field_t *) ND_shape_info(n);
02186     if ((subf = map_rec_port(f, portname))) {
02187         if (compassPort(n, &subf->b, &rv, compass, subf->sides, NULL)) {
02188             agerr(AGWARN,
02189               "node %s, port %s, unrecognized compass point '%s' - ignored\n",
02190               n->name, portname, compass);
02191         }
02192     } else if (compassPort(n, &f->b, &rv, portname, sides, NULL)) {
02193         unrecognized(n, portname);
02194     }
02195 
02196     return rv;
02197 }
02198 
02199 /* record_inside:
02200  * Note that this does not handle Mrecords correctly. It assumes 
02201  * everything is a rectangle.
02202  */
02203 static boolean
02204 record_inside(inside_t * inside_context, pointf p)
02205 {
02206 
02207     field_t *fld0;
02208     box *bp = inside_context->s.bp;
02209     node_t *n = inside_context->s.n;
02210     box bbox;
02211 
02212     /* convert point to node coordinate system */
02213     p = ccwrotatepf(p, 90*GD_rankdir(n->graph));
02214 
02215     if (bp == NULL) {
02216         fld0 = (field_t *) ND_shape_info(n);
02217         bbox = fld0->b;
02218     }
02219     else bbox = *bp;
02220 
02221     return INSIDE(p, bbox);
02222 }
02223 
02224 /* record_path:
02225  * Generate box path from port to border.
02226  * See poly_path for constraints.
02227  */
02228 static int record_path(node_t* n, port* prt, int side, box rv[], int *kptr)
02229 {
02230     int i, ls, rs;
02231     point p;
02232     field_t *info;
02233 
02234     if (!prt->defined) return 0;
02235     p = prt->p;
02236     info = (field_t *) ND_shape_info(n);
02237 
02238     for (i = 0; i < info->n_flds; i++) {
02239         if (!GD_flip(n->graph)) {
02240             ls = info->fld[i]->b.LL.x;
02241             rs = info->fld[i]->b.UR.x;
02242         } else {
02243             ls = info->fld[i]->b.LL.y;
02244             rs = info->fld[i]->b.UR.y;
02245         }
02246         if (BETWEEN(ls, p.x, rs)) {
02247             /* FIXME: I don't understand this code */
02248             if (GD_flip(n->graph)) {
02249                 rv[0] = flip_rec_box(info->fld[i]->b, ND_coord_i(n));
02250             } else {
02251                 rv[0].LL.x = ND_coord_i(n).x + ls;
02252                 rv[0].LL.y = ND_coord_i(n).y - ND_ht_i(n) / 2;
02253                 rv[0].UR.x = ND_coord_i(n).x + rs;
02254             }
02255             rv[0].UR.y = ND_coord_i(n).y + ND_ht_i(n) / 2;
02256             *kptr = 1;
02257             break;
02258         }
02259     }
02260     return side;
02261 }
02262 
02263 static void gen_fields(GVJ_t * job, node_t * n, field_t * f)
02264 {
02265     int i;
02266     double cx, cy;
02267     pointf AF[2], coord;
02268 
02269     if (f->lp) {
02270         cx = (f->b.LL.x + f->b.UR.x) / 2.0 + ND_coord_i(n).x;
02271         cy = (f->b.LL.y + f->b.UR.y) / 2.0 + ND_coord_i(n).y;
02272         f->lp->p = pointof((int) cx, (int) cy);
02273         emit_label(job, EMIT_NLABEL, f->lp);
02274         pencolor(job, n);
02275     }
02276 
02277     for (i = 0; i < f->n_flds; i++) {
02278         if (i > 0) {
02279             if (f->LR) {
02280                 P2PF(f->fld[i]->b.LL, AF[0]);
02281                 AF[1].x = AF[0].x;
02282                 AF[1].y = (double)(f->fld[i]->b.UR.y);
02283             } else {
02284                 P2PF(f->fld[i]->b.UR, AF[1]);
02285                 AF[0].x = (double)(f->fld[i]->b.LL.x);
02286                 AF[0].y = AF[1].y;
02287             }
02288             P2PF(ND_coord_i(n), coord);
02289             AF[0] = add_pointfs(AF[0], coord);
02290             AF[1] = add_pointfs(AF[1], coord);
02291             gvrender_polyline(job, AF, 2);
02292         }
02293         gen_fields(job, n, f->fld[i]);
02294     }
02295 }
02296 
02297 static void record_gencode(GVJ_t * job, node_t * n)
02298 {
02299     obj_state_t *obj = job->obj;
02300     boxf BF;
02301     pointf AF[4];
02302     int style;
02303     field_t *f;
02304     int doMap = (obj->url || obj->explicit_tooltip);
02305 
02306     f = (field_t *) ND_shape_info(n);
02307     B2BF(f->b, BF);
02308     BF.LL.x += (double)(ND_coord_i(n).x);
02309     BF.LL.y += (double)(ND_coord_i(n).y);
02310     BF.UR.x += (double)(ND_coord_i(n).x);
02311     BF.UR.y += (double)(ND_coord_i(n).y);
02312     
02313     if (doMap && !(job->flags & EMIT_CLUSTERS_LAST))
02314         gvrender_begin_anchor(job, obj->url, obj->tooltip, obj->target);
02315     style = stylenode(job, n);
02316     pencolor(job, n);
02317     if (style & FILLED)
02318         gvrender_set_fillcolor(job, findFill(n)); /* emit fill color */
02319     if (streq(ND_shape(n)->name, "Mrecord"))
02320         style |= ROUNDED;
02321     if (SPECIAL_CORNERS(style)) {
02322         AF[0] = BF.LL;
02323         AF[2] = BF.UR;
02324         AF[1].x = AF[2].x;
02325         AF[1].y = AF[0].y;
02326         AF[3].x = AF[0].x;
02327         AF[3].y = AF[2].y;
02328         node_round_corners(job, n, AF, 4, style);
02329     }
02330     else
02331         gvrender_box(job, BF, style & FILLED);
02332 
02333     gen_fields(job, n, f);
02334 
02335     if (doMap) {
02336         if (job->flags & EMIT_CLUSTERS_LAST)
02337             gvrender_begin_anchor(job, obj->url, obj->tooltip, obj->target);
02338         gvrender_end_anchor(job);
02339     }
02340 }
02341 
02342 static shape_desc **UserShape;
02343 static int N_UserShape;
02344 
02345 shape_desc *find_user_shape(char *name)
02346 {
02347     int i;
02348     if (UserShape) {
02349         for (i = 0; i < N_UserShape; i++) {
02350             if (streq(UserShape[i]->name, name))
02351                 return UserShape[i];
02352         }
02353     }
02354     return NULL;
02355 }
02356 
02357 static shape_desc *user_shape(char *name)
02358 {
02359     int i;
02360     shape_desc *p;
02361 
02362     if ((p = find_user_shape(name)))
02363         return p;
02364     i = N_UserShape++;
02365     UserShape = ALLOC(N_UserShape, UserShape, shape_desc *);
02366     p = UserShape[i] = NEW(shape_desc);
02367     *p = Shapes[0];
02368     p->name = strdup(name);
02369     p->usershape = TRUE;
02370     if (Lib == NULL && ! streq(name, "custom"))
02371         agerr(AGWARN, "using %s for unknown shape %s\n", Shapes[0].name,
02372               p->name);
02373     return p;
02374 }
02375 
02376 shape_desc *bind_shape(char *name, node_t * np)
02377 {
02378     shape_desc *ptr, *rv = NULL;
02379     char *str;
02380 
02381     str = safefile(agget(np, "shapefile"));
02382     /* If shapefile is defined and not epsf, set shape = custom */
02383     if (str && ! streq(name, "epsf"))
02384         name = "custom";
02385     if (! streq(name, "custom")) {
02386         for (ptr = Shapes; ptr->name; ptr++) {
02387             if (streq(ptr->name, name)) {
02388                 rv = ptr;
02389                 break;
02390             }
02391         }
02392     }
02393     if (rv == NULL)
02394         rv = user_shape(name);
02395     return rv;
02396 }
02397 
02398 static boolean epsf_inside(inside_t * inside_context, pointf p)
02399 {
02400     pointf P;
02401     double x2;
02402     node_t *n = inside_context->s.n;
02403 
02404     P = ccwrotatepf(p, 90*GD_rankdir(n->graph));
02405     x2 = ND_ht_i(n) / 2;
02406     return ((P.y >= -x2) && (P.y <= x2) && (P.x >= -ND_lw_i(n))
02407             && (P.x <= ND_rw_i(n)));
02408 }
02409 
02410 static void epsf_gencode(GVJ_t * job, node_t * n)
02411 {
02412     obj_state_t *obj = job->obj;
02413     epsf_t *desc;
02414     int doMap = (obj->url || obj->explicit_tooltip);
02415 
02416     desc = (epsf_t *) (ND_shape_info(n));
02417     if (!desc)
02418         return;
02419 
02420     if (doMap && !(job->flags & EMIT_CLUSTERS_LAST))
02421         gvrender_begin_anchor(job, obj->url, obj->tooltip, obj->target);
02422     gvrender_begin_context(job);
02423     if (desc)
02424         fprintf(job->output_file,
02425                 "%d %d translate newpath user_shape_%d\n",
02426                 ND_coord_i(n).x + desc->offset.x,
02427                 ND_coord_i(n).y + desc->offset.y, desc->macro_id);
02428     ND_label(n)->p = ND_coord_i(n);
02429     gvrender_end_context(job);
02430 
02431     emit_label(job, EMIT_NLABEL, ND_label(n));
02432     if (doMap) {
02433         if (job->flags & EMIT_CLUSTERS_LAST)
02434             gvrender_begin_anchor(job, obj->url, obj->tooltip, obj->target);
02435         gvrender_end_anchor(job);
02436     }
02437 }

Generated on Mon Mar 31 19:03:25 2008 for Graphviz by  doxygen 1.5.1