00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
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
00028 #define DEF_POINT 0.05
00029
00030
00031
00032
00033 #define MIN_POINT 0.0003
00034
00035
00036
00037 static char *point_style[3] = { "invis\0", "filled\0", 0 };
00038
00039
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
00063
00064
00065 static polygon_t p_polygon = { FALSE, 1, 0, 0., 0., 0. };
00066
00067
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
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
00104 #define SPECIAL_CORNERS(style) \
00105 ((style) & (ROUNDED | DIAGONALS | DOGEAR | TAB | FOLDER | BOX3D | COMPONENT))
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
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[] = {
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
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
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
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;
00306 do {
00307 qp++;
00308 *(qp-1) = *qp;
00309 } while (*qp);
00310 } else if (streq(p, "diagonals")) {
00311 istyle |= DIAGONALS;
00312 qp = pp;
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;
00354 p.y = y * ND_ht_i(n) / 2.0;
00355 p.x = ND_rw_i(n) * x;
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
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
00445 gvrender_set_pencolor(job, penc);
00446 if (style & FILLED)
00447 gvrender_set_fillcolor(job, fillc);
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);
00465
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
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
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498 gvrender_set_pencolor(job, penc);
00499 if (style & FILLED)
00500 gvrender_set_fillcolor(job, fillc);
00501
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
00516 C[0] = B[3];
00517 C[1] = B[2];
00518 gvrender_polyline(job, C, 2);
00519 break;
00520 case FOLDER:
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534 gvrender_set_pencolor(job, penc);
00535 if (style & FILLED)
00536 gvrender_set_fillcolor(job, fillc);
00537
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);
00558
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
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);
00584
00585
00586
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
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
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
00654
00655
00656
00657
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
00717
00718
00719
00720
00721
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) {
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
00746 dimen = ND_label(n)->dimen;
00747
00748
00749 if ((dimen.x > 0.0) || (dimen.y > 0.0)) {
00750
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
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
00776
00777
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;
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;
00806 imagesize.y += 2;
00807 }
00808 }
00809
00810
00811 bb.x = MAX(dimen.x, imagesize.x);
00812 bb.y = MAX(dimen.y, imagesize.y);
00813
00814
00815
00816 if ((sides <= 2) && ((distortion != 0.) || (skew != 0.))) {
00817 sides = 120;
00818 }
00819
00820
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
00832 } else {
00833
00834
00835 temp = bb.y * SQRT2;
00836
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
00851
00852 }
00853 #endif
00854 }
00855
00856
00857 min_bb = bb;
00858
00859
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
00874
00875
00876 if (regular) {
00877 bb.x = bb.y = MAX(bb.x, bb.y);
00878 }
00879
00880
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
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) {
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
00943 angle += sectorangle;
00944 sincos(angle, &sinx, &cosx);
00945 R.x += sidelength * cosx;
00946 R.y += sidelength * sinx;
00947
00948
00949 P.x = R.x * (skewdist + R.y * gdistortion) + R.y * gskew;
00950 P.y = R.y;
00951
00952
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
00960 P.x *= bb.x;
00961 P.y *= bb.y;
00962
00963
00964 xmax = MAX(fabs(P.x), xmax);
00965 ymax = MAX(fabs(P.y), ymax);
00966
00967
00968 vertices[i] = P;
00969 }
00970
00971
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
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
01001
01002 temp = GAP / sin(gamma);
01003
01004
01005 sincos((alpha - gamma), &sinx, &cosx);
01006 sinx *= temp;
01007 cosx *= temp;
01008
01009
01010
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;
01052 static polygon_t *poly;
01053 static int last, outp, sides;
01054 static pointf O;
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
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
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
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
01096 outp = (poly->peripheries - 1) * sides;
01097 if (outp < 0)
01098 outp = 0;
01099 lastn = n;
01100 }
01101
01102
01103 P.x *= scalex;
01104 P.y *= scaley;
01105
01106
01107 if ((fabs(P.x) > box_URx) || (fabs(P.y) > box_URy))
01108 return FALSE;
01109
01110
01111 if (sides <= 2)
01112 return (hypot(P.x / box_URx, P.y / box_URy) < 1.);
01113
01114
01115 i = last % sides;
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;
01137 return TRUE;
01138 }
01139
01140
01141
01142
01143
01144
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
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
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
01247
01248
01249
01250
01251
01252 static point
01253 compassPoint(inside_t* ictxt, double y, double x)
01254 {
01255 point p;
01256 pointf curve[4];
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
01277
01278
01279
01280
01281
01282
01283
01284
01285
01286
01287
01288
01289
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
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;
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
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;
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));
01528 filled = TRUE;
01529 } else {
01530 filled = FALSE;
01531 }
01532 pencolor(job, n);
01533 }
01534
01535 pfilled = !ND_shape(n)->usershape || streq(ND_shape(n)->name, "custom");
01536
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
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
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;
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
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
01607
01608
01609
01610
01611
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
01623
01624
01625
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))
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;
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
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
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);
01756 pencolor(job, n);
01757 }
01758 filled = TRUE;
01759
01760
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
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
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
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
01954 if ((dimen.x > 0.0) || (dimen.y > 0.0)) {
01955
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
01995 d.x = sz.x - f->size.x;
01996 d.y = sz.y - f->size.y;
01997 f->size = sz;
01998
01999
02000 if (f->lp && !nojustify_p) {
02001 P2PF(d, f->lp->d);
02002 }
02003
02004
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
02024
02025
02026
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
02096 static void record_init(node_t * n)
02097 {
02098 field_t *info;
02099 point ul, sz;
02100 int flip, len;
02101 char *textbuf;
02102 int sides = BOTTOM | RIGHT | TOP | LEFT;
02103
02104
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
02122
02123
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;
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
02200
02201
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
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
02225
02226
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
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));
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
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 }