|
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 /* 00016 * set edge splines. 00017 */ 00018 00019 #include "dot.h" 00020 00021 #ifdef ORTHO 00022 #include <ortho.h> 00023 #endif 00024 00025 #define NSUB 9 /* number of subdivisions, re-aiming splines */ 00026 #define CHUNK 128 /* in building list of edges */ 00027 00028 #define MINW 16 /* minimum width of a box in the edge path */ 00029 #define HALFMINW 8 00030 00031 #define FWDEDGE 16 00032 #define BWDEDGE 32 00033 00034 #define MAINGRAPH 64 00035 #define AUXGRAPH 128 00036 #define GRAPHTYPEMASK 192 /* the OR of the above */ 00037 00038 #ifndef WITH_CGRAPH 00039 #define MAKEFWDEDGE(new, old) { \ 00040 edge_t *newp; \ 00041 newp = new; \ 00042 *newp = *old; \ 00043 newp->tail = old->head; \ 00044 newp->head = old->tail; \ 00045 ED_tail_port(newp) = ED_head_port(old); \ 00046 ED_head_port(newp) = ED_tail_port(old); \ 00047 ED_edge_type(newp) = VIRTUAL; \ 00048 ED_to_orig(newp) = old; \ 00049 } 00050 #else /* WITH_CGRAPH */ 00051 #define MAKEFWDEDGE(new, old) { \ 00052 edge_t *newp; \ 00053 Agedgeinfo_t *info; \ 00054 newp = new; \ 00055 info = (Agedgeinfo_t*)newp->base.data; \ 00056 *info = *(Agedgeinfo_t*)old->base.data; \ 00057 *newp = *old; \ 00058 newp->base.data = (Agrec_t*)info; \ 00059 AGTAIL(newp) = AGHEAD(old); \ 00060 AGHEAD(newp) = AGTAIL(old); \ 00061 ED_tail_port(newp) = ED_head_port(old); \ 00062 ED_head_port(newp) = ED_tail_port(old); \ 00063 ED_edge_type(newp) = VIRTUAL; \ 00064 ED_to_orig(newp) = old; \ 00065 } 00066 #endif /* WITH_CGRAPH */ 00067 00068 static boxf boxes[1000]; 00069 typedef struct { 00070 int LeftBound, RightBound, Splinesep, Multisep; 00071 boxf* Rank_box; 00072 } spline_info_t; 00073 00074 static void adjustregularpath(path *, int, int); 00075 static Agedge_t *bot_bound(Agedge_t *, int); 00076 static boolean pathscross(Agnode_t *, Agnode_t *, Agedge_t *, Agedge_t *); 00077 static Agraph_t *cl_bound(Agnode_t *, Agnode_t *); 00078 static int cl_vninside(Agraph_t *, Agnode_t *); 00079 static void completeregularpath(path *, Agedge_t *, Agedge_t *, 00080 pathend_t *, pathend_t *, boxf *, int, int); 00081 static int edgecmp(Agedge_t **, Agedge_t **); 00082 static void make_flat_edge(spline_info_t*, path *, Agedge_t **, int, int, int); 00083 static void make_regular_edge(spline_info_t*, path *, Agedge_t **, int, int, int); 00084 static boxf makeregularend(boxf, int, int); 00085 static boxf maximal_bbox(spline_info_t*, Agnode_t *, Agedge_t *, Agedge_t *); 00086 static Agnode_t *neighbor(Agnode_t *, Agedge_t *, Agedge_t *, int); 00087 static void place_vnlabel(Agnode_t *); 00088 static boxf rank_box(spline_info_t* sp, Agraph_t *, int); 00089 static void recover_slack(Agedge_t *, path *); 00090 static void resize_vn(Agnode_t *, int, int, int); 00091 static void setflags(Agedge_t *, int, int, int); 00092 static int straight_len(Agnode_t *); 00093 static Agedge_t *straight_path(Agedge_t *, int, pointf *, int *); 00094 static Agedge_t *top_bound(Agedge_t *, int); 00095 00096 #define GROWEDGES (edges = ALLOC (n_edges + CHUNK, edges, edge_t*)) 00097 00098 static edge_t* 00099 getmainedge(edge_t * e) 00100 { 00101 edge_t *le = e; 00102 while (ED_to_virt(le)) 00103 le = ED_to_virt(le); 00104 while (ED_to_orig(le)) 00105 le = ED_to_orig(le); 00106 return le; 00107 } 00108 00109 static boolean spline_merge(node_t * n) 00110 { 00111 return ((ND_node_type(n) == VIRTUAL) 00112 && ((ND_in(n).size > 1) || (ND_out(n).size > 1))); 00113 } 00114 00115 static boolean swap_ends_p(edge_t * e) 00116 { 00117 while (ED_to_orig(e)) 00118 e = ED_to_orig(e); 00119 if (ND_rank(aghead(e)) > ND_rank(agtail(e))) 00120 return FALSE; 00121 if (ND_rank(aghead(e)) < ND_rank(agtail(e))) 00122 return TRUE; 00123 if (ND_order(aghead(e)) >= ND_order(agtail(e))) 00124 return FALSE; 00125 return TRUE; 00126 } 00127 00128 static splineInfo sinfo = { swap_ends_p, spline_merge }; 00129 00130 int portcmp(port p0, port p1) 00131 { 00132 int rv; 00133 if (p1.defined == FALSE) 00134 return (p0.defined ? 1 : 0); 00135 if (p0.defined == FALSE) 00136 return -1; 00137 rv = p0.p.x - p1.p.x; 00138 if (rv == 0) 00139 rv = p0.p.y - p1.p.y; 00140 return rv; 00141 } 00142 00143 /* swap_bezier: 00144 */ 00145 static void swap_bezier(bezier * old, bezier * new) 00146 { 00147 pointf *list; 00148 pointf *lp; 00149 pointf *olp; 00150 int i, sz; 00151 00152 sz = old->size; 00153 list = N_GNEW(sz, pointf); 00154 lp = list; 00155 olp = old->list + (sz - 1); 00156 for (i = 0; i < sz; i++) { /* reverse list of points */ 00157 *lp++ = *olp--; 00158 } 00159 00160 new->list = list; 00161 new->size = sz; 00162 new->sflag = old->eflag; 00163 new->eflag = old->sflag; 00164 new->sp = old->ep; 00165 new->ep = old->sp; 00166 } 00167 00168 /* swap_spline: 00169 */ 00170 static void swap_spline(splines * s) 00171 { 00172 bezier *list; 00173 bezier *lp; 00174 bezier *olp; 00175 int i, sz; 00176 00177 sz = s->size; 00178 list = N_GNEW(sz, bezier); 00179 lp = list; 00180 olp = s->list + (sz - 1); 00181 for (i = 0; i < sz; i++) { /* reverse and swap list of beziers */ 00182 swap_bezier(olp--, lp++); 00183 } 00184 00185 /* free old structures */ 00186 for (i = 0; i < sz; i++) 00187 free(s->list[i].list); 00188 free(s->list); 00189 00190 s->list = list; 00191 } 00192 00193 /* edge_normalize: 00194 * Some back edges are reversed during layout and the reversed edge 00195 * is used to compute the spline. We would like to guarantee that 00196 * the order of control points always goes from tail to head, so 00197 * we reverse them if necessary. 00198 */ 00199 static void edge_normalize(graph_t * g) 00200 { 00201 edge_t *e; 00202 node_t *n; 00203 00204 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00205 for (e = agfstout(g, n); e; e = agnxtout(g, e)) { 00206 if (sinfo.swapEnds(e) && ED_spl(e)) 00207 swap_spline(ED_spl(e)); 00208 } 00209 } 00210 } 00211 00212 /* resetRW: 00213 * In position, each node has its rw stored in mval and, 00214 * if a node is part of a loop, rw may be increased to 00215 * reflect the loops and associated labels. We restore 00216 * the original value here. 00217 */ 00218 static void 00219 resetRW (graph_t * g) 00220 { 00221 node_t* n; 00222 00223 for (n = agfstnode(g); n; n = agnxtnode(g,n)) { 00224 if (ND_other(n).list) { 00225 double tmp = ND_rw(n); 00226 ND_rw(n) = ND_mval(n); 00227 ND_mval(n) = tmp; 00228 } 00229 } 00230 } 00231 00232 /* setEdgeLabelPos: 00233 * Set edge label position information for regular and non-adjacent flat edges. 00234 * Dot has allocated space and position for these labels. This info will be 00235 * used when routing orthogonal edges. 00236 */ 00237 static void 00238 setEdgeLabelPos (graph_t * g) 00239 { 00240 node_t* n; 00241 textlabel_t* l; 00242 00243 /* place regular edge labels */ 00244 for (n = GD_nlist(g); n; n = ND_next(n)) { 00245 if (ND_node_type(n) == VIRTUAL) { 00246 if (ND_alg(n)) { // label of non-adjacent flat edge 00247 edge_t* fe = (edge_t*)ND_alg(n); 00248 assert ((l = ED_label(fe))); 00249 l->pos = ND_coord(n); 00250 l->set = TRUE; 00251 } 00252 else if ((l = ND_label(n))) {// label of regular edge 00253 place_vnlabel(n); 00254 } 00255 if (l) updateBB(g, l); 00256 } 00257 } 00258 } 00259 00260 /* _dot_splines: 00261 * Main spline routing code. 00262 * The normalize parameter allows this function to be called by the 00263 * recursive call in make_flat_edge without normalization occurring, 00264 * so that the edge will only be normalized once in the top level call 00265 * of dot_splines. 00266 */ 00267 static void _dot_splines(graph_t * g, int normalize) 00268 { 00269 int i, j, k, n_nodes, n_edges, ind, cnt; 00270 node_t *n; 00271 #ifndef WITH_CGRAPH 00272 edge_t fwdedgea, fwdedgeb; 00273 #else 00274 Agedgeinfo_t fwdedgeai, fwdedgebi; 00275 Agedgepair_t fwdedgea, fwdedgeb; 00276 #endif 00277 edge_t *e, *e0, *e1, *ea, *eb, *le0, *le1, **edges; 00278 path *P; 00279 spline_info_t sd; 00280 int et = EDGE_TYPE(g); 00281 #ifdef WITH_CGRAPH 00282 fwdedgea.out.base.data = (Agrec_t*)&fwdedgeai; 00283 fwdedgeb.out.base.data = (Agrec_t*)&fwdedgebi; 00284 #endif /* WITH_CGRAPH */ 00285 00286 if (et == ET_NONE) return; 00287 if (et == ET_CURVED) { 00288 resetRW (g); 00289 if (GD_has_labels(g) & EDGE_LABEL) { 00290 agerr (AGWARN, "edge labels with splines=curved not supported in dot - use xlabels\n"); 00291 } 00292 for (n = agfstnode (g); n; n = agnxtnode(g, n)) { 00293 for (e = agfstout(g, n); e; e = agnxtout(g,e)) { 00294 makeStraightEdge(g, e, et, &sinfo); 00295 } 00296 } 00297 goto finish; 00298 } 00299 #ifdef ORTHO 00300 if (et == ET_ORTHO) { 00301 resetRW (g); 00302 if (GD_has_labels(g) & EDGE_LABEL) { 00303 setEdgeLabelPos (g); 00304 orthoEdges (g, 1); 00305 } 00306 else 00307 orthoEdges (g, 0); 00308 goto finish; 00309 } 00310 #endif 00311 00312 mark_lowclusters(g); 00313 if (routesplinesinit()) return; 00314 P = NEW(path); 00315 /* FlatHeight = 2 * GD_nodesep(g); */ 00316 sd.Splinesep = GD_nodesep(g) / 4; 00317 sd.Multisep = GD_nodesep(g); 00318 edges = N_NEW(CHUNK, edge_t *); 00319 00320 /* compute boundaries and list of splines */ 00321 sd.LeftBound = sd.RightBound = 0; 00322 n_edges = n_nodes = 0; 00323 for (i = GD_minrank(g); i <= GD_maxrank(g); i++) { 00324 n_nodes += GD_rank(g)[i].n; 00325 if ((n = GD_rank(g)[i].v[0])) 00326 sd.LeftBound = MIN(sd.LeftBound, (ND_coord(n).x - ND_lw(n))); 00327 if (GD_rank(g)[i].n && (n = GD_rank(g)[i].v[GD_rank(g)[i].n - 1])) 00328 sd.RightBound = MAX(sd.RightBound, (ND_coord(n).x + ND_rw(n))); 00329 sd.LeftBound -= MINW; 00330 sd.RightBound += MINW; 00331 00332 for (j = 0; j < GD_rank(g)[i].n; j++) { 00333 n = GD_rank(g)[i].v[j]; 00334 /* if n is the label of a flat edge, copy its position to 00335 * the label. 00336 */ 00337 if (ND_alg(n)) { 00338 edge_t* fe = (edge_t*)ND_alg(n); 00339 assert (ED_label(fe)); 00340 ED_label(fe)->pos = ND_coord(n); 00341 ED_label(fe)->set = TRUE; 00342 } 00343 if ((ND_node_type(n) != NORMAL) && 00344 (sinfo.splineMerge(n) == FALSE)) 00345 continue; 00346 for (k = 0; (e = ND_out(n).list[k]); k++) { 00347 if ((ED_edge_type(e) == FLATORDER) 00348 || (ED_edge_type(e) == IGNORED)) 00349 continue; 00350 setflags(e, REGULAREDGE, FWDEDGE, MAINGRAPH); 00351 edges[n_edges++] = e; 00352 if (n_edges % CHUNK == 0) 00353 GROWEDGES; 00354 } 00355 if (ND_flat_out(n).list) 00356 for (k = 0; (e = ND_flat_out(n).list[k]); k++) { 00357 setflags(e, FLATEDGE, 0, AUXGRAPH); 00358 edges[n_edges++] = e; 00359 if (n_edges % CHUNK == 0) 00360 GROWEDGES; 00361 } 00362 if (ND_other(n).list) { 00363 /* In position, each node has its rw stored in mval and, 00364 * if a node is part of a loop, rw may be increased to 00365 * reflect the loops and associated labels. We restore 00366 * the original value here. 00367 */ 00368 if (ND_node_type(n) == NORMAL) { 00369 double tmp = ND_rw(n); 00370 ND_rw(n) = ND_mval(n); 00371 ND_mval(n) = tmp; 00372 } 00373 for (k = 0; (e = ND_other(n).list[k]); k++) { 00374 setflags(e, 0, 0, AUXGRAPH); 00375 edges[n_edges++] = e; 00376 if (n_edges % CHUNK == 0) 00377 GROWEDGES; 00378 } 00379 } 00380 } 00381 } 00382 00383 /* Sort so that equivalent edges are contiguous. 00384 * Equivalence should basically mean that 2 edges have the 00385 * same set {(tailnode,tailport),(headnode,headport)}, or 00386 * alternatively, the edges would be routed identically if 00387 * routed separately. 00388 */ 00389 qsort((char *) &edges[0], n_edges, sizeof(edges[0]), 00390 (qsort_cmpf) edgecmp); 00391 00392 /* FIXME: just how many boxes can there be? */ 00393 P->boxes = N_NEW(n_nodes + 20 * 2 * NSUB, boxf); 00394 sd.Rank_box = N_NEW(i, boxf); 00395 00396 if (et == ET_LINE) { 00397 /* place regular edge labels */ 00398 for (n = GD_nlist(g); n; n = ND_next(n)) { 00399 if ((ND_node_type(n) == VIRTUAL) && (ND_label(n))) { 00400 place_vnlabel(n); 00401 } 00402 } 00403 } 00404 00405 for (i = 0; i < n_edges;) { 00406 ind = i; 00407 le0 = getmainedge((e0 = edges[i++])); 00408 ea = (ED_tail_port(e0).defined 00409 || ED_head_port(e0).defined) ? e0 : le0; 00410 if (ED_tree_index(ea) & BWDEDGE) { 00411 #ifndef WITH_CGRAPH 00412 MAKEFWDEDGE(&fwdedgea, ea); 00413 ea = &fwdedgea; 00414 #else 00415 MAKEFWDEDGE(&fwdedgea.out, ea); 00416 ea = &fwdedgea.out; 00417 #endif 00418 } 00419 for (cnt = 1; i < n_edges; cnt++, i++) { 00420 if (le0 != (le1 = getmainedge((e1 = edges[i])))) 00421 break; 00422 if (ED_adjacent(e0)) continue; /* all flat adjacent edges at once */ 00423 eb = (ED_tail_port(e1).defined 00424 || ED_head_port(e1).defined) ? e1 : le1; 00425 if (ED_tree_index(eb) & BWDEDGE) { 00426 #ifndef WITH_CGRAPH 00427 MAKEFWDEDGE(&fwdedgeb, eb); 00428 eb = &fwdedgeb; 00429 #else 00430 MAKEFWDEDGE(&fwdedgeb.out, eb); 00431 eb = &fwdedgeb.out; 00432 #endif 00433 } 00434 if (portcmp(ED_tail_port(ea), ED_tail_port(eb))) 00435 break; 00436 if (portcmp(ED_head_port(ea), ED_head_port(eb))) 00437 break; 00438 if ((ED_tree_index(e0) & EDGETYPEMASK) == FLATEDGE 00439 && ED_label(e0) != ED_label(e1)) 00440 break; 00441 if (ED_tree_index(edges[i]) & MAINGRAPH) /* Aha! -C is on */ 00442 break; 00443 } 00444 00445 if (agtail(e0) == aghead(e0)) { 00446 int b, sizey, r; 00447 n = agtail(e0); 00448 r = ND_rank(n); 00449 if (r == GD_maxrank(g)) { 00450 if (r > 0) 00451 sizey = ND_coord(GD_rank(g)[r-1].v[0]).y - ND_coord(n).y; 00452 else 00453 sizey = ND_ht(n); 00454 } 00455 else if (r == GD_minrank(g)) { 00456 sizey = ND_coord(n).y - ND_coord(GD_rank(g)[r+1].v[0]).y; 00457 } 00458 else { 00459 int upy = ND_coord(GD_rank(g)[r-1].v[0]).y - ND_coord(n).y; 00460 int dwny = ND_coord(n).y - ND_coord(GD_rank(g)[r+1].v[0]).y; 00461 sizey = MIN(upy, dwny); 00462 } 00463 makeSelfEdge(P, edges, ind, cnt, sd.Multisep, sizey/2, &sinfo); 00464 for (b = 0; b < cnt; b++) { 00465 e = edges[ind+b]; 00466 if (ED_label(e)) 00467 updateBB(g, ED_label(e)); 00468 } 00469 } 00470 else if (ND_rank(agtail(e0)) == ND_rank(aghead(e0))) { 00471 make_flat_edge(&sd, P, edges, ind, cnt, et); 00472 } 00473 else 00474 make_regular_edge(&sd, P, edges, ind, cnt, et); 00475 } 00476 00477 /* place regular edge labels */ 00478 for (n = GD_nlist(g); n; n = ND_next(n)) { 00479 if ((ND_node_type(n) == VIRTUAL) && (ND_label(n))) { 00480 place_vnlabel(n); 00481 updateBB(g, ND_label(n)); 00482 } 00483 } 00484 00485 /* normalize splines so they always go from tail to head */ 00486 /* place_portlabel relies on this being done first */ 00487 if (normalize) 00488 edge_normalize(g); 00489 00490 finish : 00491 /* vladimir: place port labels */ 00492 /* FIX: head and tail labels are not part of cluster bbox */ 00493 if ((E_headlabel || E_taillabel) && (E_labelangle || E_labeldistance)) { 00494 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00495 if (E_headlabel) { 00496 for (e = agfstin(g, n); e; e = agnxtin(g, e)) 00497 #ifndef WITH_CGRAPH 00498 if (ED_head_label(e)) { 00499 if (place_portlabel(e, TRUE)) 00500 updateBB(g, ED_head_label(e)); 00501 } 00502 #else 00503 if (ED_head_label(AGMKOUT(e))) { 00504 place_portlabel(AGMKOUT(e), TRUE); 00505 updateBB(g, ED_head_label(AGMKOUT(e))); 00506 } 00507 #endif 00508 00509 } 00510 if (E_taillabel) { 00511 for (e = agfstout(g, n); e; e = agnxtout(g, e)) { 00512 if (ED_tail_label(e)) { 00513 if (place_portlabel(e, FALSE)) 00514 updateBB(g, ED_tail_label(e)); 00515 } 00516 } 00517 } 00518 } 00519 } 00520 /* end vladimir */ 00521 00522 #ifdef ORTHO 00523 if ((et != ET_ORTHO) && (et != ET_CURVED)) { 00524 #else 00525 if (et != ET_CURVED) { 00526 #endif 00527 free(edges); 00528 free(P->boxes); 00529 free(P); 00530 free(sd.Rank_box); 00531 routesplinesterm(); 00532 } 00533 State = GVSPLINES; 00534 EdgeLabelsDone = 1; 00535 } 00536 00537 /* dot_splines: 00538 * If the splines attribute is defined but equal to "", skip edge routing. 00539 */ 00540 void dot_splines(graph_t * g) 00541 { 00542 _dot_splines (g, 1); 00543 } 00544 00545 /* place_vnlabel: 00546 * assign position of an edge label from its virtual node 00547 * This is for regular edges only. 00548 */ 00549 static void 00550 place_vnlabel(node_t * n) 00551 { 00552 pointf dimen; 00553 double width; 00554 edge_t *e; 00555 if (ND_in(n).size == 0) 00556 return; /* skip flat edge labels here */ 00557 for (e = ND_out(n).list[0]; ED_edge_type(e) != NORMAL; 00558 e = ED_to_orig(e)); 00559 dimen = ED_label(e)->dimen; 00560 width = GD_flip(agraphof(n)) ? dimen.y : dimen.x; 00561 ED_label(e)->pos.x = ND_coord(n).x + width / 2.0; 00562 ED_label(e)->pos.y = ND_coord(n).y; 00563 ED_label(e)->set = TRUE; 00564 } 00565 00566 static void 00567 setflags(edge_t *e, int hint1, int hint2, int f3) 00568 { 00569 int f1, f2; 00570 if (hint1 != 0) 00571 f1 = hint1; 00572 else { 00573 if (agtail(e) == aghead(e)) 00574 if (ED_tail_port(e).defined || ED_head_port(e).defined) 00575 f1 = SELFWPEDGE; 00576 else 00577 f1 = SELFNPEDGE; 00578 else if (ND_rank(agtail(e)) == ND_rank(aghead(e))) 00579 f1 = FLATEDGE; 00580 else 00581 f1 = REGULAREDGE; 00582 } 00583 if (hint2 != 0) 00584 f2 = hint2; 00585 else { 00586 if (f1 == REGULAREDGE) 00587 f2 = (ND_rank(agtail(e)) < ND_rank(aghead(e))) ? FWDEDGE : BWDEDGE; 00588 else if (f1 == FLATEDGE) 00589 f2 = (ND_order(agtail(e)) < ND_order(aghead(e))) ? FWDEDGE : BWDEDGE; 00590 else /* f1 == SELF*EDGE */ 00591 f2 = FWDEDGE; 00592 } 00593 ED_tree_index(e) = (f1 | f2 | f3); 00594 } 00595 00596 /* edgecmp: 00597 * lexicographically order edges by 00598 * - edge type 00599 * - |rank difference of nodes| 00600 * - |x difference of nodes| 00601 * - id of witness edge for equivalence class 00602 * - port comparison 00603 * - graph type 00604 * - labels if flat edges 00605 * - edge id 00606 */ 00607 static int edgecmp(edge_t** ptr0, edge_t** ptr1) 00608 { 00609 #ifndef WITH_CGRAPH 00610 edge_t fwdedgea, fwdedgeb, *e0, *e1, *ea, *eb, *le0, *le1; 00611 #else 00612 Agedgeinfo_t fwdedgeai, fwdedgebi; 00613 Agedgepair_t fwdedgea, fwdedgeb; 00614 edge_t *e0, *e1, *ea, *eb, *le0, *le1; 00615 #endif 00616 int et0, et1, v0, v1, rv; 00617 double t0, t1; 00618 00619 #ifdef WITH_CGRAPH 00620 fwdedgea.out.base.data = (Agrec_t*)&fwdedgeai; 00621 fwdedgeb.out.base.data = (Agrec_t*)&fwdedgebi; 00622 #endif 00623 e0 = (edge_t *) * ptr0; 00624 e1 = (edge_t *) * ptr1; 00625 et0 = ED_tree_index(e0) & EDGETYPEMASK; 00626 et1 = ED_tree_index(e1) & EDGETYPEMASK; 00627 if (et0 != et1) 00628 return (et1 - et0); 00629 00630 le0 = getmainedge(e0); 00631 le1 = getmainedge(e1); 00632 00633 t0 = ND_rank(agtail(le0)) - ND_rank(aghead(le0)); 00634 t1 = ND_rank(agtail(le1)) - ND_rank(aghead(le1)); 00635 v0 = ABS((int)t0); /* ugly, but explicit as to how we avoid equality tests on fp numbers */ 00636 v1 = ABS((int)t1); 00637 if (v0 != v1) 00638 return (v0 - v1); 00639 00640 t0 = ND_coord(agtail(le0)).x - ND_coord(aghead(le0)).x; 00641 t1 = ND_coord(agtail(le1)).x - ND_coord(aghead(le1)).x; 00642 v0 = ABS((int)t0); 00643 v1 = ABS((int)t1); 00644 if (v0 != v1) 00645 return (v0 - v1); 00646 00647 /* This provides a cheap test for edges having the same set of endpoints. */ 00648 if (AGSEQ(le0) != AGSEQ(le1)) 00649 return (AGSEQ(le0) - AGSEQ(le1)); 00650 00651 ea = (ED_tail_port(e0).defined || ED_head_port(e0).defined) ? e0 : le0; 00652 if (ED_tree_index(ea) & BWDEDGE) { 00653 #ifndef WITH_CGRAPH 00654 MAKEFWDEDGE(&fwdedgea, ea); 00655 ea = &fwdedgea; 00656 #else 00657 MAKEFWDEDGE(&fwdedgea.out, ea); 00658 ea = &fwdedgea.out; 00659 #endif 00660 } 00661 eb = (ED_tail_port(e1).defined || ED_head_port(e1).defined) ? e1 : le1; 00662 if (ED_tree_index(eb) & BWDEDGE) { 00663 #ifndef WITH_CGRAPH 00664 MAKEFWDEDGE(&fwdedgeb, eb); 00665 eb = &fwdedgeb; 00666 #else 00667 MAKEFWDEDGE(&fwdedgeb.out, eb); 00668 eb = &fwdedgeb.out; 00669 #endif 00670 } 00671 if ((rv = portcmp(ED_tail_port(ea), ED_tail_port(eb)))) 00672 return rv; 00673 if ((rv = portcmp(ED_head_port(ea), ED_head_port(eb)))) 00674 return rv; 00675 00676 et0 = ED_tree_index(e0) & GRAPHTYPEMASK; 00677 et1 = ED_tree_index(e1) & GRAPHTYPEMASK; 00678 if (et0 != et1) 00679 return (et0 - et1); 00680 00681 if (et0 == FLATEDGE && ED_label(e0) != ED_label(e1)) 00682 return (int) (ED_label(e0) - ED_label(e1)); 00683 00684 return (AGSEQ(e0) - AGSEQ(e1)); 00685 } 00686 00687 /* cloneGraph: 00688 */ 00689 static struct { 00690 attrsym_t* E_constr; 00691 attrsym_t* E_samehead; 00692 attrsym_t* E_sametail; 00693 attrsym_t* E_weight; 00694 attrsym_t* E_minlen; 00695 attrsym_t* N_group; 00696 int State; 00697 } attr_state; 00698 00699 /* cloneGraph: 00700 * Create clone graph. It stores the global Agsyms, to be 00701 * restored in cleanupCloneGraph. The graph uses the main 00702 * graph's settings for certain geometry parameters, and 00703 * declares all node and edge attributes used in the original 00704 * graph. 00705 */ 00706 static graph_t* 00707 cloneGraph (graph_t* g) 00708 { 00709 Agsym_t* sym; 00710 graph_t* auxg; 00711 #ifndef WITH_CGRAPH 00712 Agsym_t **list; 00713 00714 auxg = agopen ("auxg", AG_IS_DIRECTED(g)?AGDIGRAPH:AGRAPH); 00715 agraphattr(auxg, "rank", ""); 00716 #else /* WITH_CGRAPH */ 00717 if (agisdirected(g)) 00718 auxg = agopen ("auxg",Agdirected, NIL(Agdisc_t *)); 00719 else 00720 auxg = agopen ("auxg",Agundirected, NIL(Agdisc_t *)); 00721 agbindrec(auxg, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE); 00722 agattr(auxg, AGRAPH, "rank", ""); 00723 #endif /* WITH_CGRAPH */ 00724 GD_drawing(auxg) = NEW(layout_t); 00725 GD_drawing(auxg)->quantum = GD_drawing(g)->quantum; 00726 GD_drawing(auxg)->dpi = GD_drawing(g)->dpi; 00727 00728 GD_charset(auxg) = GD_charset (g); 00729 if (GD_flip(g)) 00730 SET_RANKDIR(auxg, RANKDIR_TB); 00731 else 00732 SET_RANKDIR(auxg, RANKDIR_LR); 00733 GD_nodesep(auxg) = GD_nodesep(g); 00734 GD_ranksep(auxg) = GD_ranksep(g); 00735 00736 #ifndef WITH_CGRAPH 00737 list = g->root->univ->nodeattr->list; 00738 while ((sym = *list++)) { 00739 agnodeattr (auxg, sym->name, sym->value); 00740 #else /* WITH_CGRAPH */ 00741 //copy node attrs to auxg 00742 // list = g->root->univ->nodeattr->list; 00743 sym=agnxtattr(agroot(g),AGNODE,NULL); //get the first attr. 00744 while ((sym = agnxtattr(agroot(g),AGNODE,sym))) { 00745 agattr (auxg, AGNODE,sym->name, sym->defval ); 00746 #endif /* WITH_CGRAPH */ 00747 } 00748 00749 #ifndef WITH_CGRAPH 00750 list = g->root->univ->edgeattr->list; 00751 while ((sym = *list++)) { 00752 agedgeattr (auxg, sym->name, sym->value); 00753 #else /* WITH_CGRAPH */ 00754 //copy edge attributes 00755 sym=agnxtattr(agroot(g),AGEDGE,NULL); //get the first attr. 00756 while ((sym = agnxtattr(agroot(g),AGEDGE,sym))) { 00757 agattr (auxg, AGEDGE,sym->name, sym->defval); 00758 #endif /* WITH_CGRAPH */ 00759 } 00760 00761 #ifndef WITH_CGRAPH 00762 if (!agfindattr(auxg->proto->e, "headport")) 00763 agedgeattr (auxg, "headport", ""); 00764 if (!agfindattr(auxg->proto->e, "tailport")) 00765 agedgeattr (auxg, "tailport", ""); 00766 #else /* WITH_CGRAPH */ 00767 if (!agattr(auxg,AGEDGE, "headport", NULL)) 00768 agattr(auxg,AGEDGE, "headport", ""); 00769 if (!agattr(auxg,AGEDGE, "tailport", NULL)) 00770 agattr(auxg,AGEDGE, "tailport", ""); 00771 #endif /* WITH_CGRAPH */ 00772 00773 attr_state.E_constr = E_constr; 00774 attr_state.E_samehead = E_samehead; 00775 attr_state.E_sametail = E_sametail; 00776 attr_state.E_weight = E_weight; 00777 attr_state.E_minlen = E_minlen; 00778 attr_state.N_group = N_group; 00779 attr_state.State = State; 00780 E_constr = NULL; 00781 #ifndef WITH_CGRAPH 00782 E_samehead = agfindattr(auxg->proto->e, "samehead"); 00783 E_sametail = agfindattr(auxg->proto->e, "sametail"); 00784 E_weight = agfindattr(auxg->proto->e, "weight"); 00785 #else /* WITH_CGRAPH */ 00786 E_samehead = agattr(auxg,AGEDGE, "samehead", NULL); 00787 E_sametail = agattr(auxg,AGEDGE, "sametail", NULL); 00788 E_weight = agattr(auxg,AGEDGE, "weight", NULL); 00789 #endif /* WITH_CGRAPH */ 00790 if (!E_weight) 00791 #ifndef WITH_CGRAPH 00792 E_weight = agedgeattr (auxg, "weight", ""); 00793 #else /* WITH_CGRAPH */ 00794 E_weight = agattr (auxg,AGEDGE,"weight", ""); 00795 #endif /* WITH_CGRAPH */ 00796 E_minlen = NULL; 00797 N_group = NULL; 00798 00799 return auxg; 00800 } 00801 00802 /* cleanupCloneGraph: 00803 */ 00804 static void 00805 cleanupCloneGraph (graph_t* g) 00806 { 00807 /* restore main graph syms */ 00808 E_constr = attr_state.E_constr; 00809 E_samehead = attr_state.E_samehead; 00810 E_sametail = attr_state.E_sametail; 00811 E_weight = attr_state.E_weight; 00812 E_minlen = attr_state.E_minlen; 00813 N_group = attr_state.N_group; 00814 State = attr_state.State; 00815 00816 dot_cleanup(g); 00817 agclose(g); 00818 } 00819 00820 /* cloneNode: 00821 * If flipped is true, original graph has rankdir=LR or RL. 00822 * In this case, records change shape, so we wrap a record node's 00823 * label in "{...}" to prevent this. 00824 */ 00825 static node_t* 00826 cloneNode (graph_t* g, node_t* orign, int flipped) 00827 { 00828 #ifndef WITH_CGRAPH 00829 node_t* n = agnode(g, orign->name); 00830 #else /* WITH_CGRAPH */ 00831 node_t* n = agnode(g, agnameof(orign),1); 00832 agbindrec(n, "Agnodeinfo_t", sizeof(Agnodeinfo_t), TRUE); 00833 #endif /* WITH_CGRAPH */ 00834 agcopyattr (orign, n); 00835 if (shapeOf(orign) == SH_RECORD) { 00836 int lbllen = strlen(ND_label(orign)->text); 00837 char* buf = N_GNEW(lbllen+3,char); 00838 sprintf (buf, "{%s}", ND_label(orign)->text); 00839 agset (n, "label", buf); 00840 } 00841 00842 return n; 00843 } 00844 00845 /* cloneEdge: 00846 */ 00847 static edge_t* 00848 cloneEdge (graph_t* g, node_t* tn, node_t* hn, edge_t* orig) 00849 { 00850 #ifndef WITH_CGRAPH 00851 edge_t* e = agedge(g, tn, hn); 00852 #else /* WITH_CGRAPH */ 00853 edge_t* e = agedge(g, tn, hn,NULL,1); 00854 #endif /* WITH_CGRAPH */ 00855 /* for (; ED_edge_type(orig) != NORMAL; orig = ED_to_orig(orig)); */ 00856 agcopyattr (orig, e); 00857 /* 00858 if (orig->tail != ND_alg(tn)) { 00859 char* hdport = agget (orig, HEAD_ID); 00860 char* tlport = agget (orig, TAIL_ID); 00861 agset (e, TAIL_ID, (hdport ? hdport : "")); 00862 agset (e, HEAD_ID, (tlport ? tlport : "")); 00863 } 00864 */ 00865 00866 return e; 00867 } 00868 00869 /* transformf: 00870 * Rotate, if necessary, then translate points. 00871 */ 00872 static pointf 00873 transformf (pointf p, pointf del, int flip) 00874 { 00875 if (flip) { 00876 double i = p.x; 00877 p.x = p.y; 00878 p.y = -i; 00879 } 00880 return add_pointf(p, del); 00881 } 00882 00883 /* edgelblcmpfn: 00884 * lexicographically order edges by 00885 * - has label 00886 * - label is wider 00887 * - label is higher 00888 */ 00889 static int edgelblcmpfn(edge_t** ptr0, edge_t** ptr1) 00890 { 00891 edge_t *e0, *e1; 00892 pointf sz0, sz1; 00893 00894 e0 = (edge_t *) * ptr0; 00895 e1 = (edge_t *) * ptr1; 00896 00897 if (ED_label(e0)) { 00898 if (ED_label(e1)) { 00899 sz0 = ED_label(e0)->dimen; 00900 sz1 = ED_label(e1)->dimen; 00901 if (sz0.x > sz1.x) return -1; 00902 else if (sz0.x < sz1.x) return 1; 00903 else if (sz0.y > sz1.y) return -1; 00904 else if (sz0.y < sz1.y) return 1; 00905 else return 0; 00906 } 00907 else 00908 return -1; 00909 } 00910 else if (ED_label(e1)) { 00911 return 1; 00912 } 00913 else 00914 return 0; 00915 } 00916 00917 #define LBL_SPACE 6 /* space between labels, in points */ 00918 00919 /* makeSimpleFlatLabels: 00920 * This handles the second simplest case for flat edges between 00921 * two adjacent nodes. We still invoke a dot on a rotated problem 00922 * to handle edges with ports. This usually works, but fails for 00923 * records because of their weird nature. 00924 */ 00925 static void 00926 makeSimpleFlatLabels (node_t* tn, node_t* hn, edge_t** edges, int ind, int cnt, int et, int n_lbls) 00927 { 00928 pointf *ps; 00929 Ppoly_t poly; 00930 int pn; 00931 edge_t* e = edges[ind]; 00932 pointf points[10], tp, hp; 00933 int i, pointn; 00934 double leftend, rightend, ctrx, ctry, miny, maxy; 00935 double uminx, umaxx; 00936 double lminx, lmaxx; 00937 00938 edge_t** earray = N_NEW(cnt, edge_t*); 00939 00940 for (i = 0; i < cnt; i++) { 00941 earray[i] = edges[ind + i]; 00942 } 00943 00944 qsort (earray, cnt, sizeof(edge_t*), (qsort_cmpf) edgelblcmpfn); 00945 00946 tp = add_pointf(ND_coord(tn), ED_tail_port(e).p); 00947 hp = add_pointf(ND_coord(hn), ED_head_port(e).p); 00948 00949 leftend = tp.x+ND_rw(tn); 00950 rightend = hp.x-ND_lw(hn); 00951 ctrx = (leftend + rightend)/2.0; 00952 00953 /* do first edge */ 00954 e = earray[0]; 00955 pointn = 0; 00956 points[pointn++] = tp; 00957 points[pointn++] = tp; 00958 points[pointn++] = hp; 00959 points[pointn++] = hp; 00960 #ifndef WITH_CGRAPH 00961 clip_and_install(e, e->head, points, pointn, &sinfo); 00962 #else /* WITH_CGRAPH */ 00963 clip_and_install(e, aghead(e), points, pointn, &sinfo); 00964 #endif /* WITH_CGRAPH */ 00965 ED_label(e)->pos.x = ctrx; 00966 ED_label(e)->pos.y = tp.y + (ED_label(e)->dimen.y+LBL_SPACE)/2.0; 00967 ED_label(e)->set = TRUE; 00968 00969 miny = tp.y + LBL_SPACE/2.0; 00970 maxy = miny + ED_label(e)->dimen.y; 00971 uminx = ctrx - (ED_label(e)->dimen.x)/2.0; 00972 umaxx = ctrx + (ED_label(e)->dimen.x)/2.0; 00973 00974 for (i = 1; i < n_lbls; i++) { 00975 e = earray[i]; 00976 if (i%2) { /* down */ 00977 if (i == 1) { 00978 lminx = ctrx - (ED_label(e)->dimen.x)/2.0; 00979 lmaxx = ctrx + (ED_label(e)->dimen.x)/2.0; 00980 } 00981 miny -= LBL_SPACE + ED_label(e)->dimen.y; 00982 points[0] = tp; 00983 points[1].x = tp.x; 00984 points[1].y = miny - LBL_SPACE; 00985 points[2].x = hp.x; 00986 points[2].y = points[1].y; 00987 points[3] = hp; 00988 points[4].x = lmaxx; 00989 points[4].y = hp.y; 00990 points[5].x = lmaxx; 00991 points[5].y = miny; 00992 points[6].x = lminx; 00993 points[6].y = miny; 00994 points[7].x = lminx; 00995 points[7].y = tp.y; 00996 ctry = miny + (ED_label(e)->dimen.y)/2.0; 00997 } 00998 else { /* up */ 00999 points[0] = tp; 01000 points[1].x = uminx; 01001 points[1].y = tp.y; 01002 points[2].x = uminx; 01003 points[2].y = maxy; 01004 points[3].x = umaxx; 01005 points[3].y = maxy; 01006 points[4].x = umaxx; 01007 points[4].y = hp.y; 01008 points[5].x = hp.x; 01009 points[5].y = hp.y; 01010 points[6].x = hp.x; 01011 points[6].y = maxy + LBL_SPACE; 01012 points[7].x = tp.x; 01013 points[7].y = maxy + LBL_SPACE; 01014 ctry = maxy + (ED_label(e)->dimen.y)/2.0 + LBL_SPACE; 01015 maxy += ED_label(e)->dimen.y + LBL_SPACE; 01016 } 01017 poly.pn = 8; 01018 poly.ps = (Ppoint_t*)points; 01019 ps = simpleSplineRoute (tp, hp, poly, &pn, et == ET_PLINE); 01020 if (pn == 0) return; 01021 ED_label(e)->pos.x = ctrx; 01022 ED_label(e)->pos.y = ctry; 01023 ED_label(e)->set = TRUE; 01024 #ifndef WITH_CGRAPH 01025 clip_and_install(e, e->head, ps, pn, &sinfo); 01026 #else /* WITH_CGRAPH */ 01027 clip_and_install(e, aghead(e), ps, pn, &sinfo); 01028 #endif /* WITH_CGRAPH */ 01029 } 01030 01031 /* edges with no labels */ 01032 for (; i < cnt; i++) { 01033 e = earray[i]; 01034 if (i%2) { /* down */ 01035 if (i == 1) { 01036 lminx = (2*leftend + rightend)/3.0; 01037 lmaxx = (leftend + 2*rightend)/3.0; 01038 } 01039 miny -= LBL_SPACE; 01040 points[0] = tp; 01041 points[1].x = tp.x; 01042 points[1].y = miny - LBL_SPACE; 01043 points[2].x = hp.x; 01044 points[2].y = points[1].y; 01045 points[3] = hp; 01046 points[4].x = lmaxx; 01047 points[4].y = hp.y; 01048 points[5].x = lmaxx; 01049 points[5].y = miny; 01050 points[6].x = lminx; 01051 points[6].y = miny; 01052 points[7].x = lminx; 01053 points[7].y = tp.y; 01054 } 01055 else { /* up */ 01056 points[0] = tp; 01057 points[1].x = uminx; 01058 points[1].y = tp.y; 01059 points[2].x = uminx; 01060 points[2].y = maxy; 01061 points[3].x = umaxx; 01062 points[3].y = maxy; 01063 points[4].x = umaxx; 01064 points[4].y = hp.y; 01065 points[5].x = hp.x; 01066 points[5].y = hp.y; 01067 points[6].x = hp.x; 01068 points[6].y = maxy + LBL_SPACE; 01069 points[7].x = tp.x; 01070 points[7].y = maxy + LBL_SPACE; 01071 maxy += + LBL_SPACE; 01072 } 01073 poly.pn = 8; 01074 poly.ps = (Ppoint_t*)points; 01075 ps = simpleSplineRoute (tp, hp, poly, &pn, et == ET_PLINE); 01076 if (pn == 0) return; 01077 #ifndef WITH_CGRAPH 01078 clip_and_install(e, e->head, ps, pn, &sinfo); 01079 #else /* WITH_CGRAPH */ 01080 clip_and_install(e, aghead(e), ps, pn, &sinfo); 01081 #endif /* WITH_CGRAPH */ 01082 } 01083 01084 free (earray); 01085 } 01086 01087 /* makeSimpleFlat: 01088 */ 01089 static void 01090 makeSimpleFlat (node_t* tn, node_t* hn, edge_t** edges, int ind, int cnt, int et) 01091 { 01092 edge_t* e = edges[ind]; 01093 pointf points[10], tp, hp; 01094 int i, pointn; 01095 double stepy, dy; 01096 01097 tp = add_pointf(ND_coord(tn), ED_tail_port(e).p); 01098 hp = add_pointf(ND_coord(hn), ED_head_port(e).p); 01099 01100 stepy = (cnt > 1) ? ND_ht(tn) / (double)(cnt - 1) : 0.; 01101 dy = tp.y - ((cnt > 1) ? ND_ht(tn) / 2. : 0.); 01102 01103 for (i = 0; i < cnt; i++) { 01104 e = edges[ind + i]; 01105 pointn = 0; 01106 if ((et == ET_SPLINE) || (et == ET_LINE)) { 01107 points[pointn++] = tp; 01108 points[pointn++] = pointfof((2 * tp.x + hp.x) / 3, dy); 01109 points[pointn++] = pointfof((2 * hp.x + tp.x) / 3, dy); 01110 points[pointn++] = hp; 01111 } 01112 else { /* ET_PLINE */ 01113 points[pointn++] = tp; 01114 points[pointn++] = tp; 01115 points[pointn++] = pointfof((2 * tp.x + hp.x) / 3, dy); 01116 points[pointn++] = pointfof((2 * tp.x + hp.x) / 3, dy); 01117 points[pointn++] = pointfof((2 * tp.x + hp.x) / 3, dy); 01118 points[pointn++] = pointfof((2 * hp.x + tp.x) / 3, dy); 01119 points[pointn++] = pointfof((2 * hp.x + tp.x) / 3, dy); 01120 points[pointn++] = pointfof((2 * hp.x + tp.x) / 3, dy); 01121 points[pointn++] = hp; 01122 points[pointn++] = hp; 01123 } 01124 dy += stepy; 01125 #ifndef WITH_CGRAPH 01126 clip_and_install(e, e->head, points, pointn, &sinfo); 01127 #else /* WITH_CGRAPH */ 01128 clip_and_install(e, aghead(e), points, pointn, &sinfo); 01129 #endif /* WITH_CGRAPH */ 01130 } 01131 } 01132 01133 /* make_flat_adj_edges: 01134 * In the simple case, with no labels or ports, this creates a simple 01135 * spindle of splines. 01136 * If there are only labels, cobble something together. 01137 * Otherwise, we run dot recursively on the 2 nodes and the edges, 01138 * essentially using rankdir=LR, to get the needed spline info. 01139 */ 01140 static void 01141 make_flat_adj_edges(path* P, edge_t** edges, int ind, int cnt, edge_t* e0, 01142 int et) 01143 { 01144 node_t* n; 01145 node_t *tn, *hn; 01146 edge_t* e; 01147 int labels = 0, ports = 0; 01148 graph_t* g; 01149 graph_t* auxg; 01150 graph_t* subg; 01151 node_t *auxt, *auxh; 01152 edge_t* auxe; 01153 int i, j, midx, midy, leftx, rightx; 01154 pointf del; 01155 edge_t* hvye = NULL; 01156 01157 g = agraphof(agtail(e0)); 01158 tn = agtail(e0), hn = aghead(e0); 01159 for (i = 0; i < cnt; i++) { 01160 e = edges[ind + i]; 01161 if (ED_label(e)) labels++; 01162 if (ED_tail_port(e).defined || ED_head_port(e).defined) ports = 1; 01163 } 01164 01165 if (ports == 0) { 01166 /* flat edges without ports and labels can go straight left to right */ 01167 if (labels == 0) { 01168 makeSimpleFlat (tn, hn, edges, ind, cnt, et); 01169 } 01170 /* flat edges without ports but with labels take more work */ 01171 else { 01172 makeSimpleFlatLabels (tn, hn, edges, ind, cnt, et, labels); 01173 } 01174 return; 01175 } 01176 01177 auxg = cloneGraph (g); 01178 #ifndef WITH_CGRAPH 01179 subg = agsubg (auxg, "xxx"); 01180 #else /* WITH_CGRAPH */ 01181 subg = agsubg (auxg, "xxx",1); 01182 agbindrec(subg, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE); 01183 #endif /* WITH_CGRAPH */ 01184 agset (subg, "rank", "source"); 01185 rightx = ND_coord(hn).x; 01186 leftx = ND_coord(tn).x; 01187 if (GD_flip(g)) { 01188 node_t* n; 01189 n = tn; 01190 tn = hn; 01191 hn = n; 01192 } 01193 auxt = cloneNode(subg, tn, GD_flip(g)); 01194 auxh = cloneNode(auxg, hn, GD_flip(g)); 01195 for (i = 0; i < cnt; i++) { 01196 e = edges[ind + i]; 01197 for (; ED_edge_type(e) != NORMAL; e = ED_to_orig(e)); 01198 if (agtail(e) == tn) 01199 auxe = cloneEdge (auxg, auxt, auxh, e); 01200 else 01201 auxe = cloneEdge (auxg, auxh, auxt, e); 01202 ED_alg(e) = auxe; 01203 if (!hvye && !ED_tail_port(e).defined && !ED_head_port(e).defined) { 01204 hvye = auxe; 01205 ED_alg(hvye) = e; 01206 } 01207 } 01208 if (!hvye) { 01209 #ifndef WITH_CGRAPH 01210 hvye = agedge (auxg, auxt, auxh); 01211 #else /* WITH_CGRAPH */ 01212 hvye = agedge (auxg, auxt, auxh,NULL,1); 01213 #endif /* WITH_CGRAPH */ 01214 } 01215 #ifndef WITH_CGRAPH 01216 agxset (hvye, E_weight->index, "10000"); 01217 #else /* WITH_CGRAPH */ 01218 agxset (hvye, E_weight, "10000"); 01219 #endif /* WITH_CGRAPH */ 01220 GD_gvc(auxg) = GD_gvc(g); 01221 setEdgeType (auxg, et); 01222 dot_init_node_edge(auxg); 01223 01224 dot_rank(auxg, 0); 01225 dot_mincross(auxg, 0); 01226 dot_position(auxg, 0); 01227 01228 /* reposition */ 01229 midx = (ND_coord(tn).x - ND_rw(tn) + ND_coord(hn).x + ND_lw(hn))/2; 01230 midy = (ND_coord(auxt).x + ND_coord(auxh).x)/2; 01231 for (n = GD_nlist(auxg); n; n = ND_next(n)) { 01232 if (n == auxt) { 01233 ND_coord(n).y = rightx; 01234 ND_coord(n).x = midy; 01235 } 01236 else if (n == auxh) { 01237 ND_coord(n).y = leftx; 01238 ND_coord(n).x = midy; 01239 } 01240 else ND_coord(n).y = midx; 01241 } 01242 dot_sameports(auxg); 01243 _dot_splines(auxg, 0); 01244 dotneato_postprocess(auxg); 01245 01246 /* copy splines */ 01247 if (GD_flip(g)) { 01248 del.x = ND_coord(tn).x - ND_coord(auxt).y; 01249 del.y = ND_coord(tn).y + ND_coord(auxt).x; 01250 } 01251 else { 01252 del.x = ND_coord(tn).x - ND_coord(auxt).x; 01253 del.y = ND_coord(tn).y - ND_coord(auxt).y; 01254 } 01255 for (i = 0; i < cnt; i++) { 01256 bezier* auxbz; 01257 bezier* bz; 01258 01259 e = edges[ind + i]; 01260 for (; ED_edge_type(e) != NORMAL; e = ED_to_orig(e)); 01261 auxe = (edge_t*)ED_alg(e); 01262 if ((auxe == hvye) & !ED_alg(auxe)) continue; /* pseudo-edge */ 01263 auxbz = ED_spl(auxe)->list; 01264 bz = new_spline(e, auxbz->size); 01265 bz->sflag = auxbz->sflag; 01266 bz->sp = transformf(auxbz->sp, del, GD_flip(g)); 01267 bz->eflag = auxbz->eflag; 01268 bz->ep = transformf(auxbz->ep, del, GD_flip(g)); 01269 for (j = 0; j < auxbz->size; ) { 01270 pointf cp[4]; 01271 cp[0] = bz->list[j] = transformf(auxbz->list[j], del, GD_flip(g)); 01272 j++; 01273 if ( j >= auxbz->size ) 01274 break; 01275 cp[1] = bz->list[j] = transformf(auxbz->list[j], del, GD_flip(g)); 01276 j++; 01277 cp[2] = bz->list[j] = transformf(auxbz->list[j], del, GD_flip(g)); 01278 j++; 01279 cp[3] = transformf(auxbz->list[j], del, GD_flip(g)); 01280 update_bb_bz(&GD_bb(g), cp); 01281 } 01282 if (ED_label(e)) { 01283 ED_label(e)->pos = transformf(ED_label(auxe)->pos, del, GD_flip(g)); 01284 ED_label(e)->set = TRUE; 01285 updateBB(g, ED_label(e)); 01286 } 01287 } 01288 01289 cleanupCloneGraph (auxg); 01290 } 01291 01292 /* makeFlatEnd; 01293 */ 01294 static void 01295 makeFlatEnd (spline_info_t* sp, path* P, node_t* n, edge_t* e, pathend_t* endp, 01296 boolean isBegin) 01297 { 01298 boxf b; 01299 graph_t* g = agraphof(n); 01300 01301 b = endp->nb = maximal_bbox(sp, n, NULL, e); 01302 endp->sidemask = TOP; 01303 if (isBegin) beginpath(P, e, FLATEDGE, endp, FALSE); 01304 else endpath(P, e, FLATEDGE, endp, FALSE); 01305 b.UR.y = endp->boxes[endp->boxn - 1].UR.y; 01306 b.LL.y = endp->boxes[endp->boxn - 1].LL.y; 01307 b = makeregularend(b, TOP, ND_coord(n).y + GD_rank(g)[ND_rank(n)].ht2); 01308 if (b.LL.x < b.UR.x && b.LL.y < b.UR.y) 01309 endp->boxes[endp->boxn++] = b; 01310 } 01311 /* makeBottomFlatEnd; 01312 */ 01313 static void 01314 makeBottomFlatEnd (spline_info_t* sp, path* P, node_t* n, edge_t* e, 01315 pathend_t* endp, boolean isBegin) 01316 { 01317 boxf b; 01318 graph_t* g = agraphof(n); 01319 01320 b = endp->nb = maximal_bbox(sp, n, NULL, e); 01321 endp->sidemask = BOTTOM; 01322 if (isBegin) beginpath(P, e, FLATEDGE, endp, FALSE); 01323 else endpath(P, e, FLATEDGE, endp, FALSE); 01324 b.UR.y = endp->boxes[endp->boxn - 1].UR.y; 01325 b.LL.y = endp->boxes[endp->boxn - 1].LL.y; 01326 b = makeregularend(b, BOTTOM, ND_coord(n).y - GD_rank(g)[ND_rank(n)].ht2); 01327 if (b.LL.x < b.UR.x && b.LL.y < b.UR.y) 01328 endp->boxes[endp->boxn++] = b; 01329 } 01330 01331 01332 /* make_flat_labeled_edge: 01333 */ 01334 static void 01335 make_flat_labeled_edge(spline_info_t* sp, path* P, edge_t* e, int et) 01336 { 01337 graph_t *g; 01338 node_t *tn, *hn, *ln; 01339 pointf *ps; 01340 pathend_t tend, hend; 01341 boxf lb; 01342 int boxn, i, pn, ydelta; 01343 edge_t *f; 01344 pointf points[7]; 01345 01346 tn = agtail(e); 01347 hn = aghead(e); 01348 g = agraphof(tn); 01349 01350 for (f = ED_to_virt(e); ED_to_virt(f); f = ED_to_virt(f)); 01351 ln = agtail(f); 01352 ED_label(e)->pos = ND_coord(ln); 01353 ED_label(e)->set = TRUE; 01354 01355 if (et == ET_LINE) { 01356 pointf startp, endp, lp; 01357 01358 startp = add_pointf(ND_coord(tn), ED_tail_port(e).p); 01359 endp = add_pointf(ND_coord(hn), ED_head_port(e).p); 01360 01361 lp = ED_label(e)->pos; 01362 lp.y -= (ED_label(e)->dimen.y)/2.0; 01363 points[1] = points[0] = startp; 01364 points[2] = points[3] = points[4] = lp; 01365 points[5] = points[6] = endp; 01366 ps = points; 01367 pn = 7; 01368 } 01369 else { 01370 lb.LL.x = ND_coord(ln).x - ND_lw(ln); 01371 lb.UR.x = ND_coord(ln).x + ND_rw(ln); 01372 lb.UR.y = ND_coord(ln).y + ND_ht(ln)/2; 01373 ydelta = ND_coord(ln).y - GD_rank(g)[ND_rank(tn)].ht1 - 01374 ND_coord(tn).y + GD_rank(g)[ND_rank(tn)].ht2; 01375 ydelta /= 6.; 01376 lb.LL.y = lb.UR.y - MAX(5.,ydelta); 01377 01378 boxn = 0; 01379 makeFlatEnd (sp, P, tn, e, &tend, TRUE); 01380 makeFlatEnd (sp, P, hn, e, &hend, FALSE); 01381 01382 boxes[boxn].LL.x = tend.boxes[tend.boxn - 1].LL.x; 01383 boxes[boxn].LL.y = tend.boxes[tend.boxn - 1].UR.y; 01384 boxes[boxn].UR.x = lb.LL.x; 01385 boxes[boxn].UR.y = lb.LL.y; 01386 boxn++; 01387 boxes[boxn].LL.x = tend.boxes[tend.boxn - 1].LL.x; 01388 boxes[boxn].LL.y = lb.LL.y; 01389 boxes[boxn].UR.x = hend.boxes[hend.boxn - 1].UR.x; 01390 boxes[boxn].UR.y = lb.UR.y; 01391 boxn++; 01392 boxes[boxn].LL.x = lb.UR.x; 01393 boxes[boxn].UR.y = lb.LL.y; 01394 boxes[boxn].LL.y = hend.boxes[hend.boxn - 1].UR.y; 01395 boxes[boxn].UR.x = hend.boxes[hend.boxn - 1].UR.x; 01396 boxn++; 01397 01398 for (i = 0; i < tend.boxn; i++) add_box(P, tend.boxes[i]); 01399 for (i = 0; i < boxn; i++) add_box(P, boxes[i]); 01400 for (i = hend.boxn - 1; i >= 0; i--) add_box(P, hend.boxes[i]); 01401 01402 if (et == ET_SPLINE) ps = routesplines(P, &pn); 01403 else ps = routepolylines(P, &pn); 01404 if (pn == 0) return; 01405 } 01406 #ifndef WITH_CGRAPH 01407 clip_and_install(e, e->head, ps, pn, &sinfo); 01408 #else /* WITH_CGRAPH */ 01409 clip_and_install(e, aghead(e), ps, pn, &sinfo); 01410 #endif /* WITH_CGRAPH */ 01411 } 01412 01413 /* make_flat_bottom_edges: 01414 */ 01415 static void 01416 make_flat_bottom_edges(spline_info_t* sp, path * P, edge_t ** edges, int 01417 ind, int cnt, edge_t* e, int splines) 01418 { 01419 node_t *tn, *hn; 01420 int j, i, r; 01421 double stepx, stepy, vspace; 01422 rank_t* nextr; 01423 int pn; 01424 pointf *ps; 01425 pathend_t tend, hend; 01426 graph_t* g; 01427 01428 tn = agtail(e); 01429 hn = aghead(e); 01430 g = agraphof(tn); 01431 r = ND_rank(tn); 01432 if (r < GD_maxrank(g)) { 01433 nextr = GD_rank(g) + (r+1); 01434 vspace = ND_coord(tn).y - GD_rank(g)[r].pht1 - 01435 (ND_coord(nextr->v[0]).y + nextr->pht2); 01436 } 01437 else { 01438 vspace = GD_ranksep(g); 01439 } 01440 stepx = ((double)(sp->Multisep)) / (cnt+1); 01441 stepy = vspace / (cnt+1); 01442 01443 makeBottomFlatEnd (sp, P, tn, e, &tend, TRUE); 01444 makeBottomFlatEnd (sp, P, hn, e, &hend, FALSE); 01445 01446 for (i = 0; i < cnt; i++) { 01447 int boxn; 01448 boxf b; 01449 e = edges[ind + i]; 01450 boxn = 0; 01451 01452 b = tend.boxes[tend.boxn - 1]; 01453 boxes[boxn].LL.x = b.LL.x; 01454 boxes[boxn].UR.y = b.LL.y; 01455 boxes[boxn].UR.x = b.UR.x + (i + 1) * stepx; 01456 boxes[boxn].LL.y = b.LL.y - (i + 1) * stepy; 01457 boxn++; 01458 boxes[boxn].LL.x = tend.boxes[tend.boxn - 1].LL.x; 01459 boxes[boxn].UR.y = boxes[boxn-1].LL.y; 01460 boxes[boxn].UR.x = hend.boxes[hend.boxn - 1].UR.x; 01461 boxes[boxn].LL.y = boxes[boxn].UR.y - stepy; 01462 boxn++; 01463 b = hend.boxes[hend.boxn - 1]; 01464 boxes[boxn].UR.x = b.UR.x; 01465 boxes[boxn].UR.y = b.LL.y; 01466 boxes[boxn].LL.x = b.LL.x - (i + 1) * stepx; 01467 boxes[boxn].LL.y = boxes[boxn-1].UR.y; 01468 boxn++; 01469 01470 for (j = 0; j < tend.boxn; j++) add_box(P, tend.boxes[j]); 01471 for (j = 0; j < boxn; j++) add_box(P, boxes[j]); 01472 for (j = hend.boxn - 1; j >= 0; j--) add_box(P, hend.boxes[j]); 01473 01474 if (splines) ps = routesplines(P, &pn); 01475 else ps = routepolylines(P, &pn); 01476 if (pn == 0) 01477 return; 01478 clip_and_install(e, aghead(e), ps, pn, &sinfo); 01479 P->nbox = 0; 01480 } 01481 } 01482 01483 /* make_flat_edge: 01484 * Construct flat edges edges[ind...ind+cnt-1] 01485 * There are 4 main cases: 01486 * - all edges between a and b where a and b are adjacent 01487 * - one labeled edge 01488 * - all non-labeled edges with identical ports between non-adjacent a and b 01489 * = connecting bottom to bottom/left/right - route along bottom 01490 * = the rest - route along top 01491 */ 01492 static void 01493 make_flat_edge(spline_info_t* sp, path * P, edge_t ** edges, int ind, int cnt, int et) 01494 { 01495 node_t *tn, *hn; 01496 #ifndef WITH_CGRAPH 01497 edge_t fwdedge, *e; 01498 #else 01499 Agedgeinfo_t fwdedgei; 01500 Agedgepair_t fwdedge; 01501 edge_t *e; 01502 #endif 01503 int j, i, r, isAdjacent; 01504 double stepx, stepy, vspace; 01505 int tside, hside, pn; 01506 pointf *ps; 01507 pathend_t tend, hend; 01508 graph_t* g; 01509 01510 #ifdef WITH_CGRAPH 01511 fwdedge.out.base.data = (Agrec_t*)&fwdedgei; 01512 #endif 01513 01514 /* Get sample edge; normalize to go from left to right */ 01515 e = edges[ind]; 01516 isAdjacent = ED_adjacent(e); 01517 if (ED_tree_index(e) & BWDEDGE) { 01518 #ifndef WITH_CGRAPH 01519 MAKEFWDEDGE(&fwdedge, e); 01520 e = &fwdedge; 01521 #else 01522 MAKEFWDEDGE(&fwdedge.out, e); 01523 e = &fwdedge.out; 01524 #endif 01525 } 01526 for (i = 1; i < cnt; i++) { 01527 if (ED_adjacent(edges[ind+i])) { 01528 isAdjacent = 1; 01529 break; 01530 } 01531 } 01532 /* The lead edge edges[ind] might not have been marked earlier as adjacent, 01533 * so check them all. 01534 */ 01535 if (isAdjacent) { 01536 make_flat_adj_edges (P, edges, ind, cnt, e, et); 01537 return; 01538 } 01539 if (ED_label(e)) { /* edges with labels aren't multi-edges */ 01540 make_flat_labeled_edge (sp, P, e, et); 01541 return; 01542 } 01543 01544 if (et == ET_LINE) { 01545 makeSimpleFlat (agtail(e), aghead(e), edges, ind, cnt, et); 01546 return; 01547 } 01548 01549 tside = ED_tail_port(e).side; 01550 hside = ED_head_port(e).side; 01551 if (((tside == BOTTOM) && (hside != TOP)) || 01552 ((hside == BOTTOM) && (tside != TOP))) { 01553 make_flat_bottom_edges (sp, P, edges, ind, cnt, e, et == ET_SPLINE); 01554 return; 01555 } 01556 01557 tn = agtail(e); 01558 hn = aghead(e); 01559 g = agraphof(tn); 01560 r = ND_rank(tn); 01561 if (r > 0) { 01562 rank_t* prevr; 01563 if (GD_has_labels(g) & EDGE_LABEL) 01564 prevr = GD_rank(g) + (r-2); 01565 else 01566 prevr = GD_rank(g) + (r-1); 01567 vspace = ND_coord(prevr->v[0]).y - prevr->ht1 - ND_coord(tn).y - GD_rank(g)[r].ht2; 01568 } 01569 else { 01570 vspace = GD_ranksep(g); 01571 } 01572 stepx = ((double)sp->Multisep) / (cnt+1); 01573 stepy = vspace / (cnt+1); 01574 01575 makeFlatEnd (sp, P, tn, e, &tend, TRUE); 01576 makeFlatEnd (sp, P, hn, e, &hend, FALSE); 01577 01578 for (i = 0; i < cnt; i++) { 01579 int boxn; 01580 boxf b; 01581 e = edges[ind + i]; 01582 boxn = 0; 01583 01584 b = tend.boxes[tend.boxn - 1]; 01585 boxes[boxn].LL.x = b.LL.x; 01586 boxes[boxn].LL.y = b.UR.y; 01587 boxes[boxn].UR.x = b.UR.x + (i + 1) * stepx; 01588 boxes[boxn].UR.y = b.UR.y + (i + 1) * stepy; 01589 boxn++; 01590 boxes[boxn].LL.x = tend.boxes[tend.boxn - 1].LL.x; 01591 boxes[boxn].LL.y = boxes[boxn-1].UR.y; 01592 boxes[boxn].UR.x = hend.boxes[hend.boxn - 1].UR.x; 01593 boxes[boxn].UR.y = boxes[boxn].LL.y + stepy; 01594 boxn++; 01595 b = hend.boxes[hend.boxn - 1]; 01596 boxes[boxn].UR.x = b.UR.x; 01597 boxes[boxn].LL.y = b.UR.y; 01598 boxes[boxn].LL.x = b.LL.x - (i + 1) * stepx; 01599 boxes[boxn].UR.y = boxes[boxn-1].LL.y; 01600 boxn++; 01601 01602 for (j = 0; j < tend.boxn; j++) add_box(P, tend.boxes[j]); 01603 for (j = 0; j < boxn; j++) add_box(P, boxes[j]); 01604 for (j = hend.boxn - 1; j >= 0; j--) add_box(P, hend.boxes[j]); 01605 01606 if (et == ET_SPLINE) ps = routesplines(P, &pn); 01607 else ps = routepolylines(P, &pn); 01608 if (pn == 0) 01609 return; 01610 clip_and_install(e, aghead(e), ps, pn, &sinfo); 01611 P->nbox = 0; 01612 } 01613 } 01614 01615 /* ccw: 01616 * Return true if p3 is to left of ray p1->p2 01617 */ 01618 static int 01619 leftOf (pointf p1, pointf p2, pointf p3) 01620 { 01621 int d; 01622 01623 d = ((p1.y - p2.y) * (p3.x - p2.x)) - 01624 ((p3.y - p2.y) * (p1.x - p2.x)); 01625 return (d > 0); 01626 } 01627 01628 /* makeLineEdge: 01629 * Create an edge as line segment. We guarantee that the points 01630 * are always drawn downwards. This means that for flipped edges, 01631 * we draw from the head to the tail. The routine returns the 01632 * end node of the edge in *hp. The points are stored in the 01633 * given array of points, and the number of points is returned. 01634 * 01635 * If the edge has a label, the edge is draw as two segments, with 01636 * the bend near the label. 01637 * 01638 * If the endpoints are on adjacent ranks, revert to usual code by 01639 * returning 0. 01640 * This is done because the usual code handles the interaction of 01641 * multiple edges better. 01642 */ 01643 static int 01644 makeLineEdge(edge_t* fe, pointf* points, node_t** hp) 01645 { 01646 int delr, pn; 01647 node_t* hn; 01648 node_t* tn; 01649 edge_t* e = fe; 01650 pointf startp, endp, lp; 01651 pointf dimen; 01652 double width, height; 01653 01654 while (ED_edge_type(e) != NORMAL) 01655 e = ED_to_orig(e); 01656 hn = aghead(e); 01657 tn = agtail(e); 01658 delr = ABS(ND_rank(hn)-ND_rank(tn)); 01659 if ((delr == 1) || ((delr == 2) && (GD_has_labels(agraphof(hn)) & EDGE_LABEL))) 01660 return 0; 01661 if (agtail(fe) == agtail(e)) { 01662 *hp = hn; 01663 startp = add_pointf(ND_coord(tn), ED_tail_port(e).p); 01664 endp = add_pointf(ND_coord(hn), ED_head_port(e).p); 01665 } 01666 else { 01667 *hp = tn; 01668 startp = add_pointf(ND_coord(hn), ED_head_port(e).p); 01669 endp = add_pointf(ND_coord(tn), ED_tail_port(e).p); 01670 } 01671 01672 if (ED_label(e)) { 01673 dimen = ED_label(e)->dimen; 01674 if (GD_flip(agraphof(hn))) { 01675 width = dimen.y; 01676 height = dimen.x; 01677 } 01678 else { 01679 width = dimen.x; 01680 height = dimen.y; 01681 } 01682 01683 lp = ED_label(e)->pos, lp; 01684 if (leftOf (endp,startp,lp)) { 01685 lp.x += width/2.0; 01686 lp.y -= height/2.0; 01687 } 01688 else { 01689 lp.x -= width/2.0; 01690 lp.y += height/2.0; 01691 } 01692 01693 points[1] = points[0] = startp; 01694 points[2] = points[3] = points[4] = lp; 01695 points[5] = points[6] = endp; 01696 pn = 7; 01697 } 01698 else { 01699 points[1] = points[0] = startp; 01700 points[3] = points[2] = endp; 01701 pn = 4; 01702 } 01703 01704 return pn; 01705 } 01706 01707 #define NUMPTS 2000 01708 01709 /* make_regular_edge: 01710 */ 01711 static void 01712 make_regular_edge(spline_info_t* sp, path * P, edge_t ** edges, int ind, int cnt, int et) 01713 { 01714 graph_t *g; 01715 node_t *tn, *hn; 01716 #ifndef WITH_CGRAPH 01717 edge_t fwdedgea, fwdedgeb, fwdedge, *e, *fe, *le, *segfirst; 01718 #else 01719 Agedgeinfo_t fwdedgeai, fwdedgebi, fwdedgei; 01720 Agedgepair_t fwdedgea, fwdedgeb, fwdedge; 01721 edge_t *e, *fe, *le, *segfirst; 01722 #endif /* WITH_CGRAPH */ 01723 pointf *ps; 01724 pathend_t tend, hend; 01725 boxf b; 01726 int boxn, sl, si, smode, i, j, dx, pn, hackflag, longedge; 01727 static pointf* pointfs; 01728 static pointf* pointfs2; 01729 static int numpts; 01730 static int numpts2; 01731 int pointn; 01732 01733 #ifdef WITH_CGRAPH 01734 fwdedgea.out.base.data = (Agrec_t*)&fwdedgeai; 01735 fwdedgeb.out.base.data = (Agrec_t*)&fwdedgebi; 01736 fwdedge.out.base.data = (Agrec_t*)&fwdedgei; 01737 #endif /* WITH_CGRAPH */ 01738 01739 if (!pointfs) { 01740 pointfs = N_GNEW(NUMPTS, pointf); 01741 pointfs2 = N_GNEW(NUMPTS, pointf); 01742 numpts = NUMPTS; 01743 numpts2 = NUMPTS; 01744 } 01745 sl = 0; 01746 e = edges[ind]; 01747 g = agraphof(agtail(e)); 01748 hackflag = FALSE; 01749 if (ABS(ND_rank(agtail(e)) - ND_rank(aghead(e))) > 1) { 01750 #ifndef WITH_CGRAPH 01751 fwdedgea = *e; 01752 #else /* WITH_CGRAPH */ 01753 fwdedgeai = *(Agedgeinfo_t*)e->base.data; 01754 fwdedgea.out = *e; 01755 fwdedgea.out.base.data = (Agrec_t*)&fwdedgeai; 01756 #endif /* WITH_CGRAPH */ 01757 if (ED_tree_index(e) & BWDEDGE) { 01758 #ifndef WITH_CGRAPH 01759 MAKEFWDEDGE(&fwdedgeb, e); 01760 fwdedgea.tail = aghead(e); 01761 fwdedgea.u.tail_port = ED_head_port(e); 01762 #else /* WITH_CGRAPH */ 01763 MAKEFWDEDGE(&fwdedgeb.out, e); 01764 agtail(&fwdedgea.out) = aghead(e); 01765 ED_tail_port(&fwdedgea.out) = ED_head_port(e); 01766 #endif /* WITH_CGRAPH */ 01767 } else { 01768 #ifndef WITH_CGRAPH 01769 fwdedgeb = *e; 01770 fwdedgea.tail = e->tail; 01771 #else /* WITH_CGRAPH */ 01772 fwdedgebi = *(Agedgeinfo_t*)e->base.data; 01773 fwdedgeb.out = *e; 01774 fwdedgeb.out.base.data = (Agrec_t*)&fwdedgebi; 01775 agtail(&fwdedgea.out) = agtail(e); 01776 #endif /* WITH_CGRAPH */ 01777 } 01778 le = getmainedge(e); 01779 while (ED_to_virt(le)) 01780 le = ED_to_virt(le); 01781 #ifndef WITH_CGRAPH 01782 fwdedgea.head = aghead(le); 01783 fwdedgea.u.head_port.defined = FALSE; 01784 fwdedgea.u.edge_type = VIRTUAL; 01785 fwdedgea.u.head_port.p.x = fwdedgea.u.head_port.p.y = 0; 01786 fwdedgea.u.to_orig = e; 01787 e = &fwdedgea; 01788 #else /* WITH_CGRAPH */ 01789 aghead(&fwdedgea.out) = aghead(le); 01790 ED_head_port(&fwdedgea.out).defined = FALSE; 01791 ED_edge_type(&fwdedgea.out) = VIRTUAL; 01792 ED_head_port(&fwdedgea.out).p.x = ED_head_port(&fwdedgea.out).p.y = 0; 01793 ED_to_orig(&fwdedgea.out) = e; 01794 e = &fwdedgea.out; 01795 #endif /* WITH_CGRAPH */ 01796 hackflag = TRUE; 01797 } else { 01798 if (ED_tree_index(e) & BWDEDGE) { 01799 #ifndef WITH_CGRAPH 01800 MAKEFWDEDGE(&fwdedgea, e); 01801 e = &fwdedgea; 01802 #else 01803 MAKEFWDEDGE(&fwdedgea.out, e); 01804 e = &fwdedgea.out; 01805 #endif 01806 } 01807 } 01808 fe = e; 01809 01810 /* compute the spline points for the edge */ 01811 01812 if ((et == ET_LINE) && (pointn = makeLineEdge (fe, pointfs, &hn))) { 01813 } 01814 else { 01815 int splines = et == ET_SPLINE; 01816 boxn = 0; 01817 pointn = 0; 01818 segfirst = e; 01819 tn = agtail(e); 01820 hn = aghead(e); 01821 b = tend.nb = maximal_bbox(sp, tn, NULL, e); 01822 beginpath(P, e, REGULAREDGE, &tend, spline_merge(tn)); 01823 b.UR.y = tend.boxes[tend.boxn - 1].UR.y; 01824 b.LL.y = tend.boxes[tend.boxn - 1].LL.y; 01825 b = makeregularend(b, BOTTOM, 01826 ND_coord(tn).y - GD_rank(agraphof(tn))[ND_rank(tn)].ht1); 01827 if (b.LL.x < b.UR.x && b.LL.y < b.UR.y) 01828 tend.boxes[tend.boxn++] = b; 01829 longedge = 0; 01830 smode = FALSE, si = -1; 01831 while (ND_node_type(hn) == VIRTUAL && !sinfo.splineMerge(hn)) { 01832 longedge = 1; 01833 boxes[boxn++] = rank_box(sp, g, ND_rank(tn)); 01834 if (!smode 01835 && ((sl = straight_len(hn)) >= 01836 ((GD_has_labels(g) & EDGE_LABEL) ? 4 + 1 : 2 + 1))) { 01837 smode = TRUE; 01838 si = 1, sl -= 2; 01839 } 01840 if (!smode || si > 0) { 01841 si--; 01842 boxes[boxn++] = maximal_bbox(sp, hn, e, ND_out(hn).list[0]); 01843 e = ND_out(hn).list[0]; 01844 tn = agtail(e); 01845 hn = aghead(e); 01846 continue; 01847 } 01848 hend.nb = maximal_bbox(sp, hn, e, ND_out(hn).list[0]); 01849 endpath(P, e, REGULAREDGE, &hend, spline_merge(aghead(e))); 01850 b = makeregularend(hend.boxes[hend.boxn - 1], TOP, 01851 ND_coord(hn).y + GD_rank(agraphof(hn))[ND_rank(hn)].ht2); 01852 if (b.LL.x < b.UR.x && b.LL.y < b.UR.y) 01853 hend.boxes[hend.boxn++] = b; 01854 P->end.theta = M_PI / 2, P->end.constrained = TRUE; 01855 completeregularpath(P, segfirst, e, &tend, &hend, boxes, boxn, 1); 01856 if (splines) ps = routesplines(P, &pn); 01857 else { 01858 ps = routepolylines (P, &pn); 01859 if ((et == ET_LINE) && (pn > 4)) { 01860 ps[1] = ps[0]; 01861 ps[3] = ps[2] = ps[pn-1]; 01862 pn = 4; 01863 } 01864 } 01865 if (pn == 0) 01866 return; 01867 01868 if (pointn + pn > numpts) { 01869 /* This should be enough to include 3 extra points added by 01870 * straight_path below. 01871 */ 01872 numpts = 2*(pointn+pn); 01873 pointfs = RALLOC(numpts, pointfs, pointf); 01874 } 01875 for (i = 0; i < pn; i++) { 01876 pointfs[pointn++] = ps[i]; 01877 } 01878 e = straight_path(ND_out(hn).list[0], sl, pointfs, &pointn); 01879 recover_slack(segfirst, P); 01880 segfirst = e; 01881 tn = agtail(e); 01882 hn = aghead(e); 01883 boxn = 0; 01884 tend.nb = maximal_bbox(sp, tn, ND_in(tn).list[0], e); 01885 beginpath(P, e, REGULAREDGE, &tend, spline_merge(tn)); 01886 b = makeregularend(tend.boxes[tend.boxn - 1], BOTTOM, 01887 ND_coord(tn).y - GD_rank(agraphof(tn))[ND_rank(tn)].ht1); 01888 if (b.LL.x < b.UR.x && b.LL.y < b.UR.y) 01889 tend.boxes[tend.boxn++] = b; 01890 P->start.theta = -M_PI / 2, P->start.constrained = TRUE; 01891 smode = FALSE; 01892 } 01893 boxes[boxn++] = rank_box(sp, g, ND_rank(tn)); 01894 b = hend.nb = maximal_bbox(sp, hn, e, NULL); 01895 #ifndef WITH_CGRAPH 01896 endpath(P, hackflag ? &fwdedgeb : e, REGULAREDGE, &hend, spline_merge(aghead(e))); 01897 #else 01898 endpath(P, hackflag ? &fwdedgeb.out : e, REGULAREDGE, &hend, spline_merge(aghead(e))); 01899 #endif 01900 b.UR.y = hend.boxes[hend.boxn - 1].UR.y; 01901 b.LL.y = hend.boxes[hend.boxn - 1].LL.y; 01902 b = makeregularend(b, TOP, 01903 ND_coord(hn).y + GD_rank(agraphof(hn))[ND_rank(hn)].ht2); 01904 if (b.LL.x < b.UR.x && b.LL.y < b.UR.y) 01905 hend.boxes[hend.boxn++] = b; 01906 completeregularpath(P, segfirst, e, &tend, &hend, boxes, boxn, 01907 longedge); 01908 if (splines) ps = routesplines(P, &pn); 01909 else ps = routepolylines (P, &pn); 01910 if ((et == ET_LINE) && (pn > 4)) { 01911 /* Here we have used the polyline case to handle 01912 * an edge between two nodes on adjacent ranks. If the 01913 * results really is a polyline, straighten it. 01914 */ 01915 ps[1] = ps[0]; 01916 ps[3] = ps[2] = ps[pn-1]; 01917 pn = 4; 01918 } 01919 if (pn == 0) 01920 return; 01921 if (pointn + pn > numpts) { 01922 numpts = 2*(pointn+pn); 01923 pointfs = RALLOC(numpts, pointfs, pointf); 01924 } 01925 for (i = 0; i < pn; i++) { 01926 pointfs[pointn++] = ps[i]; 01927 } 01928 recover_slack(segfirst, P); 01929 #ifndef WITH_CGRAPH 01930 hn = hackflag ? aghead(&fwdedgeb) : aghead(e); 01931 #else 01932 hn = hackflag ? aghead(&fwdedgeb.out) : aghead(e); 01933 #endif 01934 } 01935 01936 /* make copies of the spline points, one per multi-edge */ 01937 01938 if (cnt == 1) { 01939 clip_and_install(fe, hn, pointfs, pointn, &sinfo); 01940 return; 01941 } 01942 dx = sp->Multisep * (cnt - 1) / 2; 01943 for (i = 1; i < pointn - 1; i++) 01944 pointfs[i].x -= dx; 01945 01946 if (numpts > numpts2) { 01947 numpts2 = numpts; 01948 pointfs2 = RALLOC(numpts2, pointfs2, pointf); 01949 } 01950 for (i = 0; i < pointn; i++) 01951 pointfs2[i] = pointfs[i]; 01952 clip_and_install(fe, hn, pointfs2, pointn, &sinfo); 01953 for (j = 1; j < cnt; j++) { 01954 e = edges[ind + j]; 01955 if (ED_tree_index(e) & BWDEDGE) { 01956 #ifndef WITH_CGRAPH 01957 MAKEFWDEDGE(&fwdedge, e); 01958 e = &fwdedge; 01959 #else 01960 MAKEFWDEDGE(&fwdedge.out, e); 01961 e = &fwdedge.out; 01962 #endif 01963 } 01964 for (i = 1; i < pointn - 1; i++) 01965 pointfs[i].x += sp->Multisep; 01966 for (i = 0; i < pointn; i++) 01967 pointfs2[i] = pointfs[i]; 01968 clip_and_install(e, aghead(e), pointfs2, pointn, &sinfo); 01969 } 01970 } 01971 01972 /* regular edges */ 01973 01974 #define DONT_WANT_ANY_ENDPOINT_PATH_REFINEMENT 01975 #ifdef DONT_WANT_ANY_ENDPOINT_PATH_REFINEMENT 01976 static void 01977 completeregularpath(path * P, edge_t * first, edge_t * last, 01978 pathend_t * tendp, pathend_t * hendp, boxf * boxes, 01979 int boxn, int flag) 01980 { 01981 edge_t *uleft, *uright, *lleft, *lright; 01982 int i, fb, lb; 01983 splines *spl; 01984 pointf *pp; 01985 int pn; 01986 01987 fb = lb = -1; 01988 uleft = uright = NULL; 01989 uleft = top_bound(first, -1), uright = top_bound(first, 1); 01990 if (uleft) { 01991 if (!(spl = getsplinepoints(uleft))) return; 01992 pp = spl->list[0].list; 01993 pn = spl->list[0].size; 01994 } 01995 if (uright) { 01996 if (!(spl = getsplinepoints(uright))) return; 01997 pp = spl->list[0].list; 01998 pn = spl->list[0].size; 01999 } 02000 lleft = lright = NULL; 02001 lleft = bot_bound(last, -1), lright = bot_bound(last, 1); 02002 if (lleft) { 02003 if (!(spl = getsplinepoints(lleft))) return; 02004 pp = spl->list[spl->size - 1].list; 02005 pn = spl->list[spl->size - 1].size; 02006 } 02007 if (lright) { 02008 if (!(spl = getsplinepoints(lright))) return; 02009 pp = spl->list[spl->size - 1].list; 02010 pn = spl->list[spl->size - 1].size; 02011 } 02012 for (i = 0; i < tendp->boxn; i++) 02013 add_box(P, tendp->boxes[i]); 02014 fb = P->nbox + 1; 02015 lb = fb + boxn - 3; 02016 for (i = 0; i < boxn; i++) 02017 add_box(P, boxes[i]); 02018 for (i = hendp->boxn - 1; i >= 0; i--) 02019 add_box(P, hendp->boxes[i]); 02020 adjustregularpath(P, fb, lb); 02021 } 02022 #else 02023 void refineregularends(edge_t * left, edge_t * right, pathend_t * endp, 02024 int dir, boxf b, boxf * boxes, int *boxnp); 02025 02026 /* box subdivision is obsolete, I think... ek */ 02027 static void 02028 completeregularpath(path * P, edge_t * first, edge_t * last, 02029 pathend_t * tendp, pathend_t * hendp, boxf * boxes, 02030 int boxn, int flag) 02031 { 02032 edge_t *uleft, *uright, *lleft, *lright; 02033 boxf uboxes[NSUB], lboxes[NSUB]; 02034 boxf b; 02035 int uboxn, lboxn, i, y, fb, lb; 02036 02037 fb = lb = -1; 02038 uleft = uright = NULL; 02039 if (flag || ND_rank(agtail(first)) + 1 != ND_rank(aghead(last))) 02040 uleft = top_bound(first, -1), uright = top_bound(first, 1); 02041 refineregularends(uleft, uright, tendp, 1, boxes[0], uboxes, &uboxn); 02042 lleft = lright = NULL; 02043 if (flag || ND_rank(agtail(first)) + 1 != ND_rank(aghead(last))) 02044 lleft = bot_bound(last, -1), lright = bot_bound(last, 1); 02045 refineregularends(lleft, lright, hendp, -1, boxes[boxn - 1], lboxes, 02046 &lboxn); 02047 for (i = 0; i < tendp->boxn; i++) 02048 add_box(P, tendp->boxes[i]); 02049 if (ND_rank(agtail(first)) + 1 == ND_rank(aghead(last))) { 02050 if ((!uleft && !uright) && (lleft || lright)) { 02051 b = boxes[0]; 02052 y = b.UR.y - b.LL.y; 02053 for (i = 0; i < NSUB; i++) { 02054 uboxes[i] = b; 02055 uboxes[i].UR.y = b.UR.y - y * i / NSUB; 02056 uboxes[i].LL.y = b.UR.y - y * (i + 1) / NSUB; 02057 } 02058 uboxn = NSUB; 02059 } else if ((uleft || uright) && (!lleft && !lright)) { 02060 b = boxes[boxn - 1]; 02061 y = b.UR.y - b.LL.y; 02062 for (i = 0; i < NSUB; i++) { 02063 lboxes[i] = b; 02064 lboxes[i].UR.y = b.UR.y - y * i / NSUB; 02065 lboxes[i].LL.y = b.UR.y - y * (i + 1) / NSUB; 02066 } 02067 lboxn = NSUB; 02068 } 02069 for (i = 0; i < uboxn; i++) { 02070 uboxes[i].LL.x = MAX(uboxes[i].LL.x, lboxes[i].LL.x); 02071 uboxes[i].UR.x = MIN(uboxes[i].UR.x, lboxes[i].UR.x); 02072 } 02073 for (i = 0; i < uboxn; i++) 02074 add_box(P, uboxes[i]); 02075 } else { 02076 for (i = 0; i < uboxn; i++) 02077 add_box(P, uboxes[i]); 02078 fb = P->nbox; 02079 lb = fb + boxn - 3; 02080 for (i = 1; i < boxn - 1; i++) 02081 add_box(P, boxes[i]); 02082 for (i = 0; i < lboxn; i++) 02083 add_box(P, lboxes[i]); 02084 } 02085 for (i = hendp->boxn - 1; i >= 0; i--) 02086 add_box(P, hendp->boxes[i]); 02087 adjustregularpath(P, fb, lb); 02088 } 02089 #endif 02090 02091 /* makeregularend: 02092 * Add box to fill between node and interrank space. Needed because 02093 * nodes in a given rank can differ in height. 02094 * for now, regular edges always go from top to bottom 02095 */ 02096 static boxf makeregularend(boxf b, int side, int y) 02097 { 02098 boxf newb; 02099 switch (side) { 02100 case BOTTOM: 02101 newb = boxfof(b.LL.x, y, b.UR.x, b.LL.y); 02102 break; 02103 case TOP: 02104 newb = boxfof(b.LL.x, b.UR.y, b.UR.x, y); 02105 break; 02106 } 02107 return newb; 02108 } 02109 02110 #ifndef DONT_WANT_ANY_ENDPOINT_PATH_REFINEMENT 02111 void refineregularends(left, right, endp, dir, b, boxes, boxnp) 02112 edge_t *left, *right; 02113 pathend_t *endp; 02114 int dir; 02115 box b; 02116 box *boxes; 02117 int *boxnp; 02118 { 02119 splines *lspls, *rspls; 02120 point pp, cp; 02121 box eb; 02122 box *bp; 02123 int y, i, j, k; 02124 int nsub; 02125 02126 y = b.UR.y - b.LL.y; 02127 if ((y == 1) || (!left && !right)) { 02128 boxes[0] = b; 02129 *boxnp = 1; 02130 return; 02131 } 02132 nsub = MIN(NSUB, y); 02133 for (i = 0; i < nsub; i++) { 02134 boxes[i] = b; 02135 boxes[i].UR.y = b.UR.y - y * i / nsub; 02136 boxes[i].LL.y = b.UR.y - y * (i + 1) / nsub; 02137 if (boxes[i].UR.y == boxes[i].LL.y) 02138 abort(); 02139 } 02140 *boxnp = nsub; 02141 /* only break big boxes */ 02142 for (j = 0; j < endp->boxn; j++) { 02143 eb = endp->boxes[j]; 02144 y = eb.UR.y - eb.LL.y; 02145 #ifdef STEVE_AND_LEFTY_GRASPING_AT_STRAWS 02146 if (y < 15) 02147 continue; 02148 #else 02149 if (y < nsub) 02150 continue; 02151 #endif 02152 for (k = endp->boxn - 1; k > j; k--) 02153 endp->boxes[k + (nsub - 1)] = endp->boxes[k]; 02154 for (i = 0; i < nsub; i++) { 02155 bp = &endp->boxes[j + ((dir == 1) ? i : (nsub - i - 1))]; 02156 *bp = eb; 02157 bp->UR.y = eb.UR.y - y * i / nsub; 02158 bp->LL.y = eb.UR.y - y * (i + 1) / nsub; 02159 if (bp->UR.y == bp->LL.y) 02160 abort(); 02161 } 02162 endp->boxn += (nsub - 1); 02163 j += nsub - 1; 02164 } 02165 if (left) { 02166 if (!(lspls = getsplinepoints(left))) return; 02167 pp = spline_at_y(lspls, boxes[0].UR.y); 02168 for (i = 0; i < nsub; i++) { 02169 cp = spline_at_y(lspls, boxes[i].LL.y); 02170 /*boxes[i].LL.x = AVG (pp.x, cp.x); */ 02171 boxes[i].LL.x = MAX(pp.x, cp.x); 02172 pp = cp; 02173 } 02174 pp = spline_at_y(lspls, (dir == 1) ? 02175 endp->boxes[1].UR.y : endp->boxes[1].LL.y); 02176 for (i = 1; i < endp->boxn; i++) { 02177 cp = spline_at_y(lspls, (dir == 1) ? 02178 endp->boxes[i].LL.y : endp->boxes[i].UR.y); 02179 endp->boxes[i].LL.x = MIN(endp->nb.UR.x, MAX(pp.x, cp.x)); 02180 pp = cp; 02181 } 02182 i = (dir == 1) ? 0 : *boxnp - 1; 02183 if (boxes[i].LL.x > endp->boxes[endp->boxn - 1].UR.x - MINW) 02184 boxes[i].LL.x = endp->boxes[endp->boxn - 1].UR.x - MINW; 02185 } 02186 if (right) { 02187 if (!(rspls = getsplinepoints(right))) return; 02188 pp = spline_at_y(rspls, boxes[0].UR.y); 02189 for (i = 0; i < nsub; i++) { 02190 cp = spline_at_y(rspls, boxes[i].LL.y); 02191 /*boxes[i].UR.x = AVG (pp.x, cp.x); */ 02192 boxes[i].UR.x = AVG(pp.x, cp.x); 02193 pp = cp; 02194 } 02195 pp = spline_at_y(rspls, (dir == 1) ? 02196 endp->boxes[1].UR.y : endp->boxes[1].LL.y); 02197 for (i = 1; i < endp->boxn; i++) { 02198 cp = spline_at_y(rspls, (dir == 1) ? 02199 endp->boxes[i].LL.y : endp->boxes[i].UR.y); 02200 endp->boxes[i].UR.x = MAX(endp->nb.LL.x, AVG(pp.x, cp.x)); 02201 pp = cp; 02202 } 02203 i = (dir == 1) ? 0 : *boxnp - 1; 02204 if (boxes[i].UR.x < endp->boxes[endp->boxn - 1].LL.x + MINW) 02205 boxes[i].UR.x = endp->boxes[endp->boxn - 1].LL.x + MINW; 02206 } 02207 } 02208 #endif 02209 02210 /* adjustregularpath: 02211 * make sure the path is wide enough. 02212 * the % 2 was so that in rank boxes would only be grown if 02213 * they were == 0 while inter-rank boxes could be stretched to a min 02214 * width. 02215 * The list of boxes has three parts: tail boxes, path boxes, and head 02216 * boxes. (Note that because of back edges, the tail boxes might actually 02217 * belong to the head node, and vice versa.) fb is the index of the 02218 * first interrank path box and lb is the last interrank path box. 02219 * If fb > lb, there are none. 02220 * 02221 * The second for loop was added by ek long ago, and apparently is intended 02222 * to guarantee an overlap between adjacent boxes of at least MINW. 02223 * It doesn't do this, and the ifdef'ed part has the potential of moving 02224 * a box within a node for more complex paths. 02225 */ 02226 static void adjustregularpath(path * P, int fb, int lb) 02227 { 02228 boxf *bp1, *bp2; 02229 int i, x; 02230 02231 for (i = fb-1; i < lb+1; i++) { 02232 bp1 = &P->boxes[i]; 02233 if ((i - fb) % 2 == 0) { 02234 if (bp1->LL.x >= bp1->UR.x) { 02235 x = (bp1->LL.x + bp1->UR.x) / 2; 02236 bp1->LL.x = x - HALFMINW, bp1->UR.x = x + HALFMINW; 02237 } 02238 } else { 02239 if (bp1->LL.x + MINW > bp1->UR.x) { 02240 x = (bp1->LL.x + bp1->UR.x) / 2; 02241 bp1->LL.x = x - HALFMINW, bp1->UR.x = x + HALFMINW; 02242 } 02243 } 02244 } 02245 for (i = 0; i < P->nbox - 1; i++) { 02246 bp1 = &P->boxes[i], bp2 = &P->boxes[i + 1]; 02247 if (i >= fb && i <= lb && (i - fb) % 2 == 0) { 02248 if (bp1->LL.x + MINW > bp2->UR.x) 02249 bp2->UR.x = bp1->LL.x + MINW; 02250 if (bp1->UR.x - MINW < bp2->LL.x) 02251 bp2->LL.x = bp1->UR.x - MINW; 02252 } else if (i + 1 >= fb && i < lb && (i + 1 - fb) % 2 == 0) { 02253 if (bp1->LL.x + MINW > bp2->UR.x) 02254 bp1->LL.x = bp2->UR.x - MINW; 02255 if (bp1->UR.x - MINW < bp2->LL.x) 02256 bp1->UR.x = bp2->LL.x + MINW; 02257 } 02258 } 02259 } 02260 02261 static boxf rank_box(spline_info_t* sp, graph_t * g, int r) 02262 { 02263 boxf b; 02264 node_t /* *right0, *right1, */ * left0, *left1; 02265 02266 b = sp->Rank_box[r]; 02267 if (b.LL.x == b.UR.x) { 02268 left0 = GD_rank(g)[r].v[0]; 02269 /* right0 = GD_rank(g)[r].v[GD_rank(g)[r].n - 1]; */ 02270 left1 = GD_rank(g)[r + 1].v[0]; 02271 /* right1 = GD_rank(g)[r + 1].v[GD_rank(g)[r + 1].n - 1]; */ 02272 b.LL.x = sp->LeftBound; 02273 b.LL.y = ND_coord(left1).y + GD_rank(g)[r + 1].ht2; 02274 b.UR.x = sp->RightBound; 02275 b.UR.y = ND_coord(left0).y - GD_rank(g)[r].ht1; 02276 sp->Rank_box[r] = b; 02277 } 02278 return b; 02279 } 02280 02281 /* returns count of vertically aligned edges starting at n */ 02282 static int straight_len(node_t * n) 02283 { 02284 int cnt = 0; 02285 node_t *v; 02286 02287 v = n; 02288 while (1) { 02289 v = aghead(ND_out(v).list[0]); 02290 if (ND_node_type(v) != VIRTUAL) 02291 break; 02292 if ((ND_out(v).size != 1) || (ND_in(v).size != 1)) 02293 break; 02294 if (ND_coord(v).x != ND_coord(n).x) 02295 break; 02296 cnt++; 02297 } 02298 return cnt; 02299 } 02300 02301 static edge_t *straight_path(edge_t * e, int cnt, pointf * plist, int *np) 02302 { 02303 int n = *np; 02304 edge_t *f = e; 02305 02306 while (cnt--) 02307 f = ND_out(aghead(f)).list[0]; 02308 plist[(*np)++] = plist[n - 1]; 02309 plist[(*np)++] = plist[n - 1]; 02310 plist[(*np)] = ND_coord(agtail(f)); /* will be overwritten by next spline */ 02311 02312 return f; 02313 } 02314 02315 static void recover_slack(edge_t * e, path * p) 02316 { 02317 int b; 02318 node_t *vn; 02319 02320 b = 0; /* skip first rank box */ 02321 for (vn = aghead(e); 02322 ND_node_type(vn) == VIRTUAL && !sinfo.splineMerge(vn); 02323 vn = aghead(ND_out(vn).list[0])) { 02324 while ((b < p->nbox) && (p->boxes[b].LL.y > ND_coord(vn).y)) 02325 b++; 02326 if (b >= p->nbox) 02327 break; 02328 if (p->boxes[b].UR.y < ND_coord(vn).y) 02329 continue; 02330 if (ND_label(vn)) 02331 resize_vn(vn, p->boxes[b].LL.x, p->boxes[b].UR.x, 02332 p->boxes[b].UR.x + ND_rw(vn)); 02333 else 02334 resize_vn(vn, p->boxes[b].LL.x, (p->boxes[b].LL.x + 02335 p->boxes[b].UR.x) / 2, 02336 p->boxes[b].UR.x); 02337 } 02338 } 02339 02340 static void resize_vn(vn, lx, cx, rx) 02341 node_t *vn; 02342 int lx, cx, rx; 02343 { 02344 ND_coord(vn).x = cx; 02345 ND_lw(vn) = cx - lx, ND_rw(vn) = rx - cx; 02346 } 02347 02348 /* side > 0 means right. side < 0 means left */ 02349 static edge_t *top_bound(edge_t * e, int side) 02350 { 02351 edge_t *f, *ans = NULL; 02352 int i; 02353 02354 for (i = 0; (f = ND_out(agtail(e)).list[i]); i++) { 02355 #if 0 /* were we out of our minds? */ 02356 if (ED_tail_port(e).p.x != ED_tail_port(f).p.x) 02357 continue; 02358 #endif 02359 if (side * (ND_order(aghead(f)) - ND_order(aghead(e))) <= 0) 02360 continue; 02361 if ((ED_spl(f) == NULL) 02362 && ((ED_to_orig(f) == NULL) || (ED_spl(ED_to_orig(f)) == NULL))) 02363 continue; 02364 if ((ans == NULL) 02365 || (side * (ND_order(aghead(ans)) - ND_order(aghead(f))) > 0)) 02366 ans = f; 02367 } 02368 return ans; 02369 } 02370 02371 static edge_t *bot_bound(edge_t * e, int side) 02372 { 02373 edge_t *f, *ans = NULL; 02374 int i; 02375 02376 for (i = 0; (f = ND_in(aghead(e)).list[i]); i++) { 02377 #if 0 /* same here */ 02378 if (ED_head_port(e).p.x != ED_head_port(f).p.x) 02379 continue; 02380 #endif 02381 if (side * (ND_order(agtail(f)) - ND_order(agtail(e))) <= 0) 02382 continue; 02383 if ((ED_spl(f) == NULL) 02384 && ((ED_to_orig(f) == NULL) || (ED_spl(ED_to_orig(f)) == NULL))) 02385 continue; 02386 if ((ans == NULL) 02387 || (side * (ND_order(agtail(ans)) - ND_order(agtail(f))) > 0)) 02388 ans = f; 02389 } 02390 return ans; 02391 } 02392 02393 /* common routines */ 02394 02395 static int cl_vninside(graph_t * cl, node_t * n) 02396 { 02397 return (BETWEEN(GD_bb(cl).LL.x, (double)(ND_coord(n).x), GD_bb(cl).UR.x) && 02398 BETWEEN(GD_bb(cl).LL.y, (double)(ND_coord(n).y), GD_bb(cl).UR.y)); 02399 } 02400 02401 /* All nodes belong to some cluster, which may be the root graph. 02402 * For the following, we only want a cluster if it is a real cluster 02403 * It is not clear this will handle all potential problems. It seems one 02404 * could have hcl and tcl contained in cl, which would also cause problems. 02405 */ 02406 #define REAL_CLUSTER(n) (ND_clust(n)==agraphof(n)?NULL:ND_clust(n)) 02407 02408 /* returns the cluster of (adj) that interferes with n, 02409 */ 02410 static Agraph_t *cl_bound(n, adj) 02411 node_t *n, *adj; 02412 { 02413 graph_t *rv, *cl, *tcl, *hcl; 02414 edge_t *orig; 02415 02416 rv = NULL; 02417 if (ND_node_type(n) == NORMAL) 02418 tcl = hcl = ND_clust(n); 02419 else { 02420 orig = ED_to_orig(ND_out(n).list[0]); 02421 tcl = ND_clust(agtail(orig)); 02422 hcl = ND_clust(aghead(orig)); 02423 } 02424 if (ND_node_type(adj) == NORMAL) { 02425 cl = REAL_CLUSTER(adj); 02426 if (cl && (cl != tcl) && (cl != hcl)) 02427 rv = cl; 02428 } else { 02429 orig = ED_to_orig(ND_out(adj).list[0]); 02430 cl = REAL_CLUSTER(agtail(orig)); 02431 if (cl && (cl != tcl) && (cl != hcl) && cl_vninside(cl, adj)) 02432 rv = cl; 02433 else { 02434 cl = REAL_CLUSTER(aghead(orig)); 02435 if (cl && (cl != tcl) && (cl != hcl) && cl_vninside(cl, adj)) 02436 rv = cl; 02437 } 02438 } 02439 return rv; 02440 } 02441 02442 /* maximal_bbox: 02443 * Return an initial bounding box to be used for building the 02444 * beginning or ending of the path of boxes. 02445 * Height reflects height of tallest node on rank. 02446 * The extra space provided by FUDGE allows begin/endpath to create a box 02447 * FUDGE-2 away from the node, so the routing can avoid the node and the 02448 * box is at least 2 wide. 02449 */ 02450 #define FUDGE 4 02451 02452 static boxf maximal_bbox(spline_info_t* sp, node_t* vn, edge_t* ie, edge_t* oe) 02453 { 02454 double b, nb; 02455 graph_t *g = agraphof(vn), *left_cl, *right_cl; 02456 node_t *left, *right; 02457 boxf rv; 02458 02459 left_cl = right_cl = NULL; 02460 02461 /* give this node all the available space up to its neighbors */ 02462 b = (double)(ND_coord(vn).x - ND_lw(vn) - FUDGE); 02463 if ((left = neighbor(vn, ie, oe, -1))) { 02464 if ((left_cl = cl_bound(vn, left))) 02465 nb = GD_bb(left_cl).UR.x + (double)(sp->Splinesep); 02466 else { 02467 nb = (double)(ND_coord(left).x + ND_mval(left)); 02468 if (ND_node_type(left) == NORMAL) 02469 nb += GD_nodesep(g) / 2.; 02470 else 02471 nb += (double)(sp->Splinesep); 02472 } 02473 if (nb < b) 02474 b = nb; 02475 rv.LL.x = ROUND(b); 02476 } else 02477 rv.LL.x = MIN(ROUND(b), sp->LeftBound); 02478 02479 /* we have to leave room for our own label! */ 02480 if ((ND_node_type(vn) == VIRTUAL) && (ND_label(vn))) 02481 b = (double)(ND_coord(vn).x + 10); 02482 else 02483 b = (double)(ND_coord(vn).x + ND_rw(vn) + FUDGE); 02484 if ((right = neighbor(vn, ie, oe, 1))) { 02485 if ((right_cl = cl_bound(vn, right))) 02486 nb = GD_bb(right_cl).LL.x - (double)(sp->Splinesep); 02487 else { 02488 nb = ND_coord(right).x - ND_lw(right); 02489 if (ND_node_type(right) == NORMAL) 02490 nb -= GD_nodesep(g) / 2.; 02491 else 02492 nb -= (double)(sp->Splinesep); 02493 } 02494 if (nb > b) 02495 b = nb; 02496 rv.UR.x = ROUND(b); 02497 } else 02498 rv.UR.x = MAX(ROUND(b), sp->RightBound); 02499 02500 if ((ND_node_type(vn) == VIRTUAL) && (ND_label(vn))) { 02501 rv.UR.x -= ND_rw(vn); 02502 if (rv.UR.x < rv.LL.x) rv.UR.x = ND_coord(vn).x; 02503 } 02504 02505 rv.LL.y = ND_coord(vn).y - GD_rank(g)[ND_rank(vn)].ht1; 02506 rv.UR.y = ND_coord(vn).y + GD_rank(g)[ND_rank(vn)].ht2; 02507 return rv; 02508 } 02509 02510 static node_t *neighbor(vn, ie, oe, dir) 02511 node_t *vn; 02512 edge_t *ie, *oe; 02513 int dir; 02514 { 02515 int i; 02516 node_t *n, *rv = NULL; 02517 rank_t *rank = &(GD_rank(agraphof(vn))[ND_rank(vn)]); 02518 02519 for (i = ND_order(vn) + dir; ((i >= 0) && (i < rank->n)); i += dir) { 02520 n = rank->v[i]; 02521 if ((ND_node_type(n) == VIRTUAL) && (ND_label(n))) { 02522 rv = n; 02523 break; 02524 } 02525 if (ND_node_type(n) == NORMAL) { 02526 rv = n; 02527 break; 02528 } 02529 if (pathscross(n, vn, ie, oe) == FALSE) { 02530 rv = n; 02531 break; 02532 } 02533 } 02534 return rv; 02535 } 02536 02537 static boolean pathscross(n0, n1, ie1, oe1) 02538 node_t *n0, *n1; 02539 edge_t *ie1, *oe1; 02540 { 02541 edge_t *e0, *e1; 02542 node_t *na, *nb; 02543 int order, cnt; 02544 02545 order = (ND_order(n0) > ND_order(n1)); 02546 if ((ND_out(n0).size != 1) && (ND_out(n0).size != 1)) 02547 return FALSE; 02548 e1 = oe1; 02549 if (ND_out(n0).size == 1 && e1) { 02550 e0 = ND_out(n0).list[0]; 02551 for (cnt = 0; cnt < 2; cnt++) { 02552 if ((na = aghead(e0)) == (nb = aghead(e1))) 02553 break; 02554 if (order != (ND_order(na) > ND_order(nb))) 02555 return TRUE; 02556 if ((ND_out(na).size != 1) || (ND_node_type(na) == NORMAL)) 02557 break; 02558 e0 = ND_out(na).list[0]; 02559 if ((ND_out(nb).size != 1) || (ND_node_type(nb) == NORMAL)) 02560 break; 02561 e1 = ND_out(nb).list[0]; 02562 } 02563 } 02564 e1 = ie1; 02565 if (ND_in(n0).size == 1 && e1) { 02566 e0 = ND_in(n0).list[0]; 02567 for (cnt = 0; cnt < 2; cnt++) { 02568 if ((na = agtail(e0)) == (nb = agtail(e1))) 02569 break; 02570 if (order != (ND_order(na) > ND_order(nb))) 02571 return TRUE; 02572 if ((ND_in(na).size != 1) || (ND_node_type(na) == NORMAL)) 02573 break; 02574 e0 = ND_in(na).list[0]; 02575 if ((ND_in(nb).size != 1) || (ND_node_type(nb) == NORMAL)) 02576 break; 02577 e1 = ND_in(nb).list[0]; 02578 } 02579 } 02580 return FALSE; 02581 } 02582 02583 #ifdef DEBUG 02584 void showpath(path * p) 02585 { 02586 int i; 02587 pointf LL, UR; 02588 02589 fprintf(stderr, "%%!PS\n"); 02590 for (i = 0; i < p->nbox; i++) { 02591 LL = p->boxes[i].LL; 02592 UR = p->boxes[i].UR; 02593 fprintf(stderr, 02594 "newpath %.04f %.04f moveto %.04f %.04f lineto %.04f %.04f lineto %.04f %.04f lineto closepath stroke\n", 02595 LL.x, LL.y, UR.x, LL.y, UR.x, UR.y, LL.x, UR.y); 02596 } 02597 fprintf(stderr, "showpage\n"); 02598 } 02599 #endif
1.7.5