|
Graphviz
2.31.20130618.0446
|
00001 /* $Id$ $Revision$ */ 00002 /* vim:set shiftwidth=4 ts=8: */ 00003 00004 /************************************************************************* 00005 * Copyright (c) 2011 AT&T Intellectual Property 00006 * All rights reserved. This program and the accompanying materials 00007 * are made available under the terms of the Eclipse Public License v1.0 00008 * which accompanies this distribution, and is available at 00009 * http://www.eclipse.org/legal/epl-v10.html 00010 * 00011 * Contributors: See CVS logs. Details at http://www.graphviz.org/ 00012 *************************************************************************/ 00013 00014 00015 #ifdef HAVE_CONFIG_H 00016 #include "config.h" 00017 #endif 00018 00019 #include "neato.h" 00020 #include "adjust.h" 00021 #include "pathplan.h" 00022 #include "vispath.h" 00023 #include "multispline.h" 00024 #ifndef HAVE_DRAND48 00025 extern double drand48(void); 00026 #endif 00027 00028 #ifdef ORTHO 00029 #include <ortho.h> 00030 #endif 00031 00032 extern void printvis(vconfig_t * cp); 00033 extern int in_poly(Ppoly_t argpoly, Ppoint_t q); 00034 00035 00036 static boolean spline_merge(node_t * n) 00037 { 00038 return FALSE; 00039 } 00040 00041 static boolean swap_ends_p(edge_t * e) 00042 { 00043 return FALSE; 00044 } 00045 00046 static splineInfo sinfo = { swap_ends_p, spline_merge }; 00047 00048 static void 00049 make_barriers(Ppoly_t ** poly, int npoly, int pp, int qp, 00050 Pedge_t ** barriers, int *n_barriers) 00051 { 00052 int i, j, k, n, b; 00053 Pedge_t *bar; 00054 00055 n = 0; 00056 for (i = 0; i < npoly; i++) { 00057 if (i == pp) 00058 continue; 00059 if (i == qp) 00060 continue; 00061 n = n + poly[i]->pn; 00062 } 00063 bar = N_GNEW(n, Pedge_t); 00064 b = 0; 00065 for (i = 0; i < npoly; i++) { 00066 if (i == pp) 00067 continue; 00068 if (i == qp) 00069 continue; 00070 for (j = 0; j < poly[i]->pn; j++) { 00071 k = j + 1; 00072 if (k >= poly[i]->pn) 00073 k = 0; 00074 bar[b].a = poly[i]->ps[j]; 00075 bar[b].b = poly[i]->ps[k]; 00076 b++; 00077 } 00078 } 00079 assert(b == n); 00080 *barriers = bar; 00081 *n_barriers = n; 00082 } 00083 00084 /* genPt: 00085 */ 00086 static Ppoint_t genPt(double x, double y, pointf c) 00087 { 00088 Ppoint_t p; 00089 00090 p.x = x + c.x; 00091 p.y = y + c.y; 00092 return p; 00093 } 00094 00095 00096 /* recPt: 00097 */ 00098 static Ppoint_t recPt(double x, double y, pointf c, expand_t* m) 00099 { 00100 Ppoint_t p; 00101 00102 p.x = (x * m->x) + c.x; 00103 p.y = (y * m->y) + c.y; 00104 return p; 00105 } 00106 00107 typedef struct { 00108 node_t *n1; 00109 pointf p1; 00110 node_t *n2; 00111 pointf p2; 00112 } edgeinfo; 00113 typedef struct { 00114 Dtlink_t link; 00115 edgeinfo id; 00116 edge_t *e; 00117 } edgeitem; 00118 00119 static void *newitem(Dt_t * d, edgeitem * obj, Dtdisc_t * disc) 00120 { 00121 edgeitem *newp; 00122 00123 NOTUSED(disc); 00124 newp = NEW(edgeitem); 00125 newp->id = obj->id; 00126 newp->e = obj->e; 00127 ED_count(newp->e) = 1; 00128 00129 return newp; 00130 } 00131 00132 static void freeitem(Dt_t * d, edgeitem * obj, Dtdisc_t * disc) 00133 { 00134 free(obj); 00135 } 00136 00137 static int 00138 cmpitems(Dt_t * d, edgeinfo * key1, edgeinfo * key2, Dtdisc_t * disc) 00139 { 00140 int x; 00141 00142 if (key1->n1 > key2->n1) 00143 return 1; 00144 if (key1->n1 < key2->n1) 00145 return -1; 00146 if (key1->n2 > key2->n2) 00147 return 1; 00148 if (key1->n2 < key2->n2) 00149 return -1; 00150 00151 if ((x = key1->p1.x - key2->p1.x)) 00152 return x; 00153 if ((x = key1->p1.y - key2->p1.y)) 00154 return x; 00155 if ((x = key1->p2.x - key2->p2.x)) 00156 return x; 00157 return (key1->p2.y - key2->p2.y); 00158 } 00159 00160 Dtdisc_t edgeItemDisc = { 00161 offsetof(edgeitem, id), 00162 sizeof(edgeinfo), 00163 offsetof(edgeitem, link), 00164 (Dtmake_f) newitem, 00165 (Dtfree_f) freeitem, 00166 (Dtcompar_f) cmpitems, 00167 0, 00168 0, 00169 0 00170 }; 00171 00172 /* equivEdge: 00173 * See if we have already encountered an edge between the same 00174 * node:port pairs. If so, return the earlier edge. If not, 00175 * this edge is added to map and returned. 00176 * We first have to canonicalize the key fields using a lexicographic 00177 * ordering. 00178 */ 00179 static edge_t *equivEdge(Dt_t * map, edge_t * e) 00180 { 00181 edgeinfo test; 00182 edgeitem dummy; 00183 edgeitem *ip; 00184 00185 if (agtail(e) < aghead(e)) { 00186 test.n1 = agtail(e); 00187 test.p1 = ED_tail_port(e).p; 00188 test.n2 = aghead(e); 00189 test.p2 = ED_head_port(e).p; 00190 } else if (agtail(e) > aghead(e)) { 00191 test.n2 = agtail(e); 00192 test.p2 = ED_tail_port(e).p; 00193 test.n1 = aghead(e); 00194 test.p1 = ED_head_port(e).p; 00195 } else { 00196 pointf hp = ED_head_port(e).p; 00197 pointf tp = ED_tail_port(e).p; 00198 if (tp.x < hp.x) { 00199 test.p1 = tp; 00200 test.p2 = hp; 00201 } else if (tp.x > hp.x) { 00202 test.p1 = hp; 00203 test.p2 = tp; 00204 } else if (tp.y < hp.y) { 00205 test.p1 = tp; 00206 test.p2 = hp; 00207 } else if (tp.y > hp.y) { 00208 test.p1 = hp; 00209 test.p2 = tp; 00210 } else { 00211 test.p1 = test.p2 = tp; 00212 } 00213 test.n2 = test.n1 = agtail(e); 00214 } 00215 dummy.id = test; 00216 dummy.e = e; 00217 ip = dtinsert(map, &dummy); 00218 return ip->e; 00219 } 00220 00221 00222 /* makeSelfArcs: 00223 * Generate loops. We use the library routine makeSelfEdge 00224 * which also places the labels. 00225 * We have to handle port labels here. 00226 * as well as update the bbox from edge labels. 00227 */ 00228 void makeSelfArcs(path * P, edge_t * e, int stepx) 00229 { 00230 int cnt = ED_count(e); 00231 00232 if ((cnt == 1) || Concentrate) { 00233 edge_t *edges1[1]; 00234 edges1[0] = e; 00235 makeSelfEdge(P, edges1, 0, 1, stepx, stepx, &sinfo); 00236 if (ED_label(e)) 00237 updateBB(agraphof(agtail(e)), ED_label(e)); 00238 makePortLabels(e); 00239 } else { 00240 int i; 00241 edge_t **edges = N_GNEW(cnt, edge_t *); 00242 for (i = 0; i < cnt; i++) { 00243 edges[i] = e; 00244 e = ED_to_virt(e); 00245 } 00246 makeSelfEdge(P, edges, 0, cnt, stepx, stepx, &sinfo); 00247 for (i = 0; i < cnt; i++) { 00248 e = edges[i]; 00249 if (ED_label(e)) 00250 updateBB(agraphof(agtail(e)), ED_label(e)); 00251 makePortLabels(e); 00252 } 00253 free(edges); 00254 } 00255 } 00256 00257 /* makeObstacle: 00258 * Given a node, return an obstacle reflecting the 00259 * node's geometry. pmargin specifies how much space to allow 00260 * around the polygon. 00261 * Returns the constructed polygon on success, NULL on failure. 00262 * Failure means the node shape is not supported. 00263 * 00264 * The polygon has its vertices in CW order. 00265 * 00266 */ 00267 Ppoly_t *makeObstacle(node_t * n, expand_t* pmargin) 00268 { 00269 Ppoly_t *obs; 00270 polygon_t *poly; 00271 double adj = 0.0; 00272 int j, sides; 00273 pointf polyp; 00274 boxf b; 00275 pointf pt; 00276 field_t *fld; 00277 epsf_t *desc; 00278 00279 switch (shapeOf(n)) { 00280 case SH_POLY: 00281 case SH_POINT: 00282 obs = NEW(Ppoly_t); 00283 poly = (polygon_t *) ND_shape_info(n); 00284 if (poly->sides >= 3) { 00285 sides = poly->sides; 00286 } else { /* ellipse */ 00287 sides = 8; 00288 adj = drand48() * .01; 00289 } 00290 obs->pn = sides; 00291 obs->ps = N_NEW(sides, Ppoint_t); 00292 /* assuming polys are in CCW order, and pathplan needs CW */ 00293 for (j = 0; j < sides; j++) { 00294 double xmargin = 0.0, ymargin = 0.0; 00295 if (poly->sides >= 3) { 00296 if (pmargin->doAdd) { 00297 if (poly->sides == 4) { 00298 switch (j) { 00299 case 0 : 00300 xmargin = pmargin->x; 00301 ymargin = pmargin->y; 00302 break; 00303 case 1 : 00304 xmargin = -pmargin->x; 00305 ymargin = pmargin->y; 00306 break; 00307 case 2 : 00308 xmargin = -pmargin->x; 00309 ymargin = -pmargin->y; 00310 break; 00311 case 3 : 00312 xmargin = pmargin->x; 00313 ymargin = -pmargin->y; 00314 break; 00315 } 00316 polyp.x = poly->vertices[j].x + xmargin; 00317 polyp.y = poly->vertices[j].y + ymargin; 00318 } 00319 else { 00320 double h = LEN(poly->vertices[j].x,poly->vertices[j].y); 00321 polyp.x = poly->vertices[j].x * (1.0 + pmargin->x/h); 00322 polyp.y = poly->vertices[j].y * (1.0 + pmargin->y/h); 00323 } 00324 } 00325 else { 00326 polyp.x = poly->vertices[j].x * pmargin->x; 00327 polyp.y = poly->vertices[j].y * pmargin->y; 00328 } 00329 } else { 00330 double c, s; 00331 c = cos(2.0 * M_PI * j / sides + adj); 00332 s = sin(2.0 * M_PI * j / sides + adj); 00333 if (pmargin->doAdd) { 00334 polyp.x = c*(ND_lw(n)+ND_rw(n)+pmargin->x) / 2.0; 00335 polyp.y = s*(ND_ht(n)+pmargin->y) / 2.0; 00336 } 00337 else { 00338 polyp.x = pmargin->x * c * (ND_lw(n) + ND_rw(n)) / 2.0; 00339 polyp.y = pmargin->y * s * ND_ht(n) / 2.0; 00340 } 00341 } 00342 obs->ps[sides - j - 1].x = polyp.x + ND_coord(n).x; 00343 obs->ps[sides - j - 1].y = polyp.y + ND_coord(n).y; 00344 } 00345 break; 00346 case SH_RECORD: 00347 fld = (field_t *) ND_shape_info(n); 00348 b = fld->b; 00349 obs = NEW(Ppoly_t); 00350 obs->pn = 4; 00351 obs->ps = N_NEW(4, Ppoint_t); 00352 /* CW order */ 00353 pt = ND_coord(n); 00354 if (pmargin->doAdd) { 00355 obs->ps[0] = genPt(b.LL.x-pmargin->x, b.LL.y-pmargin->y, pt); 00356 obs->ps[1] = genPt(b.LL.x-pmargin->x, b.UR.y+pmargin->y, pt); 00357 obs->ps[2] = genPt(b.UR.x+pmargin->x, b.UR.y+pmargin->y, pt); 00358 obs->ps[3] = genPt(b.UR.x+pmargin->x, b.LL.y-pmargin->y, pt); 00359 } 00360 else { 00361 obs->ps[0] = recPt(b.LL.x, b.LL.y, pt, pmargin); 00362 obs->ps[1] = recPt(b.LL.x, b.UR.y, pt, pmargin); 00363 obs->ps[2] = recPt(b.UR.x, b.UR.y, pt, pmargin); 00364 obs->ps[3] = recPt(b.UR.x, b.LL.y, pt, pmargin); 00365 } 00366 break; 00367 case SH_EPSF: 00368 desc = (epsf_t *) (ND_shape_info(n)); 00369 obs = NEW(Ppoly_t); 00370 obs->pn = 4; 00371 obs->ps = N_NEW(4, Ppoint_t); 00372 /* CW order */ 00373 pt = ND_coord(n); 00374 if (pmargin->doAdd) { 00375 obs->ps[0] = genPt(-ND_lw(n)-pmargin->x, -ND_ht(n)-pmargin->y,pt); 00376 obs->ps[1] = genPt(-ND_lw(n)-pmargin->x, ND_ht(n)+pmargin->y,pt); 00377 obs->ps[2] = genPt(ND_rw(n)+pmargin->x, ND_ht(n)+pmargin->y,pt); 00378 obs->ps[3] = genPt(ND_rw(n)+pmargin->x, -ND_ht(n)-pmargin->y,pt); 00379 } 00380 else { 00381 obs->ps[0] = recPt(-ND_lw(n), -ND_ht(n), pt, pmargin); 00382 obs->ps[1] = recPt(-ND_lw(n), ND_ht(n), pt, pmargin); 00383 obs->ps[2] = recPt(ND_rw(n), ND_ht(n), pt, pmargin); 00384 obs->ps[3] = recPt(ND_rw(n), -ND_ht(n), pt, pmargin); 00385 } 00386 break; 00387 default: 00388 obs = NULL; 00389 break; 00390 } 00391 return obs; 00392 } 00393 00394 /* getPath 00395 * Construct the shortest path from one endpoint of e to the other. 00396 * The obstacles and their number are given by obs and npoly. 00397 * vconfig is a precomputed data structure to help in the computation. 00398 * If chkPts is true, the function finds the polygons, if any, containing 00399 * the endpoints and tells the shortest path computation to ignore them. 00400 * Assumes this info is set in ND_lim, usually from _spline_edges. 00401 * Returns the shortest path. 00402 */ 00403 Ppolyline_t 00404 getPath(edge_t * e, vconfig_t * vconfig, int chkPts, Ppoly_t ** obs, 00405 int npoly) 00406 { 00407 Ppolyline_t line; 00408 int pp, qp; 00409 Ppoint_t p, q; 00410 00411 p = add_pointf(ND_coord(agtail(e)), ED_tail_port(e).p); 00412 q = add_pointf(ND_coord(aghead(e)), ED_head_port(e).p); 00413 00414 /* determine the polygons (if any) that contain the endpoints */ 00415 pp = qp = POLYID_NONE; 00416 if (chkPts) { 00417 pp = ND_lim(agtail(e)); 00418 qp = ND_lim(aghead(e)); 00419 /* 00420 for (i = 0; i < npoly; i++) { 00421 if ((pp == POLYID_NONE) && in_poly(*obs[i], p)) 00422 pp = i; 00423 if ((qp == POLYID_NONE) && in_poly(*obs[i], q)) 00424 qp = i; 00425 } 00426 */ 00427 } 00428 Pobspath(vconfig, p, pp, q, qp, &line); 00429 return line; 00430 } 00431 00432 /* makePolyline: 00433 */ 00434 static void 00435 makePolyline(graph_t* g, edge_t * e) 00436 { 00437 Ppolyline_t spl, line = ED_path(e); 00438 Ppoint_t p0, q0; 00439 00440 p0 = line.ps[0]; 00441 q0 = line.ps[line.pn - 1]; 00442 make_polyline (line, &spl); 00443 if (Verbose > 1) 00444 fprintf(stderr, "polyline %s %s\n", agnameof(agtail(e)), agnameof(aghead(e))); 00445 clip_and_install(e, aghead(e), spl.ps, spl.pn, &sinfo); 00446 addEdgeLabels(g, e, p0, q0); 00447 } 00448 00449 /* makeSpline: 00450 * Construct a spline connecting the endpoints of e, avoiding the npoly 00451 * obstacles obs. 00452 * The resultant spline is attached to the edge, the positions of any 00453 * edge labels are computed, and the graph's bounding box is recomputed. 00454 * 00455 * If chkPts is true, the function checks if one or both of the endpoints 00456 * is on or inside one of the obstacles and, if so, tells the shortest path 00457 * computation to ignore them. 00458 */ 00459 void makeSpline(graph_t* g, edge_t * e, Ppoly_t ** obs, int npoly, boolean chkPts) 00460 { 00461 Ppolyline_t line, spline; 00462 Pvector_t slopes[2]; 00463 int i, n_barriers; 00464 int pp, qp; 00465 Ppoint_t p, q; 00466 Pedge_t *barriers; 00467 00468 line = ED_path(e); 00469 p = line.ps[0]; 00470 q = line.ps[line.pn - 1]; 00471 /* determine the polygons (if any) that contain the endpoints */ 00472 pp = qp = POLYID_NONE; 00473 if (chkPts) 00474 for (i = 0; i < npoly; i++) { 00475 if ((pp == POLYID_NONE) && in_poly(*obs[i], p)) 00476 pp = i; 00477 if ((qp == POLYID_NONE) && in_poly(*obs[i], q)) 00478 qp = i; 00479 } 00480 00481 make_barriers(obs, npoly, pp, qp, &barriers, &n_barriers); 00482 slopes[0].x = slopes[0].y = 0.0; 00483 slopes[1].x = slopes[1].y = 0.0; 00484 if (Proutespline(barriers, n_barriers, line, slopes, &spline) < 0) { 00485 agerr (AGERR, "makeSpline: failed to make spline edge (%s,%s)\n", agnameof(agtail(e)), agnameof(aghead(e))); 00486 return; 00487 } 00488 00489 /* north why did you ever use int coords */ 00490 if (Verbose > 1) 00491 fprintf(stderr, "spline %s %s\n", agnameof(agtail(e)), agnameof(aghead(e))); 00492 clip_and_install(e, aghead(e), spline.ps, spline.pn, &sinfo); 00493 free(barriers); 00494 addEdgeLabels(g, e, p, q); 00495 } 00496 00497 /* True if either head or tail has a port on its boundary */ 00498 #define BOUNDARY_PORT(e) ((ED_tail_port(e).side)||(ED_head_port(e).side)) 00499 00500 /* _spline_edges: 00501 * Basic default routine for creating edges. 00502 * If splines are requested, we construct the obstacles. 00503 * If not, or nodes overlap, the function reverts to line segments. 00504 * NOTE: intra-cluster edges are not constrained to 00505 * remain in the cluster's bounding box and, conversely, a cluster's box 00506 * is not altered to reflect intra-cluster edges. 00507 * If Nop > 1 and the spline exists, it is just copied. 00508 * NOTE: if edgetype = ET_NONE, we shouldn't be here. 00509 */ 00510 static int _spline_edges(graph_t * g, expand_t* pmargin, int edgetype) 00511 { 00512 node_t *n; 00513 edge_t *e; 00514 edge_t *e0; 00515 Ppoly_t **obs = 0; 00516 Ppoly_t *obp; 00517 int cnt, i = 0, npoly; 00518 vconfig_t *vconfig = 0; 00519 path *P = NULL; 00520 int useEdges = (Nop > 1); 00521 router_t* rtr = 0; 00522 int legal = 0; 00523 00524 /* build configuration */ 00525 if (edgetype >= ET_PLINE) { 00526 obs = N_NEW(agnnodes(g), Ppoly_t *); 00527 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00528 obp = makeObstacle(n, pmargin); 00529 if (obp) { 00530 ND_lim(n) = i; 00531 obs[i++] = obp; 00532 } 00533 else 00534 ND_lim(n) = POLYID_NONE; 00535 } 00536 } else { 00537 obs = 0; 00538 } 00539 npoly = i; 00540 if (obs) { 00541 if ((legal = Plegal_arrangement(obs, npoly))) { 00542 if (edgetype != ET_ORTHO) vconfig = Pobsopen(obs, npoly); 00543 } 00544 else if (Verbose) 00545 fprintf(stderr, 00546 "nodes touch - falling back to straight line edges\n"); 00547 } 00548 00549 /* route edges */ 00550 if (Verbose) 00551 fprintf(stderr, "Creating edges using %s\n", 00552 (legal && (edgetype == ET_ORTHO)) ? "orthogonal lines" : 00553 (vconfig ? (edgetype == ET_SPLINE ? "splines" : "polylines") : 00554 "line segments")); 00555 if (vconfig) { 00556 /* path-finding pass */ 00557 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00558 for (e = agfstout(g, n); e; e = agnxtout(g, e)) { 00559 ED_path(e) = getPath(e, vconfig, TRUE, obs, npoly); 00560 } 00561 } 00562 } 00563 #ifdef ORTHO 00564 else if (legal && (edgetype == ET_ORTHO)) { 00565 orthoEdges (g, 0); 00566 useEdges = 1; 00567 } 00568 #endif 00569 00570 /* spline-drawing pass */ 00571 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00572 for (e = agfstout(g, n); e; e = agnxtout(g, e)) { 00573 /* fprintf (stderr, "%s -- %s %d\n", agnameof(agtail(e)), agnameof(aghead(e)), ED_count(e)); */ 00574 node_t *head = aghead(e); 00575 if (useEdges && ED_spl(e)) { 00576 addEdgeLabels(g, e, 00577 add_pointf(ND_coord(n), ED_tail_port(e).p), 00578 add_pointf(ND_coord(head), ED_head_port(e).p)); 00579 } 00580 else if (ED_count(e) == 0) continue; /* only do representative */ 00581 else if (n == head) { /* self arc */ 00582 if (!P) { 00583 P = NEW(path); 00584 P->boxes = N_NEW(agnnodes(g) + 20 * 2 * 9, boxf); 00585 } 00586 makeSelfArcs(P, e, GD_nodesep(g->root)); 00587 } else if (vconfig) { /* ET_SPLINE or ET_PLINE */ 00588 #ifdef HAVE_GTS 00589 if ((ED_count(e) > 1) || BOUNDARY_PORT(e)) { 00590 int fail = 0; 00591 if ((ED_path(e).pn == 2) && !BOUNDARY_PORT(e)) 00592 /* if a straight line can connect the ends */ 00593 makeStraightEdge(g, e, edgetype, &sinfo); 00594 else { 00595 if (!rtr) rtr = mkRouter (obs, npoly); 00596 fail = makeMultiSpline(g, e, rtr, edgetype == ET_PLINE); 00597 } 00598 if (!fail) continue; 00599 } 00600 /* We can probably remove this branch and just use 00601 * makeMultiSpline. It can also catch the makeStraightEdge 00602 * case. We could then eliminate all of the vconfig stuff. 00603 */ 00604 #endif 00605 cnt = ED_count(e); 00606 if (Concentrate) cnt = 1; /* only do representative */ 00607 e0 = e; 00608 for (i = 0; i < cnt; i++) { 00609 if (edgetype == ET_SPLINE) 00610 makeSpline(g, e0, obs, npoly, TRUE); 00611 else 00612 makePolyline(g, e0); 00613 e0 = ED_to_virt(e0); 00614 } 00615 } else { 00616 makeStraightEdge(g, e, edgetype, &sinfo); 00617 } 00618 } 00619 } 00620 00621 #ifdef HAVE_GTS 00622 if (rtr) 00623 freeRouter (rtr); 00624 #endif 00625 00626 if (vconfig) 00627 Pobsclose (vconfig); 00628 if (P) { 00629 free(P->boxes); 00630 free(P); 00631 } 00632 if (obs) { 00633 for (i=0; i < npoly; i++) 00634 free (obs[i]); 00635 free (obs); 00636 } 00637 return 0; 00638 } 00639 00640 /* splineEdges: 00641 * Main wrapper code for generating edges. 00642 * Sets desired separation. 00643 * Coalesces equivalent edges (edges * with the same endpoints). 00644 * It then calls the edge generating function, and marks the 00645 * spline phase complete. 00646 * Returns 0 on success. 00647 * 00648 * The edge function is given the graph, the separation to be added 00649 * around obstacles, and the type of edge. It must guarantee 00650 * that all bounding boxes are current; in particular, the bounding box of 00651 * g must reflect the addition of the edges. 00652 */ 00653 int 00654 splineEdges(graph_t * g, int (*edgefn) (graph_t *, expand_t*, int), 00655 int edgetype) 00656 { 00657 node_t *n; 00658 edge_t *e; 00659 expand_t margin; 00660 Dt_t *map; 00661 00662 margin = esepFactor (g); 00663 00664 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00665 for (e = agfstout(g, n); e; e = agnxtout(g, e)) { 00666 resolvePorts (e); 00667 } 00668 } 00669 00670 /* find equivalent edges */ 00671 map = dtopen(&edgeItemDisc, Dtoset); 00672 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00673 for (e = agfstout(g, n); e; e = agnxtout(g, e)) { 00674 edge_t *leader = equivEdge(map, e); 00675 if (leader != e) { 00676 ED_count(leader)++; 00677 ED_to_virt(e) = ED_to_virt(leader); 00678 ED_to_virt(leader) = e; 00679 } 00680 } 00681 } 00682 dtclose(map); 00683 00684 if (edgefn(g, &margin, edgetype)) 00685 return 1; 00686 00687 State = GVSPLINES; 00688 return 0; 00689 } 00690 00691 /* spline_edges1: 00692 * Construct edges using default algorithm and given splines value. 00693 * Return 0 on success. 00694 */ 00695 int spline_edges1(graph_t * g, int edgetype) 00696 { 00697 return splineEdges(g, _spline_edges, edgetype); 00698 } 00699 00700 /* spline_edges0: 00701 * Sets the graph's aspect ratio. 00702 * Check splines attribute and construct edges using default algorithm. 00703 * If the splines attribute is defined but equal to "", skip edge routing. 00704 * 00705 * Assumes u.bb for has been computed for g and all clusters 00706 * (not just top-level clusters), and that GD_bb(g).LL is at the origin. 00707 * 00708 * This last criterion is, I believe, mainly to simplify the code 00709 * in neato_set_aspect. It would be good to remove this constraint, 00710 * as this would allow nodes pinned on input to have the same coordinates 00711 * when output in dot or plain format. 00712 * 00713 */ 00714 void spline_edges0(graph_t * g) 00715 { 00716 int et = EDGE_TYPE (g); 00717 neato_set_aspect(g); 00718 if (et == ET_NONE) return; 00719 #ifndef ORTHO 00720 if (et == ET_ORTHO) { 00721 agerr (AGWARN, "Orthogonal edges not yet supported\n"); 00722 et = ET_PLINE; 00723 GD_flags(g->root) &= ~ET_ORTHO; 00724 GD_flags(g->root) |= ET_PLINE; 00725 } 00726 #endif 00727 spline_edges1(g, et); 00728 } 00729 00730 /* shiftClusters: 00731 */ 00732 static void 00733 shiftClusters (graph_t * g, pointf offset) 00734 { 00735 int i; 00736 00737 for (i = 1; i <= GD_n_cluster(g); i++) { 00738 shiftClusters (GD_clust(g)[i], offset); 00739 } 00740 00741 GD_bb(g).UR.x -= offset.x; 00742 GD_bb(g).UR.y -= offset.y; 00743 GD_bb(g).LL.x -= offset.x; 00744 GD_bb(g).LL.y -= offset.y; 00745 } 00746 00747 /* spline_edges: 00748 * Compute bounding box, translate graph to origin, 00749 * then construct all edges. 00750 */ 00751 void spline_edges(graph_t * g) 00752 { 00753 node_t *n; 00754 pointf offset; 00755 00756 compute_bb(g); 00757 offset.x = PS2INCH(GD_bb(g).LL.x); 00758 offset.y = PS2INCH(GD_bb(g).LL.y); 00759 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00760 ND_pos(n)[0] -= offset.x; 00761 ND_pos(n)[1] -= offset.y; 00762 } 00763 00764 shiftClusters (g, GD_bb(g).LL); 00765 spline_edges0(g); 00766 } 00767 00768 /* scaleEdge: 00769 * Scale edge by given factor. 00770 * Assume ED_spl != NULL. 00771 */ 00772 static void scaleEdge(edge_t * e, double xf, double yf) 00773 { 00774 int i, j; 00775 pointf *pt; 00776 bezier *bez; 00777 pointf delh, delt; 00778 00779 delh.x = POINTS_PER_INCH * (ND_pos(aghead(e))[0] * (xf - 1.0)); 00780 delh.y = POINTS_PER_INCH * (ND_pos(aghead(e))[1] * (yf - 1.0)); 00781 delt.x = POINTS_PER_INCH * (ND_pos(agtail(e))[0] * (xf - 1.0)); 00782 delt.y = POINTS_PER_INCH * (ND_pos(agtail(e))[1] * (yf - 1.0)); 00783 00784 bez = ED_spl(e)->list; 00785 for (i = 0; i < ED_spl(e)->size; i++) { 00786 pt = bez->list; 00787 for (j = 0; j < bez->size; j++) { 00788 if ((i == 0) && (j == 0)) { 00789 pt->x += delt.x; 00790 pt->y += delt.y; 00791 } 00792 else if ((i == ED_spl(e)->size-1) && (j == bez->size-1)) { 00793 pt->x += delh.x; 00794 pt->y += delh.y; 00795 } 00796 else { 00797 pt->x *= xf; 00798 pt->y *= yf; 00799 } 00800 pt++; 00801 } 00802 if (bez->sflag) { 00803 bez->sp.x += delt.x; 00804 bez->sp.y += delt.y; 00805 } 00806 if (bez->eflag) { 00807 bez->ep.x += delh.x; 00808 bez->ep.y += delh.y; 00809 } 00810 bez++; 00811 } 00812 00813 if (ED_label(e) && ED_label(e)->set) { 00814 ED_label(e)->pos.x *= xf; 00815 ED_label(e)->pos.y *= yf; 00816 } 00817 if (ED_head_label(e) && ED_head_label(e)->set) { 00818 ED_head_label(e)->pos.x += delh.x; 00819 ED_head_label(e)->pos.y += delh.y; 00820 } 00821 if (ED_tail_label(e) && ED_tail_label(e)->set) { 00822 ED_tail_label(e)->pos.x += delt.x; 00823 ED_tail_label(e)->pos.y += delt.y; 00824 } 00825 } 00826 00827 /* scaleBB: 00828 * Scale bounding box of clusters of g by given factors. 00829 */ 00830 static void scaleBB(graph_t * g, double xf, double yf) 00831 { 00832 int i; 00833 00834 GD_bb(g).UR.x *= xf; 00835 GD_bb(g).UR.y *= yf; 00836 GD_bb(g).LL.x *= xf; 00837 GD_bb(g).LL.y *= yf; 00838 00839 if (GD_label(g) && GD_label(g)->set) { 00840 GD_label(g)->pos.x *= xf; 00841 GD_label(g)->pos.y *= yf; 00842 } 00843 00844 for (i = 1; i <= GD_n_cluster(g); i++) 00845 scaleBB(GD_clust(g)[i], xf, yf); 00846 } 00847 00848 /* _neato_set_aspect; 00849 * Assume all bounding boxes are correct and 00850 * that GD_bb(g).LL is at origin. 00851 * Also assume g is the root graph 00852 */ 00853 static void _neato_set_aspect(graph_t * g) 00854 { 00855 /* int i; */ 00856 double xf, yf, actual, desired; 00857 node_t *n; 00858 00859 /* compute_bb(g); */ 00860 if (GD_drawing(g)->ratio_kind) { 00861 /* normalize */ 00862 assert(ROUND(GD_bb(g).LL.x) == 0); 00863 assert(ROUND(GD_bb(g).LL.y) == 0); 00864 if (GD_flip(g)) { 00865 double t = GD_bb(g).UR.x; 00866 GD_bb(g).UR.x = GD_bb(g).UR.y; 00867 GD_bb(g).UR.y = t; 00868 } 00869 if (GD_drawing(g)->ratio_kind == R_FILL) { 00870 /* fill is weird because both X and Y can stretch */ 00871 if (GD_drawing(g)->size.x <= 0) 00872 return; 00873 xf = (double) GD_drawing(g)->size.x / GD_bb(g).UR.x; 00874 yf = (double) GD_drawing(g)->size.y / GD_bb(g).UR.y; 00875 /* handle case where one or more dimensions is too big */ 00876 if ((xf < 1.0) || (yf < 1.0)) { 00877 if (xf < yf) { 00878 yf = yf / xf; 00879 xf = 1.0; 00880 } else { 00881 xf = xf / yf; 00882 yf = 1.0; 00883 } 00884 } 00885 } else if (GD_drawing(g)->ratio_kind == R_EXPAND) { 00886 if (GD_drawing(g)->size.x <= 0) 00887 return; 00888 xf = (double) GD_drawing(g)->size.x / GD_bb(g).UR.x; 00889 yf = (double) GD_drawing(g)->size.y / GD_bb(g).UR.y; 00890 if ((xf > 1.0) && (yf > 1.0)) { 00891 double scale = MIN(xf, yf); 00892 xf = yf = scale; 00893 } else 00894 return; 00895 } else if (GD_drawing(g)->ratio_kind == R_VALUE) { 00896 desired = GD_drawing(g)->ratio; 00897 actual = (GD_bb(g).UR.y) / (GD_bb(g).UR.x); 00898 if (actual < desired) { 00899 yf = desired / actual; 00900 xf = 1.0; 00901 } else { 00902 xf = actual / desired; 00903 yf = 1.0; 00904 } 00905 } else 00906 return; 00907 if (GD_flip(g)) { 00908 double t = xf; 00909 xf = yf; 00910 yf = t; 00911 } 00912 00913 if (Nop > 1) { 00914 edge_t *e; 00915 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00916 for (e = agfstout(g, n); e; e = agnxtout(g, e)) 00917 if (ED_spl(e)) 00918 scaleEdge(e, xf, yf); 00919 } 00920 } 00921 /* Not relying on neato_nlist here allows us not to have to 00922 * allocate it in the root graph and the connected components. 00923 */ 00924 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00925 ND_pos(n)[0] = ND_pos(n)[0] * xf; 00926 ND_pos(n)[1] = ND_pos(n)[1] * yf; 00927 } 00928 scaleBB(g, xf, yf); 00929 } 00930 } 00931 00932 /* neato_set_aspect: 00933 * Sets aspect ratio if necessary; real work done in _neato_set_aspect; 00934 * This also copies the internal layout coordinates (ND_pos) to the 00935 * external ones (ND_coord). 00936 */ 00937 void neato_set_aspect(graph_t * g) 00938 { 00939 node_t *n; 00940 00941 /* setting aspect ratio only makes sense on root graph */ 00942 if (g->root == g) 00943 _neato_set_aspect(g); 00944 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00945 ND_coord(n).x = POINTS_PER_INCH * (ND_pos(n)[0]); 00946 ND_coord(n).y = POINTS_PER_INCH * (ND_pos(n)[1]); 00947 } 00948 } 00949
1.7.5