| 1 | /* $Id$ $Revision$ */ | 
|---|
| 2 | /* vim:set shiftwidth=4 ts=8: */ | 
|---|
| 3 |  | 
|---|
| 4 | /************************************************************************* | 
|---|
| 5 | * Copyright (c) 2011 AT&T Intellectual Property | 
|---|
| 6 | * All rights reserved. This program and the accompanying materials | 
|---|
| 7 | * are made available under the terms of the Eclipse Public License v1.0 | 
|---|
| 8 | * which accompanies this distribution, and is available at | 
|---|
| 9 | * http://www.eclipse.org/legal/epl-v10.html | 
|---|
| 10 | * | 
|---|
| 11 | * Contributors: See CVS logs. Details at http://www.graphviz.org/ | 
|---|
| 12 | *************************************************************************/ | 
|---|
| 13 |  | 
|---|
| 14 |  | 
|---|
| 15 | /* | 
|---|
| 16 | * set edge splines. | 
|---|
| 17 | */ | 
|---|
| 18 |  | 
|---|
| 19 | #include "dot.h" | 
|---|
| 20 |  | 
|---|
| 21 | #ifdef ORTHO | 
|---|
| 22 | #include <ortho.h> | 
|---|
| 23 | #endif | 
|---|
| 24 |  | 
|---|
| 25 | #define	NSUB	9		/* number of subdivisions, re-aiming splines */ | 
|---|
| 26 | #define	CHUNK	128		/* in building list of edges */ | 
|---|
| 27 |  | 
|---|
| 28 | #define MINW 16			/* minimum width of a box in the edge path */ | 
|---|
| 29 | #define HALFMINW 8 | 
|---|
| 30 |  | 
|---|
| 31 | #define FWDEDGE    16 | 
|---|
| 32 | #define BWDEDGE    32 | 
|---|
| 33 |  | 
|---|
| 34 | #define MAINGRAPH  64 | 
|---|
| 35 | #define AUXGRAPH  128 | 
|---|
| 36 | #define GRAPHTYPEMASK	192	/* the OR of the above */ | 
|---|
| 37 |  | 
|---|
| 38 | #define MAKEFWDEDGE(new, old) { \ | 
|---|
| 39 | edge_t *newp; \ | 
|---|
| 40 | Agedgeinfo_t *info; \ | 
|---|
| 41 | newp = new; \ | 
|---|
| 42 | info = (Agedgeinfo_t*)newp->base.data; \ | 
|---|
| 43 | *info = *(Agedgeinfo_t*)old->base.data; \ | 
|---|
| 44 | *newp = *old; \ | 
|---|
| 45 | newp->base.data = (Agrec_t*)info; \ | 
|---|
| 46 | AGTAIL(newp) = AGHEAD(old); \ | 
|---|
| 47 | AGHEAD(newp) = AGTAIL(old); \ | 
|---|
| 48 | ED_tail_port(newp) = ED_head_port(old); \ | 
|---|
| 49 | ED_head_port(newp) = ED_tail_port(old); \ | 
|---|
| 50 | ED_edge_type(newp) = VIRTUAL; \ | 
|---|
| 51 | ED_to_orig(newp) = old; \ | 
|---|
| 52 | } | 
|---|
| 53 |  | 
|---|
| 54 | static boxf boxes[1000]; | 
|---|
| 55 | typedef struct { | 
|---|
| 56 | int LeftBound, RightBound, Splinesep, Multisep; | 
|---|
| 57 | boxf* Rank_box; | 
|---|
| 58 | } spline_info_t; | 
|---|
| 59 |  | 
|---|
| 60 | static void adjustregularpath(path *, int, int); | 
|---|
| 61 | static Agedge_t *bot_bound(Agedge_t *, int); | 
|---|
| 62 | static boolean pathscross(Agnode_t *, Agnode_t *, Agedge_t *, Agedge_t *); | 
|---|
| 63 | static Agraph_t *cl_bound(graph_t*, Agnode_t *, Agnode_t *); | 
|---|
| 64 | static int cl_vninside(Agraph_t *, Agnode_t *); | 
|---|
| 65 | static void completeregularpath(path *, Agedge_t *, Agedge_t *, | 
|---|
| 66 | pathend_t *, pathend_t *, boxf *, int, int); | 
|---|
| 67 | static int edgecmp(Agedge_t **, Agedge_t **); | 
|---|
| 68 | static void make_flat_edge(graph_t*, spline_info_t*, path *, Agedge_t **, int, int, int); | 
|---|
| 69 | static void make_regular_edge(graph_t* g, spline_info_t*, path *, Agedge_t **, int, int, int); | 
|---|
| 70 | static boxf makeregularend(boxf, int, double); | 
|---|
| 71 | static boxf maximal_bbox(graph_t* g, spline_info_t*, Agnode_t *, Agedge_t *, Agedge_t *); | 
|---|
| 72 | static Agnode_t *neighbor(graph_t*, Agnode_t *, Agedge_t *, Agedge_t *, int); | 
|---|
| 73 | static void place_vnlabel(Agnode_t *); | 
|---|
| 74 | static boxf rank_box(spline_info_t* sp, Agraph_t *, int); | 
|---|
| 75 | static void recover_slack(Agedge_t *, path *); | 
|---|
| 76 | static void resize_vn(Agnode_t *, int, int, int); | 
|---|
| 77 | static void setflags(Agedge_t *, int, int, int); | 
|---|
| 78 | static int straight_len(Agnode_t *); | 
|---|
| 79 | static Agedge_t *straight_path(Agedge_t *, int, pointf *, int *); | 
|---|
| 80 | static Agedge_t *top_bound(Agedge_t *, int); | 
|---|
| 81 |  | 
|---|
| 82 | #define GROWEDGES (edges = ALLOC (n_edges + CHUNK, edges, edge_t*)) | 
|---|
| 83 |  | 
|---|
| 84 | static edge_t* | 
|---|
| 85 | getmainedge(edge_t * e) | 
|---|
| 86 | { | 
|---|
| 87 | edge_t *le = e; | 
|---|
| 88 | while (ED_to_virt(le)) | 
|---|
| 89 | le = ED_to_virt(le); | 
|---|
| 90 | while (ED_to_orig(le)) | 
|---|
| 91 | le = ED_to_orig(le); | 
|---|
| 92 | return le; | 
|---|
| 93 | } | 
|---|
| 94 |  | 
|---|
| 95 | static boolean spline_merge(node_t * n) | 
|---|
| 96 | { | 
|---|
| 97 | return ((ND_node_type(n) == VIRTUAL) | 
|---|
| 98 | && ((ND_in(n).size > 1) || (ND_out(n).size > 1))); | 
|---|
| 99 | } | 
|---|
| 100 |  | 
|---|
| 101 | static boolean swap_ends_p(edge_t * e) | 
|---|
| 102 | { | 
|---|
| 103 | while (ED_to_orig(e)) | 
|---|
| 104 | e = ED_to_orig(e); | 
|---|
| 105 | if (ND_rank(aghead(e)) > ND_rank(agtail(e))) | 
|---|
| 106 | return FALSE; | 
|---|
| 107 | if (ND_rank(aghead(e)) < ND_rank(agtail(e))) | 
|---|
| 108 | return TRUE; | 
|---|
| 109 | if (ND_order(aghead(e)) >= ND_order(agtail(e))) | 
|---|
| 110 | return FALSE; | 
|---|
| 111 | return TRUE; | 
|---|
| 112 | } | 
|---|
| 113 |  | 
|---|
| 114 | static splineInfo sinfo = { swap_ends_p, spline_merge }; | 
|---|
| 115 |  | 
|---|
| 116 | int portcmp(port p0, port p1) | 
|---|
| 117 | { | 
|---|
| 118 | int rv; | 
|---|
| 119 | if (p1.defined == FALSE) | 
|---|
| 120 | return (p0.defined ? 1 : 0); | 
|---|
| 121 | if (p0.defined == FALSE) | 
|---|
| 122 | return -1; | 
|---|
| 123 | rv = p0.p.x - p1.p.x; | 
|---|
| 124 | if (rv == 0) | 
|---|
| 125 | rv = p0.p.y - p1.p.y; | 
|---|
| 126 | return rv; | 
|---|
| 127 | } | 
|---|
| 128 |  | 
|---|
| 129 | /* swap_bezier: | 
|---|
| 130 | */ | 
|---|
| 131 | static void swap_bezier(bezier * old, bezier * new) | 
|---|
| 132 | { | 
|---|
| 133 | pointf *list; | 
|---|
| 134 | pointf *lp; | 
|---|
| 135 | pointf *olp; | 
|---|
| 136 | int i, sz; | 
|---|
| 137 |  | 
|---|
| 138 | sz = old->size; | 
|---|
| 139 | list = N_GNEW(sz, pointf); | 
|---|
| 140 | lp = list; | 
|---|
| 141 | olp = old->list + (sz - 1); | 
|---|
| 142 | for (i = 0; i < sz; i++) {	/* reverse list of points */ | 
|---|
| 143 | *lp++ = *olp--; | 
|---|
| 144 | } | 
|---|
| 145 |  | 
|---|
| 146 | new->list = list; | 
|---|
| 147 | new->size = sz; | 
|---|
| 148 | new->sflag = old->eflag; | 
|---|
| 149 | new->eflag = old->sflag; | 
|---|
| 150 | new->sp = old->ep; | 
|---|
| 151 | new->ep = old->sp; | 
|---|
| 152 | } | 
|---|
| 153 |  | 
|---|
| 154 | /* swap_spline: | 
|---|
| 155 | */ | 
|---|
| 156 | static void swap_spline(splines * s) | 
|---|
| 157 | { | 
|---|
| 158 | bezier *list; | 
|---|
| 159 | bezier *lp; | 
|---|
| 160 | bezier *olp; | 
|---|
| 161 | int i, sz; | 
|---|
| 162 |  | 
|---|
| 163 | sz = s->size; | 
|---|
| 164 | list = N_GNEW(sz, bezier); | 
|---|
| 165 | lp = list; | 
|---|
| 166 | olp = s->list + (sz - 1); | 
|---|
| 167 | for (i = 0; i < sz; i++) {	/* reverse and swap list of beziers */ | 
|---|
| 168 | swap_bezier(olp--, lp++); | 
|---|
| 169 | } | 
|---|
| 170 |  | 
|---|
| 171 | /* free old structures */ | 
|---|
| 172 | for (i = 0; i < sz; i++) | 
|---|
| 173 | free(s->list[i].list); | 
|---|
| 174 | free(s->list); | 
|---|
| 175 |  | 
|---|
| 176 | s->list = list; | 
|---|
| 177 | } | 
|---|
| 178 |  | 
|---|
| 179 | /* edge_normalize: | 
|---|
| 180 | * Some back edges are reversed during layout and the reversed edge | 
|---|
| 181 | * is used to compute the spline. We would like to guarantee that | 
|---|
| 182 | * the order of control points always goes from tail to head, so | 
|---|
| 183 | * we reverse them if necessary. | 
|---|
| 184 | */ | 
|---|
| 185 | static void edge_normalize(graph_t * g) | 
|---|
| 186 | { | 
|---|
| 187 | edge_t *e; | 
|---|
| 188 | node_t *n; | 
|---|
| 189 |  | 
|---|
| 190 | for (n = agfstnode(g); n; n = agnxtnode(g, n)) { | 
|---|
| 191 | for (e = agfstout(g, n); e; e = agnxtout(g, e)) { | 
|---|
| 192 | if (sinfo.swapEnds(e) && ED_spl(e)) | 
|---|
| 193 | swap_spline(ED_spl(e)); | 
|---|
| 194 | } | 
|---|
| 195 | } | 
|---|
| 196 | } | 
|---|
| 197 |  | 
|---|
| 198 | /* resetRW: | 
|---|
| 199 | * In position, each node has its rw stored in mval and, | 
|---|
| 200 | * if a node is part of a loop, rw may be increased to | 
|---|
| 201 | * reflect the loops and associated labels. We restore | 
|---|
| 202 | * the original value here. | 
|---|
| 203 | */ | 
|---|
| 204 | static void | 
|---|
| 205 | resetRW (graph_t * g) | 
|---|
| 206 | { | 
|---|
| 207 | node_t* n; | 
|---|
| 208 |  | 
|---|
| 209 | for (n = agfstnode(g); n; n = agnxtnode(g,n)) { | 
|---|
| 210 | if (ND_other(n).list) { | 
|---|
| 211 | double tmp = ND_rw(n); | 
|---|
| 212 | ND_rw(n) = ND_mval(n); | 
|---|
| 213 | ND_mval(n) = tmp; | 
|---|
| 214 | } | 
|---|
| 215 | } | 
|---|
| 216 | } | 
|---|
| 217 |  | 
|---|
| 218 | /* setEdgeLabelPos: | 
|---|
| 219 | * Set edge label position information for regular and non-adjacent flat edges. | 
|---|
| 220 | * Dot has allocated space and position for these labels. This info will be | 
|---|
| 221 | * used when routing orthogonal edges. | 
|---|
| 222 | */ | 
|---|
| 223 | static void | 
|---|
| 224 | setEdgeLabelPos (graph_t * g) | 
|---|
| 225 | { | 
|---|
| 226 | node_t* n; | 
|---|
| 227 | textlabel_t* l; | 
|---|
| 228 |  | 
|---|
| 229 | /* place regular edge labels */ | 
|---|
| 230 | for (n = GD_nlist(g); n; n = ND_next(n)) { | 
|---|
| 231 | if (ND_node_type(n) == VIRTUAL) { | 
|---|
| 232 | if (ND_alg(n)) {   // label of non-adjacent flat edge | 
|---|
| 233 | edge_t* fe = (edge_t*)ND_alg(n); | 
|---|
| 234 | l = ED_label(fe); | 
|---|
| 235 | assert (l); | 
|---|
| 236 | l->pos = ND_coord(n); | 
|---|
| 237 | l->set = TRUE; | 
|---|
| 238 | } | 
|---|
| 239 | else if ((l = ND_label(n))) {// label of regular edge | 
|---|
| 240 | place_vnlabel(n); | 
|---|
| 241 | } | 
|---|
| 242 | if (l) updateBB(g, l); | 
|---|
| 243 | } | 
|---|
| 244 | } | 
|---|
| 245 | } | 
|---|
| 246 |  | 
|---|
| 247 | /* _dot_splines: | 
|---|
| 248 | * Main spline routing code. | 
|---|
| 249 | * The normalize parameter allows this function to be called by the | 
|---|
| 250 | * recursive call in make_flat_edge without normalization occurring, | 
|---|
| 251 | * so that the edge will only be normalized once in the top level call | 
|---|
| 252 | * of dot_splines. | 
|---|
| 253 | */ | 
|---|
| 254 | static void _dot_splines(graph_t * g, int normalize) | 
|---|
| 255 | { | 
|---|
| 256 | int i, j, k, n_nodes, n_edges, ind, cnt; | 
|---|
| 257 | node_t *n; | 
|---|
| 258 | Agedgeinfo_t fwdedgeai, fwdedgebi; | 
|---|
| 259 | Agedgepair_t fwdedgea, fwdedgeb; | 
|---|
| 260 | edge_t *e, *e0, *e1, *ea, *eb, *le0, *le1, **edges = NULL; | 
|---|
| 261 | path *P = NULL; | 
|---|
| 262 | spline_info_t sd; | 
|---|
| 263 | int et = EDGE_TYPE(g); | 
|---|
| 264 | fwdedgea.out.base.data = (Agrec_t*)&fwdedgeai; | 
|---|
| 265 | fwdedgeb.out.base.data = (Agrec_t*)&fwdedgebi; | 
|---|
| 266 |  | 
|---|
| 267 | if (et == ET_NONE) return; | 
|---|
| 268 | if (et == ET_CURVED) { | 
|---|
| 269 | resetRW (g); | 
|---|
| 270 | if (GD_has_labels(g->root) & EDGE_LABEL) { | 
|---|
| 271 | agerr (AGWARN, "edge labels with splines=curved not supported in dot - use xlabels\n"); | 
|---|
| 272 | } | 
|---|
| 273 | } | 
|---|
| 274 | #ifdef ORTHO | 
|---|
| 275 | if (et == ET_ORTHO) { | 
|---|
| 276 | resetRW (g); | 
|---|
| 277 | if (GD_has_labels(g->root) & EDGE_LABEL) { | 
|---|
| 278 | setEdgeLabelPos (g); | 
|---|
| 279 | orthoEdges (g, 1); | 
|---|
| 280 | } | 
|---|
| 281 | else | 
|---|
| 282 | orthoEdges (g, 0); | 
|---|
| 283 | goto finish; | 
|---|
| 284 | } | 
|---|
| 285 | #endif | 
|---|
| 286 |  | 
|---|
| 287 | mark_lowclusters(g); | 
|---|
| 288 | if (routesplinesinit()) return; | 
|---|
| 289 | P = NEW(path); | 
|---|
| 290 | /* FlatHeight = 2 * GD_nodesep(g); */ | 
|---|
| 291 | sd.Splinesep = GD_nodesep(g) / 4; | 
|---|
| 292 | sd.Multisep = GD_nodesep(g); | 
|---|
| 293 | edges = N_NEW(CHUNK, edge_t *); | 
|---|
| 294 |  | 
|---|
| 295 | /* compute boundaries and list of splines */ | 
|---|
| 296 | sd.LeftBound = sd.RightBound = 0; | 
|---|
| 297 | n_edges = n_nodes = 0; | 
|---|
| 298 | for (i = GD_minrank(g); i <= GD_maxrank(g); i++) { | 
|---|
| 299 | n_nodes += GD_rank(g)[i].n; | 
|---|
| 300 | if ((n = GD_rank(g)[i].v[0])) | 
|---|
| 301 | sd.LeftBound = MIN(sd.LeftBound, (ND_coord(n).x - ND_lw(n))); | 
|---|
| 302 | if (GD_rank(g)[i].n && (n = GD_rank(g)[i].v[GD_rank(g)[i].n - 1])) | 
|---|
| 303 | sd.RightBound = MAX(sd.RightBound, (ND_coord(n).x + ND_rw(n))); | 
|---|
| 304 | sd.LeftBound -= MINW; | 
|---|
| 305 | sd.RightBound += MINW; | 
|---|
| 306 |  | 
|---|
| 307 | for (j = 0; j < GD_rank(g)[i].n; j++) { | 
|---|
| 308 | n = GD_rank(g)[i].v[j]; | 
|---|
| 309 | /* if n is the label of a flat edge, copy its position to | 
|---|
| 310 | * the label. | 
|---|
| 311 | */ | 
|---|
| 312 | if (ND_alg(n)) { | 
|---|
| 313 | edge_t* fe = (edge_t*)ND_alg(n); | 
|---|
| 314 | assert (ED_label(fe)); | 
|---|
| 315 | ED_label(fe)->pos = ND_coord(n); | 
|---|
| 316 | ED_label(fe)->set = TRUE; | 
|---|
| 317 | } | 
|---|
| 318 | if ((ND_node_type(n) != NORMAL) && | 
|---|
| 319 | (sinfo.splineMerge(n) == FALSE)) | 
|---|
| 320 | continue; | 
|---|
| 321 | for (k = 0; (e = ND_out(n).list[k]); k++) { | 
|---|
| 322 | if ((ED_edge_type(e) == FLATORDER) | 
|---|
| 323 | || (ED_edge_type(e) == IGNORED)) | 
|---|
| 324 | continue; | 
|---|
| 325 | setflags(e, REGULAREDGE, FWDEDGE, MAINGRAPH); | 
|---|
| 326 | edges[n_edges++] = e; | 
|---|
| 327 | if (n_edges % CHUNK == 0) | 
|---|
| 328 | GROWEDGES; | 
|---|
| 329 | } | 
|---|
| 330 | if (ND_flat_out(n).list) | 
|---|
| 331 | for (k = 0; (e = ND_flat_out(n).list[k]); k++) { | 
|---|
| 332 | setflags(e, FLATEDGE, 0, AUXGRAPH); | 
|---|
| 333 | edges[n_edges++] = e; | 
|---|
| 334 | if (n_edges % CHUNK == 0) | 
|---|
| 335 | GROWEDGES; | 
|---|
| 336 | } | 
|---|
| 337 | if (ND_other(n).list) { | 
|---|
| 338 | /* In position, each node has its rw stored in mval and, | 
|---|
| 339 | * if a node is part of a loop, rw may be increased to | 
|---|
| 340 | * reflect the loops and associated labels. We restore | 
|---|
| 341 | * the original value here. | 
|---|
| 342 | */ | 
|---|
| 343 | if (ND_node_type(n) == NORMAL) { | 
|---|
| 344 | double tmp = ND_rw(n); | 
|---|
| 345 | ND_rw(n) = ND_mval(n); | 
|---|
| 346 | ND_mval(n) = tmp; | 
|---|
| 347 | } | 
|---|
| 348 | for (k = 0; (e = ND_other(n).list[k]); k++) { | 
|---|
| 349 | setflags(e, 0, 0, AUXGRAPH); | 
|---|
| 350 | edges[n_edges++] = e; | 
|---|
| 351 | if (n_edges % CHUNK == 0) | 
|---|
| 352 | GROWEDGES; | 
|---|
| 353 | } | 
|---|
| 354 | } | 
|---|
| 355 | } | 
|---|
| 356 | } | 
|---|
| 357 |  | 
|---|
| 358 | /* Sort so that equivalent edges are contiguous. | 
|---|
| 359 | * Equivalence should basically mean that 2 edges have the | 
|---|
| 360 | * same set {(tailnode,tailport),(headnode,headport)}, or | 
|---|
| 361 | * alternatively, the edges would be routed identically if | 
|---|
| 362 | * routed separately. | 
|---|
| 363 | */ | 
|---|
| 364 | qsort((char *) &edges[0], n_edges, sizeof(edges[0]), | 
|---|
| 365 | (qsort_cmpf) edgecmp); | 
|---|
| 366 |  | 
|---|
| 367 | /* FIXME: just how many boxes can there be? */ | 
|---|
| 368 | P->boxes = N_NEW(n_nodes + 20 * 2 * NSUB, boxf); | 
|---|
| 369 | sd.Rank_box = N_NEW(i, boxf); | 
|---|
| 370 |  | 
|---|
| 371 | if (et == ET_LINE) { | 
|---|
| 372 | /* place regular edge labels */ | 
|---|
| 373 | for (n = GD_nlist(g); n; n = ND_next(n)) { | 
|---|
| 374 | if ((ND_node_type(n) == VIRTUAL) && (ND_label(n))) { | 
|---|
| 375 | place_vnlabel(n); | 
|---|
| 376 | } | 
|---|
| 377 | } | 
|---|
| 378 | } | 
|---|
| 379 |  | 
|---|
| 380 | for (i = 0; i < n_edges;) { | 
|---|
| 381 | ind = i; | 
|---|
| 382 | le0 = getmainedge((e0 = edges[i++])); | 
|---|
| 383 | if (ED_tail_port(e0).defined || ED_head_port(e0).defined) { | 
|---|
| 384 | ea = e0; | 
|---|
| 385 | } else { | 
|---|
| 386 | ea =  le0; | 
|---|
| 387 | } | 
|---|
| 388 | if (ED_tree_index(ea) & BWDEDGE) { | 
|---|
| 389 | MAKEFWDEDGE(&fwdedgea.out, ea); | 
|---|
| 390 | ea = &fwdedgea.out; | 
|---|
| 391 | } | 
|---|
| 392 | for (cnt = 1; i < n_edges; cnt++, i++) { | 
|---|
| 393 | if (le0 != (le1 = getmainedge((e1 = edges[i])))) | 
|---|
| 394 | break; | 
|---|
| 395 | if (ED_adjacent(e0)) continue; /* all flat adjacent edges at once */ | 
|---|
| 396 | if (ED_tail_port(e1).defined || ED_head_port(e1).defined) { | 
|---|
| 397 | eb = e1; | 
|---|
| 398 | } else { | 
|---|
| 399 | eb = le1; | 
|---|
| 400 | } | 
|---|
| 401 | if (ED_tree_index(eb) & BWDEDGE) { | 
|---|
| 402 | MAKEFWDEDGE(&fwdedgeb.out, eb); | 
|---|
| 403 | eb = &fwdedgeb.out; | 
|---|
| 404 | } | 
|---|
| 405 | if (portcmp(ED_tail_port(ea), ED_tail_port(eb))) | 
|---|
| 406 | break; | 
|---|
| 407 | if (portcmp(ED_head_port(ea), ED_head_port(eb))) | 
|---|
| 408 | break; | 
|---|
| 409 | if ((ED_tree_index(e0) & EDGETYPEMASK) == FLATEDGE | 
|---|
| 410 | && ED_label(e0) != ED_label(e1)) | 
|---|
| 411 | break; | 
|---|
| 412 | if (ED_tree_index(edges[i]) & MAINGRAPH)	/* Aha! -C is on */ | 
|---|
| 413 | break; | 
|---|
| 414 | } | 
|---|
| 415 |  | 
|---|
| 416 | if (et == ET_CURVED) { | 
|---|
| 417 | int ii; | 
|---|
| 418 | edge_t* e0; | 
|---|
| 419 | edge_t** edgelist; | 
|---|
| 420 | if (cnt == 1) | 
|---|
| 421 | edgelist = &e0; | 
|---|
| 422 | else | 
|---|
| 423 | edgelist = N_NEW(cnt, edge_t*); | 
|---|
| 424 | edgelist[0] = getmainedge((edges+ind)[0]); | 
|---|
| 425 | for (ii = 1; ii < cnt; ii++) | 
|---|
| 426 | edgelist[ii] = (edges+ind)[ii]; | 
|---|
| 427 | makeStraightEdges (g, edgelist, cnt, et, &sinfo); | 
|---|
| 428 | if (cnt > 1) | 
|---|
| 429 | free (edgelist); | 
|---|
| 430 | } | 
|---|
| 431 | else if (agtail(e0) == aghead(e0)) { | 
|---|
| 432 | int b, sizey, r; | 
|---|
| 433 | n = agtail(e0); | 
|---|
| 434 | r = ND_rank(n); | 
|---|
| 435 | if (r == GD_maxrank(g)) { | 
|---|
| 436 | if (r > 0) | 
|---|
| 437 | sizey = ND_coord(GD_rank(g)[r-1].v[0]).y - ND_coord(n).y; | 
|---|
| 438 | else | 
|---|
| 439 | sizey = ND_ht(n); | 
|---|
| 440 | } | 
|---|
| 441 | else if (r == GD_minrank(g)) { | 
|---|
| 442 | sizey = ND_coord(n).y - ND_coord(GD_rank(g)[r+1].v[0]).y; | 
|---|
| 443 | } | 
|---|
| 444 | else { | 
|---|
| 445 | int upy = ND_coord(GD_rank(g)[r-1].v[0]).y - ND_coord(n).y; | 
|---|
| 446 | int dwny = ND_coord(n).y - ND_coord(GD_rank(g)[r+1].v[0]).y; | 
|---|
| 447 | sizey = MIN(upy, dwny); | 
|---|
| 448 | } | 
|---|
| 449 | makeSelfEdge(P, edges, ind, cnt, sd.Multisep, sizey/2, &sinfo); | 
|---|
| 450 | for (b = 0; b < cnt; b++) { | 
|---|
| 451 | e = edges[ind+b]; | 
|---|
| 452 | if (ED_label(e)) | 
|---|
| 453 | updateBB(g, ED_label(e)); | 
|---|
| 454 | } | 
|---|
| 455 | } | 
|---|
| 456 | else if (ND_rank(agtail(e0)) == ND_rank(aghead(e0))) { | 
|---|
| 457 | make_flat_edge(g, &sd, P, edges, ind, cnt, et); | 
|---|
| 458 | } | 
|---|
| 459 | else | 
|---|
| 460 | make_regular_edge(g, &sd, P, edges, ind, cnt, et); | 
|---|
| 461 | } | 
|---|
| 462 |  | 
|---|
| 463 | /* place regular edge labels */ | 
|---|
| 464 | for (n = GD_nlist(g); n; n = ND_next(n)) { | 
|---|
| 465 | if ((ND_node_type(n) == VIRTUAL) && (ND_label(n))) { | 
|---|
| 466 | place_vnlabel(n); | 
|---|
| 467 | updateBB(g, ND_label(n)); | 
|---|
| 468 | } | 
|---|
| 469 | } | 
|---|
| 470 |  | 
|---|
| 471 | /* normalize splines so they always go from tail to head */ | 
|---|
| 472 | /* place_portlabel relies on this being done first */ | 
|---|
| 473 | if (normalize) | 
|---|
| 474 | edge_normalize(g); | 
|---|
| 475 |  | 
|---|
| 476 | finish : | 
|---|
| 477 | /* vladimir: place port labels */ | 
|---|
| 478 | /* FIX: head and tail labels are not part of cluster bbox */ | 
|---|
| 479 | if ((E_headlabel || E_taillabel) && (E_labelangle || E_labeldistance)) { | 
|---|
| 480 | for (n = agfstnode(g); n; n = agnxtnode(g, n)) { | 
|---|
| 481 | if (E_headlabel) { | 
|---|
| 482 | for (e = agfstin(g, n); e; e = agnxtin(g, e)) | 
|---|
| 483 | if (ED_head_label(AGMKOUT(e))) { | 
|---|
| 484 | place_portlabel(AGMKOUT(e), TRUE); | 
|---|
| 485 | updateBB(g, ED_head_label(AGMKOUT(e))); | 
|---|
| 486 | } | 
|---|
| 487 |  | 
|---|
| 488 | } | 
|---|
| 489 | if (E_taillabel) { | 
|---|
| 490 | for (e = agfstout(g, n); e; e = agnxtout(g, e)) { | 
|---|
| 491 | if (ED_tail_label(e)) { | 
|---|
| 492 | if (place_portlabel(e, FALSE)) | 
|---|
| 493 | updateBB(g, ED_tail_label(e)); | 
|---|
| 494 | } | 
|---|
| 495 | } | 
|---|
| 496 | } | 
|---|
| 497 | } | 
|---|
| 498 | } | 
|---|
| 499 | /* end vladimir */ | 
|---|
| 500 |  | 
|---|
| 501 | #ifdef ORTHO | 
|---|
| 502 | if ((et != ET_ORTHO) && (et != ET_CURVED))  { | 
|---|
| 503 | #else | 
|---|
| 504 | if (et != ET_CURVED) { | 
|---|
| 505 | #endif | 
|---|
| 506 | free(edges); | 
|---|
| 507 | free(P->boxes); | 
|---|
| 508 | free(P); | 
|---|
| 509 | free(sd.Rank_box); | 
|---|
| 510 | routesplinesterm(); | 
|---|
| 511 | } | 
|---|
| 512 | State = GVSPLINES; | 
|---|
| 513 | EdgeLabelsDone = 1; | 
|---|
| 514 | } | 
|---|
| 515 |  | 
|---|
| 516 | /* dot_splines: | 
|---|
| 517 | * If the splines attribute is defined but equal to "", skip edge routing. | 
|---|
| 518 | */ | 
|---|
| 519 | void dot_splines(graph_t * g) | 
|---|
| 520 | { | 
|---|
| 521 | _dot_splines (g, 1); | 
|---|
| 522 | } | 
|---|
| 523 |  | 
|---|
| 524 | /* place_vnlabel: | 
|---|
| 525 | * assign position of an edge label from its virtual node | 
|---|
| 526 | * This is for regular edges only. | 
|---|
| 527 | */ | 
|---|
| 528 | static void | 
|---|
| 529 | place_vnlabel(node_t * n) | 
|---|
| 530 | { | 
|---|
| 531 | pointf dimen; | 
|---|
| 532 | double width; | 
|---|
| 533 | edge_t *e; | 
|---|
| 534 | if (ND_in(n).size == 0) | 
|---|
| 535 | return;			/* skip flat edge labels here */ | 
|---|
| 536 | for (e = ND_out(n).list[0]; ED_edge_type(e) != NORMAL; | 
|---|
| 537 | e = ED_to_orig(e)); | 
|---|
| 538 | dimen = ED_label(e)->dimen; | 
|---|
| 539 | width = GD_flip(agraphof(n)) ? dimen.y : dimen.x; | 
|---|
| 540 | ED_label(e)->pos.x = ND_coord(n).x + width / 2.0; | 
|---|
| 541 | ED_label(e)->pos.y = ND_coord(n).y; | 
|---|
| 542 | ED_label(e)->set = TRUE; | 
|---|
| 543 | } | 
|---|
| 544 |  | 
|---|
| 545 | static void | 
|---|
| 546 | setflags(edge_t *e, int hint1, int hint2, int f3) | 
|---|
| 547 | { | 
|---|
| 548 | int f1, f2; | 
|---|
| 549 | if (hint1 != 0) | 
|---|
| 550 | f1 = hint1; | 
|---|
| 551 | else { | 
|---|
| 552 | if (agtail(e) == aghead(e)) | 
|---|
| 553 | if (ED_tail_port(e).defined || ED_head_port(e).defined) | 
|---|
| 554 | f1 = SELFWPEDGE; | 
|---|
| 555 | else | 
|---|
| 556 | f1 = SELFNPEDGE; | 
|---|
| 557 | else if (ND_rank(agtail(e)) == ND_rank(aghead(e))) | 
|---|
| 558 | f1 = FLATEDGE; | 
|---|
| 559 | else | 
|---|
| 560 | f1 = REGULAREDGE; | 
|---|
| 561 | } | 
|---|
| 562 | if (hint2 != 0) | 
|---|
| 563 | f2 = hint2; | 
|---|
| 564 | else { | 
|---|
| 565 | if (f1 == REGULAREDGE) | 
|---|
| 566 | f2 = (ND_rank(agtail(e)) < ND_rank(aghead(e))) ? FWDEDGE : BWDEDGE; | 
|---|
| 567 | else if (f1 == FLATEDGE) | 
|---|
| 568 | f2 = (ND_order(agtail(e)) < ND_order(aghead(e))) ?  FWDEDGE : BWDEDGE; | 
|---|
| 569 | else			/* f1 == SELF*EDGE */ | 
|---|
| 570 | f2 = FWDEDGE; | 
|---|
| 571 | } | 
|---|
| 572 | ED_tree_index(e) = (f1 | f2 | f3); | 
|---|
| 573 | } | 
|---|
| 574 |  | 
|---|
| 575 | /* edgecmp: | 
|---|
| 576 | * lexicographically order edges by | 
|---|
| 577 | *  - edge type | 
|---|
| 578 | *  - |rank difference of nodes| | 
|---|
| 579 | *  - |x difference of nodes| | 
|---|
| 580 | *  - id of witness edge for equivalence class | 
|---|
| 581 | *  - port comparison | 
|---|
| 582 | *  - graph type | 
|---|
| 583 | *  - labels if flat edges | 
|---|
| 584 | *  - edge id | 
|---|
| 585 | */ | 
|---|
| 586 | static int edgecmp(edge_t** ptr0, edge_t** ptr1) | 
|---|
| 587 | { | 
|---|
| 588 | Agedgeinfo_t fwdedgeai, fwdedgebi; | 
|---|
| 589 | Agedgepair_t fwdedgea, fwdedgeb; | 
|---|
| 590 | edge_t *e0, *e1, *ea, *eb, *le0, *le1; | 
|---|
| 591 | int et0, et1, v0, v1, rv; | 
|---|
| 592 | double t0, t1; | 
|---|
| 593 |  | 
|---|
| 594 | fwdedgea.out.base.data = (Agrec_t*)&fwdedgeai; | 
|---|
| 595 | fwdedgeb.out.base.data = (Agrec_t*)&fwdedgebi; | 
|---|
| 596 | e0 = (edge_t *) * ptr0; | 
|---|
| 597 | e1 = (edge_t *) * ptr1; | 
|---|
| 598 | et0 = ED_tree_index(e0) & EDGETYPEMASK; | 
|---|
| 599 | et1 = ED_tree_index(e1) & EDGETYPEMASK; | 
|---|
| 600 | if (et0 != et1) | 
|---|
| 601 | return (et1 - et0); | 
|---|
| 602 |  | 
|---|
| 603 | le0 = getmainedge(e0); | 
|---|
| 604 | le1 = getmainedge(e1); | 
|---|
| 605 |  | 
|---|
| 606 | t0 = ND_rank(agtail(le0)) - ND_rank(aghead(le0)); | 
|---|
| 607 | t1 = ND_rank(agtail(le1)) - ND_rank(aghead(le1)); | 
|---|
| 608 | v0 = ABS((int)t0);   /* ugly, but explicit as to how we avoid equality tests on fp numbers */ | 
|---|
| 609 | v1 = ABS((int)t1); | 
|---|
| 610 | if (v0 != v1) | 
|---|
| 611 | return (v0 - v1); | 
|---|
| 612 |  | 
|---|
| 613 | t0 = ND_coord(agtail(le0)).x - ND_coord(aghead(le0)).x; | 
|---|
| 614 | t1 = ND_coord(agtail(le1)).x - ND_coord(aghead(le1)).x; | 
|---|
| 615 | v0 = ABS((int)t0); | 
|---|
| 616 | v1 = ABS((int)t1); | 
|---|
| 617 | if (v0 != v1) | 
|---|
| 618 | return (v0 - v1); | 
|---|
| 619 |  | 
|---|
| 620 | /* This provides a cheap test for edges having the same set of endpoints.  */ | 
|---|
| 621 | if (AGSEQ(le0) != AGSEQ(le1)) | 
|---|
| 622 | return (AGSEQ(le0) - AGSEQ(le1)); | 
|---|
| 623 |  | 
|---|
| 624 | ea = (ED_tail_port(e0).defined || ED_head_port(e0).defined) ? e0 : le0; | 
|---|
| 625 | if (ED_tree_index(ea) & BWDEDGE) { | 
|---|
| 626 | MAKEFWDEDGE(&fwdedgea.out, ea); | 
|---|
| 627 | ea = &fwdedgea.out; | 
|---|
| 628 | } | 
|---|
| 629 | eb = (ED_tail_port(e1).defined || ED_head_port(e1).defined) ? e1 : le1; | 
|---|
| 630 | if (ED_tree_index(eb) & BWDEDGE) { | 
|---|
| 631 | MAKEFWDEDGE(&fwdedgeb.out, eb); | 
|---|
| 632 | eb = &fwdedgeb.out; | 
|---|
| 633 | } | 
|---|
| 634 | if ((rv = portcmp(ED_tail_port(ea), ED_tail_port(eb)))) | 
|---|
| 635 | return rv; | 
|---|
| 636 | if ((rv = portcmp(ED_head_port(ea), ED_head_port(eb)))) | 
|---|
| 637 | return rv; | 
|---|
| 638 |  | 
|---|
| 639 | et0 = ED_tree_index(e0) & GRAPHTYPEMASK; | 
|---|
| 640 | et1 = ED_tree_index(e1) & GRAPHTYPEMASK; | 
|---|
| 641 | if (et0 != et1) | 
|---|
| 642 | return (et0 - et1); | 
|---|
| 643 |  | 
|---|
| 644 | if (et0 == FLATEDGE && ED_label(e0) != ED_label(e1)) | 
|---|
| 645 | return (int) (ED_label(e0) - ED_label(e1)); | 
|---|
| 646 |  | 
|---|
| 647 | return (AGSEQ(e0) - AGSEQ(e1)); | 
|---|
| 648 | } | 
|---|
| 649 |  | 
|---|
| 650 | /* cloneGraph: | 
|---|
| 651 | */ | 
|---|
| 652 | typedef struct { | 
|---|
| 653 | attrsym_t* E_constr; | 
|---|
| 654 | attrsym_t* E_samehead; | 
|---|
| 655 | attrsym_t* E_sametail; | 
|---|
| 656 | attrsym_t* E_weight; | 
|---|
| 657 | attrsym_t* E_minlen; | 
|---|
| 658 | attrsym_t* E_fontcolor; | 
|---|
| 659 | attrsym_t* E_fontname; | 
|---|
| 660 | attrsym_t* E_fontsize; | 
|---|
| 661 | attrsym_t* E_headclip; | 
|---|
| 662 | attrsym_t* E_headlabel; | 
|---|
| 663 | attrsym_t* E_label; | 
|---|
| 664 | attrsym_t* E_label_float; | 
|---|
| 665 | attrsym_t* E_labelfontcolor; | 
|---|
| 666 | attrsym_t* E_labelfontname; | 
|---|
| 667 | attrsym_t* E_labelfontsize; | 
|---|
| 668 | attrsym_t* E_tailclip; | 
|---|
| 669 | attrsym_t* E_taillabel; | 
|---|
| 670 | attrsym_t* E_xlabel; | 
|---|
| 671 |  | 
|---|
| 672 | attrsym_t* N_height; | 
|---|
| 673 | attrsym_t* N_width; | 
|---|
| 674 | attrsym_t* N_shape; | 
|---|
| 675 | attrsym_t* N_style; | 
|---|
| 676 | attrsym_t* N_fontsize; | 
|---|
| 677 | attrsym_t* N_fontname; | 
|---|
| 678 | attrsym_t* N_fontcolor; | 
|---|
| 679 | attrsym_t* N_label; | 
|---|
| 680 | attrsym_t* N_xlabel; | 
|---|
| 681 | attrsym_t* N_showboxes; | 
|---|
| 682 | attrsym_t* N_ordering; | 
|---|
| 683 | attrsym_t* N_sides; | 
|---|
| 684 | attrsym_t* N_peripheries; | 
|---|
| 685 | attrsym_t* N_skew; | 
|---|
| 686 | attrsym_t* N_orientation; | 
|---|
| 687 | attrsym_t* N_distortion; | 
|---|
| 688 | attrsym_t* N_fixed; | 
|---|
| 689 | attrsym_t* N_nojustify; | 
|---|
| 690 | attrsym_t* N_group; | 
|---|
| 691 |  | 
|---|
| 692 | attrsym_t* G_ordering; | 
|---|
| 693 | int        State; | 
|---|
| 694 | } attr_state_t; | 
|---|
| 695 |  | 
|---|
| 696 | static void | 
|---|
| 697 | setState (graph_t* auxg, attr_state_t* attr_state) | 
|---|
| 698 | { | 
|---|
| 699 | /* save state */ | 
|---|
| 700 | attr_state->E_constr = E_constr; | 
|---|
| 701 | attr_state->E_samehead = E_samehead; | 
|---|
| 702 | attr_state->E_sametail = E_sametail; | 
|---|
| 703 | attr_state->E_weight = E_weight; | 
|---|
| 704 | attr_state->E_minlen = E_minlen; | 
|---|
| 705 | attr_state->E_fontcolor = E_fontcolor; | 
|---|
| 706 | attr_state->E_fontname = E_fontname; | 
|---|
| 707 | attr_state->E_fontsize = E_fontsize; | 
|---|
| 708 | attr_state->E_headclip = E_headclip; | 
|---|
| 709 | attr_state->E_headlabel = E_headlabel; | 
|---|
| 710 | attr_state->E_label = E_label; | 
|---|
| 711 | attr_state->E_label_float = E_label_float; | 
|---|
| 712 | attr_state->E_labelfontcolor = E_labelfontcolor; | 
|---|
| 713 | attr_state->E_labelfontname = E_labelfontname; | 
|---|
| 714 | attr_state->E_labelfontsize = E_labelfontsize; | 
|---|
| 715 | attr_state->E_tailclip = E_tailclip; | 
|---|
| 716 | attr_state->E_taillabel = E_taillabel; | 
|---|
| 717 | attr_state->E_xlabel = E_xlabel; | 
|---|
| 718 | attr_state->N_height = N_height; | 
|---|
| 719 | attr_state->N_width = N_width; | 
|---|
| 720 | attr_state->N_shape = N_shape; | 
|---|
| 721 | attr_state->N_style = N_style; | 
|---|
| 722 | attr_state->N_fontsize = N_fontsize; | 
|---|
| 723 | attr_state->N_fontname = N_fontname; | 
|---|
| 724 | attr_state->N_fontcolor = N_fontcolor; | 
|---|
| 725 | attr_state->N_label = N_label; | 
|---|
| 726 | attr_state->N_xlabel = N_xlabel; | 
|---|
| 727 | attr_state->N_showboxes = N_showboxes; | 
|---|
| 728 | attr_state->N_ordering = N_ordering; | 
|---|
| 729 | attr_state->N_sides = N_sides; | 
|---|
| 730 | attr_state->N_peripheries = N_peripheries; | 
|---|
| 731 | attr_state->N_skew = N_skew; | 
|---|
| 732 | attr_state->N_orientation = N_orientation; | 
|---|
| 733 | attr_state->N_distortion = N_distortion; | 
|---|
| 734 | attr_state->N_fixed = N_fixed; | 
|---|
| 735 | attr_state->N_nojustify = N_nojustify; | 
|---|
| 736 | attr_state->N_group = N_group; | 
|---|
| 737 | attr_state->State = State; | 
|---|
| 738 | attr_state->G_ordering = G_ordering; | 
|---|
| 739 |  | 
|---|
| 740 | E_constr = NULL; | 
|---|
| 741 | E_samehead = agattr(auxg,AGEDGE, "samehead", NULL); | 
|---|
| 742 | E_sametail = agattr(auxg,AGEDGE, "sametail", NULL); | 
|---|
| 743 | E_weight = agattr(auxg,AGEDGE, "weight", NULL); | 
|---|
| 744 | if (!E_weight) | 
|---|
| 745 | E_weight = agattr (auxg,AGEDGE, "weight", ""); | 
|---|
| 746 | E_minlen = NULL; | 
|---|
| 747 | E_fontcolor = NULL; | 
|---|
| 748 | E_fontname = agfindedgeattr(auxg, "fontname"); | 
|---|
| 749 | E_fontsize = agfindedgeattr(auxg, "fontsize"); | 
|---|
| 750 | E_headclip = agfindedgeattr(auxg, "headclip"); | 
|---|
| 751 | E_headlabel = NULL; | 
|---|
| 752 | E_label = agfindedgeattr(auxg, "label"); | 
|---|
| 753 | E_label_float = agfindedgeattr(auxg, "label_float"); | 
|---|
| 754 | E_labelfontcolor = NULL; | 
|---|
| 755 | E_labelfontname = agfindedgeattr(auxg, "labelfontname"); | 
|---|
| 756 | E_labelfontsize = agfindedgeattr(auxg, "labelfontsize"); | 
|---|
| 757 | E_tailclip = agfindedgeattr(auxg, "tailclip"); | 
|---|
| 758 | E_taillabel = NULL; | 
|---|
| 759 | E_xlabel = NULL; | 
|---|
| 760 | N_height = agfindnodeattr(auxg, "height"); | 
|---|
| 761 | N_width = agfindnodeattr(auxg, "width"); | 
|---|
| 762 | N_shape = agfindnodeattr(auxg, "shape"); | 
|---|
| 763 | N_style = NULL; | 
|---|
| 764 | N_fontsize = agfindnodeattr(auxg, "fontsize"); | 
|---|
| 765 | N_fontname = agfindnodeattr(auxg, "fontname"); | 
|---|
| 766 | N_fontcolor = NULL; | 
|---|
| 767 | N_label = agfindnodeattr(auxg, "label"); | 
|---|
| 768 | N_xlabel = NULL; | 
|---|
| 769 | N_showboxes = NULL; | 
|---|
| 770 | N_ordering = agfindnodeattr(auxg, "ordering"); | 
|---|
| 771 | N_sides = agfindnodeattr(auxg, "sides"); | 
|---|
| 772 | N_peripheries = agfindnodeattr(auxg, "peripheries"); | 
|---|
| 773 | N_skew = agfindnodeattr(auxg, "skew"); | 
|---|
| 774 | N_orientation = agfindnodeattr(auxg, "orientation"); | 
|---|
| 775 | N_distortion = agfindnodeattr(auxg, "distortion"); | 
|---|
| 776 | N_fixed = agfindnodeattr(auxg, "fixed"); | 
|---|
| 777 | N_nojustify = NULL; | 
|---|
| 778 | N_group = NULL; | 
|---|
| 779 | G_ordering = agfindgraphattr (auxg, "ordering"); | 
|---|
| 780 | } | 
|---|
| 781 |  | 
|---|
| 782 | /* cloneGraph: | 
|---|
| 783 | * Create clone graph. It stores the global Agsyms, to be | 
|---|
| 784 | * restored in cleanupCloneGraph. The graph uses the main | 
|---|
| 785 | * graph's settings for certain geometry parameters, and | 
|---|
| 786 | * declares all node and edge attributes used in the original | 
|---|
| 787 | * graph. | 
|---|
| 788 | */ | 
|---|
| 789 | static graph_t* | 
|---|
| 790 | cloneGraph (graph_t* g, attr_state_t* attr_state) | 
|---|
| 791 | { | 
|---|
| 792 | Agsym_t* sym; | 
|---|
| 793 | graph_t* auxg; | 
|---|
| 794 | if (agisdirected(g)) | 
|---|
| 795 | auxg = agopen ( "auxg",Agdirected, NIL(Agdisc_t *)); | 
|---|
| 796 | else | 
|---|
| 797 | auxg = agopen ( "auxg",Agundirected, NIL(Agdisc_t *)); | 
|---|
| 798 | agbindrec(auxg, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE); | 
|---|
| 799 | agattr(auxg, AGRAPH, "rank", ""); | 
|---|
| 800 | GD_drawing(auxg) = NEW(layout_t); | 
|---|
| 801 | GD_drawing(auxg)->quantum = GD_drawing(g)->quantum; | 
|---|
| 802 | GD_drawing(auxg)->dpi = GD_drawing(g)->dpi; | 
|---|
| 803 |  | 
|---|
| 804 | GD_charset(auxg) = GD_charset (g); | 
|---|
| 805 | if (GD_flip(g)) | 
|---|
| 806 | SET_RANKDIR(auxg, RANKDIR_TB); | 
|---|
| 807 | else | 
|---|
| 808 | SET_RANKDIR(auxg, RANKDIR_LR); | 
|---|
| 809 | GD_nodesep(auxg) = GD_nodesep(g); | 
|---|
| 810 | GD_ranksep(auxg) = GD_ranksep(g); | 
|---|
| 811 |  | 
|---|
| 812 | //copy node attrs to auxg | 
|---|
| 813 | sym=agnxtattr(agroot(g),AGNODE,NULL); //get the first attr. | 
|---|
| 814 | for (; sym; sym = agnxtattr(agroot(g),AGNODE,sym)) | 
|---|
| 815 | agattr (auxg, AGNODE,sym->name, sym->defval); | 
|---|
| 816 |  | 
|---|
| 817 | //copy edge attributes | 
|---|
| 818 | sym=agnxtattr(agroot(g),AGEDGE,NULL); //get the first attr. | 
|---|
| 819 | for (; sym; sym = agnxtattr(agroot(g),AGEDGE,sym)) | 
|---|
| 820 | agattr (auxg, AGEDGE,sym->name, sym->defval); | 
|---|
| 821 |  | 
|---|
| 822 | if (!agattr(auxg,AGEDGE, "headport", NULL)) | 
|---|
| 823 | agattr(auxg,AGEDGE, "headport", ""); | 
|---|
| 824 | if (!agattr(auxg,AGEDGE, "tailport", NULL)) | 
|---|
| 825 | agattr(auxg,AGEDGE, "tailport", ""); | 
|---|
| 826 |  | 
|---|
| 827 | setState (auxg, attr_state); | 
|---|
| 828 |  | 
|---|
| 829 | return auxg; | 
|---|
| 830 | } | 
|---|
| 831 |  | 
|---|
| 832 | /* cleanupCloneGraph: | 
|---|
| 833 | */ | 
|---|
| 834 | static void | 
|---|
| 835 | cleanupCloneGraph (graph_t* g, attr_state_t* attr_state) | 
|---|
| 836 | { | 
|---|
| 837 | /* restore main graph syms */ | 
|---|
| 838 | E_constr = attr_state->E_constr; | 
|---|
| 839 | E_samehead = attr_state->E_samehead; | 
|---|
| 840 | E_sametail = attr_state->E_sametail; | 
|---|
| 841 | E_weight = attr_state->E_weight; | 
|---|
| 842 | E_minlen = attr_state->E_minlen; | 
|---|
| 843 | E_fontcolor = attr_state->E_fontcolor; | 
|---|
| 844 | E_fontname = attr_state->E_fontname; | 
|---|
| 845 | E_fontsize = attr_state->E_fontsize; | 
|---|
| 846 | E_headclip = attr_state->E_headclip; | 
|---|
| 847 | E_headlabel = attr_state->E_headlabel; | 
|---|
| 848 | E_label = attr_state->E_label; | 
|---|
| 849 | E_label_float = attr_state->E_label_float; | 
|---|
| 850 | E_labelfontcolor = attr_state->E_labelfontcolor; | 
|---|
| 851 | E_labelfontname = attr_state->E_labelfontname; | 
|---|
| 852 | E_labelfontsize = attr_state->E_labelfontsize; | 
|---|
| 853 | E_tailclip = attr_state->E_tailclip; | 
|---|
| 854 | E_taillabel = attr_state->E_taillabel; | 
|---|
| 855 | E_xlabel = attr_state->E_xlabel; | 
|---|
| 856 | N_height = attr_state->N_height; | 
|---|
| 857 | N_width = attr_state->N_width; | 
|---|
| 858 | N_shape = attr_state->N_shape; | 
|---|
| 859 | N_style = attr_state->N_style; | 
|---|
| 860 | N_fontsize = attr_state->N_fontsize; | 
|---|
| 861 | N_fontname = attr_state->N_fontname; | 
|---|
| 862 | N_fontcolor = attr_state->N_fontcolor; | 
|---|
| 863 | N_label = attr_state->N_label; | 
|---|
| 864 | N_xlabel = attr_state->N_xlabel; | 
|---|
| 865 | N_showboxes = attr_state->N_showboxes; | 
|---|
| 866 | N_ordering = attr_state->N_ordering; | 
|---|
| 867 | N_sides = attr_state->N_sides; | 
|---|
| 868 | N_peripheries = attr_state->N_peripheries; | 
|---|
| 869 | N_skew = attr_state->N_skew; | 
|---|
| 870 | N_orientation = attr_state->N_orientation; | 
|---|
| 871 | N_distortion = attr_state->N_distortion; | 
|---|
| 872 | N_fixed = attr_state->N_fixed; | 
|---|
| 873 | N_nojustify = attr_state->N_nojustify; | 
|---|
| 874 | N_group = attr_state->N_group; | 
|---|
| 875 | G_ordering = attr_state->G_ordering; | 
|---|
| 876 | State = attr_state->State; | 
|---|
| 877 |  | 
|---|
| 878 | free (attr_state); | 
|---|
| 879 | dot_cleanup(g); | 
|---|
| 880 | agclose(g); | 
|---|
| 881 | } | 
|---|
| 882 |  | 
|---|
| 883 | /* cloneNode: | 
|---|
| 884 | * If flipped is true, original graph has rankdir=LR or RL. | 
|---|
| 885 | * In this case, records change shape, so we wrap a record node's | 
|---|
| 886 | * label in "{...}" to prevent this. | 
|---|
| 887 | */ | 
|---|
| 888 | static node_t* | 
|---|
| 889 | cloneNode (graph_t* g, node_t* orign, int flipped) | 
|---|
| 890 | { | 
|---|
| 891 | node_t* n = agnode(g, agnameof(orign),1); | 
|---|
| 892 | agbindrec(n, "Agnodeinfo_t", sizeof(Agnodeinfo_t), TRUE); | 
|---|
| 893 | agcopyattr (orign, n); | 
|---|
| 894 | if (shapeOf(orign) == SH_RECORD) { | 
|---|
| 895 | int lbllen = strlen(ND_label(orign)->text); | 
|---|
| 896 | char* buf = N_GNEW(lbllen+3,char); | 
|---|
| 897 | sprintf (buf, "{%s}", ND_label(orign)->text); | 
|---|
| 898 | agset (n, "label", buf); | 
|---|
| 899 | } | 
|---|
| 900 |  | 
|---|
| 901 | return n; | 
|---|
| 902 | } | 
|---|
| 903 |  | 
|---|
| 904 | /* cloneEdge: | 
|---|
| 905 | */ | 
|---|
| 906 | static edge_t* | 
|---|
| 907 | cloneEdge (graph_t* g, node_t* tn, node_t* hn, edge_t* orig) | 
|---|
| 908 | { | 
|---|
| 909 | edge_t* e = agedge(g, tn, hn,NULL,1); | 
|---|
| 910 | /* for (; ED_edge_type(orig) != NORMAL; orig = ED_to_orig(orig)); */ | 
|---|
| 911 | agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), TRUE); | 
|---|
| 912 | agcopyattr (orig, e); | 
|---|
| 913 | /* | 
|---|
| 914 | if (orig->tail != ND_alg(tn)) { | 
|---|
| 915 | char* hdport = agget (orig, HEAD_ID); | 
|---|
| 916 | char* tlport = agget (orig, TAIL_ID); | 
|---|
| 917 | agset (e, TAIL_ID, (hdport ? hdport : "")); | 
|---|
| 918 | agset (e, HEAD_ID, (tlport ? tlport : "")); | 
|---|
| 919 | } | 
|---|
| 920 | */ | 
|---|
| 921 |  | 
|---|
| 922 | return e; | 
|---|
| 923 | } | 
|---|
| 924 |  | 
|---|
| 925 | /* transformf: | 
|---|
| 926 | * Rotate, if necessary, then translate points. | 
|---|
| 927 | */ | 
|---|
| 928 | static pointf | 
|---|
| 929 | transformf (pointf p, pointf del, int flip) | 
|---|
| 930 | { | 
|---|
| 931 | if (flip) { | 
|---|
| 932 | double i = p.x; | 
|---|
| 933 | p.x = p.y; | 
|---|
| 934 | p.y = -i; | 
|---|
| 935 | } | 
|---|
| 936 | return add_pointf(p, del); | 
|---|
| 937 | } | 
|---|
| 938 |  | 
|---|
| 939 | /* edgelblcmpfn: | 
|---|
| 940 | * lexicographically order edges by | 
|---|
| 941 | *  - has label | 
|---|
| 942 | *  - label is wider | 
|---|
| 943 | *  - label is higher | 
|---|
| 944 | */ | 
|---|
| 945 | static int edgelblcmpfn(edge_t** ptr0, edge_t** ptr1) | 
|---|
| 946 | { | 
|---|
| 947 | edge_t *e0, *e1; | 
|---|
| 948 | pointf sz0, sz1; | 
|---|
| 949 |  | 
|---|
| 950 | e0 = (edge_t *) * ptr0; | 
|---|
| 951 | e1 = (edge_t *) * ptr1; | 
|---|
| 952 |  | 
|---|
| 953 | if (ED_label(e0)) { | 
|---|
| 954 | if (ED_label(e1)) { | 
|---|
| 955 | sz0 = ED_label(e0)->dimen; | 
|---|
| 956 | sz1 = ED_label(e1)->dimen; | 
|---|
| 957 | if (sz0.x > sz1.x) return -1; | 
|---|
| 958 | else if (sz0.x < sz1.x) return 1; | 
|---|
| 959 | else if (sz0.y > sz1.y) return -1; | 
|---|
| 960 | else if (sz0.y < sz1.y) return 1; | 
|---|
| 961 | else return 0; | 
|---|
| 962 | } | 
|---|
| 963 | else | 
|---|
| 964 | return -1; | 
|---|
| 965 | } | 
|---|
| 966 | else if (ED_label(e1)) { | 
|---|
| 967 | return 1; | 
|---|
| 968 | } | 
|---|
| 969 | else | 
|---|
| 970 | return 0; | 
|---|
| 971 | } | 
|---|
| 972 |  | 
|---|
| 973 | #define LBL_SPACE  6  /* space between labels, in points */ | 
|---|
| 974 |  | 
|---|
| 975 | /* makeSimpleFlatLabels: | 
|---|
| 976 | * This handles the second simplest case for flat edges between | 
|---|
| 977 | * two adjacent nodes. We still invoke a dot on a rotated problem | 
|---|
| 978 | * to handle edges with ports. This usually works, but fails for | 
|---|
| 979 | * records because of their weird nature. | 
|---|
| 980 | */ | 
|---|
| 981 | static void | 
|---|
| 982 | makeSimpleFlatLabels (node_t* tn, node_t* hn, edge_t** edges, int ind, int cnt, int et, int n_lbls) | 
|---|
| 983 | { | 
|---|
| 984 | pointf *ps; | 
|---|
| 985 | Ppoly_t poly; | 
|---|
| 986 | int pn; | 
|---|
| 987 | edge_t* e = edges[ind]; | 
|---|
| 988 | pointf points[10], tp, hp; | 
|---|
| 989 | int i, pointn; | 
|---|
| 990 | double leftend, rightend, ctrx, ctry, miny, maxy; | 
|---|
| 991 | double uminx, umaxx; | 
|---|
| 992 | double lminx=0.0, lmaxx=0.0; | 
|---|
| 993 |  | 
|---|
| 994 | edge_t** earray = N_NEW(cnt, edge_t*); | 
|---|
| 995 |  | 
|---|
| 996 | for (i = 0; i < cnt; i++) { | 
|---|
| 997 | earray[i] = edges[ind + i]; | 
|---|
| 998 | } | 
|---|
| 999 |  | 
|---|
| 1000 | qsort (earray, cnt, sizeof(edge_t*), (qsort_cmpf) edgelblcmpfn); | 
|---|
| 1001 |  | 
|---|
| 1002 | tp = add_pointf(ND_coord(tn), ED_tail_port(e).p); | 
|---|
| 1003 | hp = add_pointf(ND_coord(hn), ED_head_port(e).p); | 
|---|
| 1004 |  | 
|---|
| 1005 | leftend = tp.x+ND_rw(tn); | 
|---|
| 1006 | rightend = hp.x-ND_lw(hn); | 
|---|
| 1007 | ctrx = (leftend + rightend)/2.0; | 
|---|
| 1008 |  | 
|---|
| 1009 | /* do first edge */ | 
|---|
| 1010 | e = earray[0]; | 
|---|
| 1011 | pointn = 0; | 
|---|
| 1012 | points[pointn++] = tp; | 
|---|
| 1013 | points[pointn++] = tp; | 
|---|
| 1014 | points[pointn++] = hp; | 
|---|
| 1015 | points[pointn++] = hp; | 
|---|
| 1016 | clip_and_install(e, aghead(e), points, pointn, &sinfo); | 
|---|
| 1017 | ED_label(e)->pos.x = ctrx; | 
|---|
| 1018 | ED_label(e)->pos.y = tp.y + (ED_label(e)->dimen.y+LBL_SPACE)/2.0; | 
|---|
| 1019 | ED_label(e)->set = TRUE; | 
|---|
| 1020 |  | 
|---|
| 1021 | miny = tp.y + LBL_SPACE/2.0; | 
|---|
| 1022 | maxy = miny + ED_label(e)->dimen.y; | 
|---|
| 1023 | uminx = ctrx - (ED_label(e)->dimen.x)/2.0; | 
|---|
| 1024 | umaxx = ctrx + (ED_label(e)->dimen.x)/2.0; | 
|---|
| 1025 |  | 
|---|
| 1026 | for (i = 1; i < n_lbls; i++) { | 
|---|
| 1027 | e = earray[i]; | 
|---|
| 1028 | if (i%2) {  /* down */ | 
|---|
| 1029 | if (i == 1) { | 
|---|
| 1030 | lminx = ctrx - (ED_label(e)->dimen.x)/2.0; | 
|---|
| 1031 | lmaxx = ctrx + (ED_label(e)->dimen.x)/2.0; | 
|---|
| 1032 | } | 
|---|
| 1033 | miny -= LBL_SPACE + ED_label(e)->dimen.y; | 
|---|
| 1034 | points[0] = tp; | 
|---|
| 1035 | points[1].x = tp.x; | 
|---|
| 1036 | points[1].y = miny - LBL_SPACE; | 
|---|
| 1037 | points[2].x = hp.x; | 
|---|
| 1038 | points[2].y = points[1].y; | 
|---|
| 1039 | points[3] = hp; | 
|---|
| 1040 | points[4].x = lmaxx; | 
|---|
| 1041 | points[4].y = hp.y; | 
|---|
| 1042 | points[5].x = lmaxx; | 
|---|
| 1043 | points[5].y = miny; | 
|---|
| 1044 | points[6].x = lminx; | 
|---|
| 1045 | points[6].y = miny; | 
|---|
| 1046 | points[7].x = lminx; | 
|---|
| 1047 | points[7].y = tp.y; | 
|---|
| 1048 | ctry = miny + (ED_label(e)->dimen.y)/2.0; | 
|---|
| 1049 | } | 
|---|
| 1050 | else {   /* up */ | 
|---|
| 1051 | points[0] = tp; | 
|---|
| 1052 | points[1].x = uminx; | 
|---|
| 1053 | points[1].y = tp.y; | 
|---|
| 1054 | points[2].x = uminx; | 
|---|
| 1055 | points[2].y = maxy; | 
|---|
| 1056 | points[3].x = umaxx; | 
|---|
| 1057 | points[3].y = maxy; | 
|---|
| 1058 | points[4].x = umaxx; | 
|---|
| 1059 | points[4].y = hp.y; | 
|---|
| 1060 | points[5].x = hp.x; | 
|---|
| 1061 | points[5].y = hp.y; | 
|---|
| 1062 | points[6].x = hp.x; | 
|---|
| 1063 | points[6].y = maxy + LBL_SPACE; | 
|---|
| 1064 | points[7].x = tp.x; | 
|---|
| 1065 | points[7].y = maxy + LBL_SPACE; | 
|---|
| 1066 | ctry =  maxy + (ED_label(e)->dimen.y)/2.0 + LBL_SPACE; | 
|---|
| 1067 | maxy += ED_label(e)->dimen.y + LBL_SPACE; | 
|---|
| 1068 | } | 
|---|
| 1069 | poly.pn = 8; | 
|---|
| 1070 | poly.ps = (Ppoint_t*)points; | 
|---|
| 1071 | ps = simpleSplineRoute (tp, hp, poly, &pn, et == ET_PLINE); | 
|---|
| 1072 | if (pn == 0) return; | 
|---|
| 1073 | ED_label(e)->pos.x = ctrx; | 
|---|
| 1074 | ED_label(e)->pos.y = ctry; | 
|---|
| 1075 | ED_label(e)->set = TRUE; | 
|---|
| 1076 | clip_and_install(e, aghead(e), ps, pn, &sinfo); | 
|---|
| 1077 | } | 
|---|
| 1078 |  | 
|---|
| 1079 | /* edges with no labels */ | 
|---|
| 1080 | for (; i < cnt; i++) { | 
|---|
| 1081 | e = earray[i]; | 
|---|
| 1082 | if (i%2) {  /* down */ | 
|---|
| 1083 | if (i == 1) { | 
|---|
| 1084 | lminx = (2*leftend + rightend)/3.0; | 
|---|
| 1085 | lmaxx = (leftend + 2*rightend)/3.0; | 
|---|
| 1086 | } | 
|---|
| 1087 | miny -= LBL_SPACE; | 
|---|
| 1088 | points[0] = tp; | 
|---|
| 1089 | points[1].x = tp.x; | 
|---|
| 1090 | points[1].y = miny - LBL_SPACE; | 
|---|
| 1091 | points[2].x = hp.x; | 
|---|
| 1092 | points[2].y = points[1].y; | 
|---|
| 1093 | points[3] = hp; | 
|---|
| 1094 | points[4].x = lmaxx; | 
|---|
| 1095 | points[4].y = hp.y; | 
|---|
| 1096 | points[5].x = lmaxx; | 
|---|
| 1097 | points[5].y = miny; | 
|---|
| 1098 | points[6].x = lminx; | 
|---|
| 1099 | points[6].y = miny; | 
|---|
| 1100 | points[7].x = lminx; | 
|---|
| 1101 | points[7].y = tp.y; | 
|---|
| 1102 | } | 
|---|
| 1103 | else {   /* up */ | 
|---|
| 1104 | points[0] = tp; | 
|---|
| 1105 | points[1].x = uminx; | 
|---|
| 1106 | points[1].y = tp.y; | 
|---|
| 1107 | points[2].x = uminx; | 
|---|
| 1108 | points[2].y = maxy; | 
|---|
| 1109 | points[3].x = umaxx; | 
|---|
| 1110 | points[3].y = maxy; | 
|---|
| 1111 | points[4].x = umaxx; | 
|---|
| 1112 | points[4].y = hp.y; | 
|---|
| 1113 | points[5].x = hp.x; | 
|---|
| 1114 | points[5].y = hp.y; | 
|---|
| 1115 | points[6].x = hp.x; | 
|---|
| 1116 | points[6].y = maxy + LBL_SPACE; | 
|---|
| 1117 | points[7].x = tp.x; | 
|---|
| 1118 | points[7].y = maxy + LBL_SPACE; | 
|---|
| 1119 | maxy += + LBL_SPACE; | 
|---|
| 1120 | } | 
|---|
| 1121 | poly.pn = 8; | 
|---|
| 1122 | poly.ps = (Ppoint_t*)points; | 
|---|
| 1123 | ps = simpleSplineRoute (tp, hp, poly, &pn, et == ET_PLINE); | 
|---|
| 1124 | if (pn == 0) return; | 
|---|
| 1125 | clip_and_install(e, aghead(e), ps, pn, &sinfo); | 
|---|
| 1126 | } | 
|---|
| 1127 |  | 
|---|
| 1128 | free (earray); | 
|---|
| 1129 | } | 
|---|
| 1130 |  | 
|---|
| 1131 | /* makeSimpleFlat: | 
|---|
| 1132 | */ | 
|---|
| 1133 | static void | 
|---|
| 1134 | makeSimpleFlat (node_t* tn, node_t* hn, edge_t** edges, int ind, int cnt, int et) | 
|---|
| 1135 | { | 
|---|
| 1136 | edge_t* e = edges[ind]; | 
|---|
| 1137 | pointf points[10], tp, hp; | 
|---|
| 1138 | int i, pointn; | 
|---|
| 1139 | double stepy, dy; | 
|---|
| 1140 |  | 
|---|
| 1141 | tp = add_pointf(ND_coord(tn), ED_tail_port(e).p); | 
|---|
| 1142 | hp = add_pointf(ND_coord(hn), ED_head_port(e).p); | 
|---|
| 1143 |  | 
|---|
| 1144 | stepy = (cnt > 1) ? ND_ht(tn) / (double)(cnt - 1) : 0.; | 
|---|
| 1145 | dy = tp.y - ((cnt > 1) ? ND_ht(tn) / 2. : 0.); | 
|---|
| 1146 |  | 
|---|
| 1147 | for (i = 0; i < cnt; i++) { | 
|---|
| 1148 | e = edges[ind + i]; | 
|---|
| 1149 | pointn = 0; | 
|---|
| 1150 | if ((et == ET_SPLINE) || (et == ET_LINE)) { | 
|---|
| 1151 | points[pointn++] = tp; | 
|---|
| 1152 | points[pointn++] = pointfof((2 * tp.x + hp.x) / 3, dy); | 
|---|
| 1153 | points[pointn++] = pointfof((2 * hp.x + tp.x) / 3, dy); | 
|---|
| 1154 | points[pointn++] = hp; | 
|---|
| 1155 | } | 
|---|
| 1156 | else {   /* ET_PLINE */ | 
|---|
| 1157 | points[pointn++] = tp; | 
|---|
| 1158 | points[pointn++] = tp; | 
|---|
| 1159 | points[pointn++] = pointfof((2 * tp.x + hp.x) / 3, dy); | 
|---|
| 1160 | points[pointn++] = pointfof((2 * tp.x + hp.x) / 3, dy); | 
|---|
| 1161 | points[pointn++] = pointfof((2 * tp.x + hp.x) / 3, dy); | 
|---|
| 1162 | points[pointn++] = pointfof((2 * hp.x + tp.x) / 3, dy); | 
|---|
| 1163 | points[pointn++] = pointfof((2 * hp.x + tp.x) / 3, dy); | 
|---|
| 1164 | points[pointn++] = pointfof((2 * hp.x + tp.x) / 3, dy); | 
|---|
| 1165 | points[pointn++] = hp; | 
|---|
| 1166 | points[pointn++] = hp; | 
|---|
| 1167 | } | 
|---|
| 1168 | dy += stepy; | 
|---|
| 1169 | clip_and_install(e, aghead(e), points, pointn, &sinfo); | 
|---|
| 1170 | } | 
|---|
| 1171 | } | 
|---|
| 1172 |  | 
|---|
| 1173 | /* make_flat_adj_edges: | 
|---|
| 1174 | * In the simple case, with no labels or ports, this creates a simple | 
|---|
| 1175 | * spindle of splines. | 
|---|
| 1176 | * If there are only labels, cobble something together. | 
|---|
| 1177 | * Otherwise, we run dot recursively on the 2 nodes and the edges, | 
|---|
| 1178 | * essentially using rankdir=LR, to get the needed spline info. | 
|---|
| 1179 | * This is probably to cute and fragile, and should be rewritten in a | 
|---|
| 1180 | * more straightforward and laborious fashion. | 
|---|
| 1181 | */ | 
|---|
| 1182 | static void | 
|---|
| 1183 | make_flat_adj_edges(graph_t* g, path* P, edge_t** edges, int ind, int cnt, edge_t* e0, | 
|---|
| 1184 | int et) | 
|---|
| 1185 | { | 
|---|
| 1186 | node_t* n; | 
|---|
| 1187 | node_t *tn, *hn; | 
|---|
| 1188 | edge_t* e; | 
|---|
| 1189 | int labels = 0, ports = 0; | 
|---|
| 1190 | graph_t* auxg; | 
|---|
| 1191 | graph_t* subg; | 
|---|
| 1192 | node_t *auxt, *auxh; | 
|---|
| 1193 | edge_t* auxe; | 
|---|
| 1194 | int     i, j, midx, midy, leftx, rightx; | 
|---|
| 1195 | pointf   del; | 
|---|
| 1196 | edge_t* hvye = NULL; | 
|---|
| 1197 | attr_state_t* attrs; | 
|---|
| 1198 | static int warned; | 
|---|
| 1199 |  | 
|---|
| 1200 | tn = agtail(e0), hn = aghead(e0); | 
|---|
| 1201 | if ((shapeOf(tn) == SH_RECORD) || (shapeOf(hn) == SH_RECORD)) { | 
|---|
| 1202 | if (!warned) { | 
|---|
| 1203 | warned = 1; | 
|---|
| 1204 | agerr (AGWARN, "flat edge between adjacent nodes one of which has a record shape - replace records with HTML-like labels\n"); | 
|---|
| 1205 | agerr(AGPREV, "  Edge %s %s %s\n", | 
|---|
| 1206 | agnameof(tn), agisdirected(g)? "->": "--", agnameof(hn)); | 
|---|
| 1207 |  | 
|---|
| 1208 | } | 
|---|
| 1209 | return; | 
|---|
| 1210 | } | 
|---|
| 1211 | for (i = 0; i < cnt; i++) { | 
|---|
| 1212 | e = edges[ind + i]; | 
|---|
| 1213 | if (ED_label(e)) labels++; | 
|---|
| 1214 | if (ED_tail_port(e).defined || ED_head_port(e).defined) ports = 1; | 
|---|
| 1215 | } | 
|---|
| 1216 |  | 
|---|
| 1217 | if (ports == 0) { | 
|---|
| 1218 | /* flat edges without ports and labels can go straight left to right */ | 
|---|
| 1219 | if (labels == 0) { | 
|---|
| 1220 | makeSimpleFlat (tn, hn, edges, ind, cnt, et); | 
|---|
| 1221 | } | 
|---|
| 1222 | /* flat edges without ports but with labels take more work */ | 
|---|
| 1223 | else { | 
|---|
| 1224 | makeSimpleFlatLabels (tn, hn, edges, ind, cnt, et, labels); | 
|---|
| 1225 | } | 
|---|
| 1226 | return; | 
|---|
| 1227 | } | 
|---|
| 1228 |  | 
|---|
| 1229 | attrs = NEW(attr_state_t); | 
|---|
| 1230 | auxg = cloneGraph (g, attrs); | 
|---|
| 1231 | subg = agsubg (auxg, "xxx",1); | 
|---|
| 1232 | agbindrec(subg, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE); | 
|---|
| 1233 | agset (subg, "rank", "source"); | 
|---|
| 1234 | rightx = ND_coord(hn).x; | 
|---|
| 1235 | leftx = ND_coord(tn).x; | 
|---|
| 1236 | if (GD_flip(g)) { | 
|---|
| 1237 | node_t* n; | 
|---|
| 1238 | n = tn; | 
|---|
| 1239 | tn = hn; | 
|---|
| 1240 | hn = n; | 
|---|
| 1241 | } | 
|---|
| 1242 | auxt = cloneNode(subg, tn, GD_flip(g)); | 
|---|
| 1243 | auxh = cloneNode(auxg, hn, GD_flip(g)); | 
|---|
| 1244 | for (i = 0; i < cnt; i++) { | 
|---|
| 1245 | e = edges[ind + i]; | 
|---|
| 1246 | for (; ED_edge_type(e) != NORMAL; e = ED_to_orig(e)); | 
|---|
| 1247 | if (agtail(e) == tn) | 
|---|
| 1248 | auxe = cloneEdge (auxg, auxt, auxh, e); | 
|---|
| 1249 | else | 
|---|
| 1250 | auxe = cloneEdge (auxg, auxh, auxt, e); | 
|---|
| 1251 | ED_alg(e) = auxe; | 
|---|
| 1252 | if (!hvye && !ED_tail_port(e).defined && !ED_head_port(e).defined) { | 
|---|
| 1253 | hvye = auxe; | 
|---|
| 1254 | ED_alg(hvye) = e; | 
|---|
| 1255 | } | 
|---|
| 1256 | } | 
|---|
| 1257 | if (!hvye) { | 
|---|
| 1258 | hvye = agedge (auxg, auxt, auxh,NULL,1); | 
|---|
| 1259 | } | 
|---|
| 1260 | agxset (hvye, E_weight, "10000"); | 
|---|
| 1261 | GD_gvc(auxg) = GD_gvc(g); | 
|---|
| 1262 | GD_dotroot(auxg) = auxg; | 
|---|
| 1263 | setEdgeType (auxg, et); | 
|---|
| 1264 | dot_init_node_edge(auxg); | 
|---|
| 1265 |  | 
|---|
| 1266 | dot_rank(auxg, 0); | 
|---|
| 1267 | dot_mincross(auxg, 0); | 
|---|
| 1268 | dot_position(auxg, 0); | 
|---|
| 1269 |  | 
|---|
| 1270 | /* reposition */ | 
|---|
| 1271 | midx = (ND_coord(tn).x - ND_rw(tn) + ND_coord(hn).x + ND_lw(hn))/2; | 
|---|
| 1272 | midy = (ND_coord(auxt).x + ND_coord(auxh).x)/2; | 
|---|
| 1273 | for (n = GD_nlist(auxg); n; n = ND_next(n)) { | 
|---|
| 1274 | if (n == auxt) { | 
|---|
| 1275 | ND_coord(n).y = rightx; | 
|---|
| 1276 | ND_coord(n).x = midy; | 
|---|
| 1277 | } | 
|---|
| 1278 | else if (n == auxh) { | 
|---|
| 1279 | ND_coord(n).y = leftx; | 
|---|
| 1280 | ND_coord(n).x = midy; | 
|---|
| 1281 | } | 
|---|
| 1282 | else ND_coord(n).y = midx; | 
|---|
| 1283 | } | 
|---|
| 1284 | dot_sameports(auxg); | 
|---|
| 1285 | _dot_splines(auxg, 0); | 
|---|
| 1286 | dotneato_postprocess(auxg); | 
|---|
| 1287 |  | 
|---|
| 1288 | /* copy splines */ | 
|---|
| 1289 | if (GD_flip(g)) { | 
|---|
| 1290 | del.x = ND_coord(tn).x - ND_coord(auxt).y; | 
|---|
| 1291 | del.y = ND_coord(tn).y + ND_coord(auxt).x; | 
|---|
| 1292 | } | 
|---|
| 1293 | else { | 
|---|
| 1294 | del.x = ND_coord(tn).x - ND_coord(auxt).x; | 
|---|
| 1295 | del.y = ND_coord(tn).y - ND_coord(auxt).y; | 
|---|
| 1296 | } | 
|---|
| 1297 | for (i = 0; i < cnt; i++) { | 
|---|
| 1298 | bezier* auxbz; | 
|---|
| 1299 | bezier* bz; | 
|---|
| 1300 |  | 
|---|
| 1301 | e = edges[ind + i]; | 
|---|
| 1302 | for (; ED_edge_type(e) != NORMAL; e = ED_to_orig(e)); | 
|---|
| 1303 | auxe = (edge_t*)ED_alg(e); | 
|---|
| 1304 | if ((auxe == hvye) & !ED_alg(auxe)) continue; /* pseudo-edge */ | 
|---|
| 1305 | auxbz = ED_spl(auxe)->list; | 
|---|
| 1306 | bz = new_spline(e, auxbz->size); | 
|---|
| 1307 | bz->sflag = auxbz->sflag; | 
|---|
| 1308 | bz->sp = transformf(auxbz->sp, del, GD_flip(g)); | 
|---|
| 1309 | bz->eflag = auxbz->eflag; | 
|---|
| 1310 | bz->ep = transformf(auxbz->ep, del, GD_flip(g)); | 
|---|
| 1311 | for (j = 0; j <  auxbz->size; ) { | 
|---|
| 1312 | pointf cp[4]; | 
|---|
| 1313 | cp[0] = bz->list[j] = transformf(auxbz->list[j], del, GD_flip(g)); | 
|---|
| 1314 | j++; | 
|---|
| 1315 | if ( j >= auxbz->size ) | 
|---|
| 1316 | break; | 
|---|
| 1317 | cp[1] = bz->list[j] = transformf(auxbz->list[j], del, GD_flip(g)); | 
|---|
| 1318 | j++; | 
|---|
| 1319 | cp[2] = bz->list[j] = transformf(auxbz->list[j], del, GD_flip(g)); | 
|---|
| 1320 | j++; | 
|---|
| 1321 | cp[3] = transformf(auxbz->list[j], del, GD_flip(g)); | 
|---|
| 1322 | update_bb_bz(&GD_bb(g), cp); | 
|---|
| 1323 | } | 
|---|
| 1324 | if (ED_label(e)) { | 
|---|
| 1325 | ED_label(e)->pos = transformf(ED_label(auxe)->pos, del, GD_flip(g)); | 
|---|
| 1326 | ED_label(e)->set = TRUE; | 
|---|
| 1327 | updateBB(g, ED_label(e)); | 
|---|
| 1328 | } | 
|---|
| 1329 | } | 
|---|
| 1330 |  | 
|---|
| 1331 | cleanupCloneGraph (auxg, attrs); | 
|---|
| 1332 | } | 
|---|
| 1333 |  | 
|---|
| 1334 | /* makeFlatEnd; | 
|---|
| 1335 | */ | 
|---|
| 1336 | static void | 
|---|
| 1337 | makeFlatEnd (graph_t* g, spline_info_t* sp, path* P, node_t* n, edge_t* e, pathend_t* endp, | 
|---|
| 1338 | boolean isBegin) | 
|---|
| 1339 | { | 
|---|
| 1340 | boxf b; | 
|---|
| 1341 |  | 
|---|
| 1342 | b = endp->nb = maximal_bbox(g, sp, n, NULL, e); | 
|---|
| 1343 | endp->sidemask = TOP; | 
|---|
| 1344 | if (isBegin) beginpath(P, e, FLATEDGE, endp, FALSE); | 
|---|
| 1345 | else endpath(P, e, FLATEDGE, endp, FALSE); | 
|---|
| 1346 | b.UR.y = endp->boxes[endp->boxn - 1].UR.y; | 
|---|
| 1347 | b.LL.y = endp->boxes[endp->boxn - 1].LL.y; | 
|---|
| 1348 | b = makeregularend(b, TOP, ND_coord(n).y + GD_rank(g)[ND_rank(n)].ht2); | 
|---|
| 1349 | if (b.LL.x < b.UR.x && b.LL.y < b.UR.y) | 
|---|
| 1350 | endp->boxes[endp->boxn++] = b; | 
|---|
| 1351 | } | 
|---|
| 1352 | /* makeBottomFlatEnd; | 
|---|
| 1353 | */ | 
|---|
| 1354 | static void | 
|---|
| 1355 | makeBottomFlatEnd (graph_t* g, spline_info_t* sp, path* P, node_t* n, edge_t* e, | 
|---|
| 1356 | pathend_t* endp, boolean isBegin) | 
|---|
| 1357 | { | 
|---|
| 1358 | boxf b; | 
|---|
| 1359 |  | 
|---|
| 1360 | b = endp->nb = maximal_bbox(g, sp, n, NULL, e); | 
|---|
| 1361 | endp->sidemask = BOTTOM; | 
|---|
| 1362 | if (isBegin) beginpath(P, e, FLATEDGE, endp, FALSE); | 
|---|
| 1363 | else endpath(P, e, FLATEDGE, endp, FALSE); | 
|---|
| 1364 | b.UR.y = endp->boxes[endp->boxn - 1].UR.y; | 
|---|
| 1365 | b.LL.y = endp->boxes[endp->boxn - 1].LL.y; | 
|---|
| 1366 | b = makeregularend(b, BOTTOM, ND_coord(n).y - GD_rank(g)[ND_rank(n)].ht2); | 
|---|
| 1367 | if (b.LL.x < b.UR.x && b.LL.y < b.UR.y) | 
|---|
| 1368 | endp->boxes[endp->boxn++] = b; | 
|---|
| 1369 | } | 
|---|
| 1370 |  | 
|---|
| 1371 |  | 
|---|
| 1372 | /* make_flat_labeled_edge: | 
|---|
| 1373 | */ | 
|---|
| 1374 | static void | 
|---|
| 1375 | make_flat_labeled_edge(graph_t* g, spline_info_t* sp, path* P, edge_t* e, int et) | 
|---|
| 1376 | { | 
|---|
| 1377 | node_t *tn, *hn, *ln; | 
|---|
| 1378 | pointf *ps; | 
|---|
| 1379 | pathend_t tend, hend; | 
|---|
| 1380 | boxf lb; | 
|---|
| 1381 | int boxn, i, pn, ydelta; | 
|---|
| 1382 | edge_t *f; | 
|---|
| 1383 | pointf points[7]; | 
|---|
| 1384 |  | 
|---|
| 1385 | tn = agtail(e); | 
|---|
| 1386 | hn = aghead(e); | 
|---|
| 1387 |  | 
|---|
| 1388 | for (f = ED_to_virt(e); ED_to_virt(f); f = ED_to_virt(f)); | 
|---|
| 1389 | ln = agtail(f); | 
|---|
| 1390 | ED_label(e)->pos = ND_coord(ln); | 
|---|
| 1391 | ED_label(e)->set = TRUE; | 
|---|
| 1392 |  | 
|---|
| 1393 | if (et == ET_LINE) { | 
|---|
| 1394 | pointf startp, endp, lp; | 
|---|
| 1395 |  | 
|---|
| 1396 | startp = add_pointf(ND_coord(tn), ED_tail_port(e).p); | 
|---|
| 1397 | endp = add_pointf(ND_coord(hn), ED_head_port(e).p); | 
|---|
| 1398 |  | 
|---|
| 1399 | lp = ED_label(e)->pos; | 
|---|
| 1400 | lp.y -= (ED_label(e)->dimen.y)/2.0; | 
|---|
| 1401 | points[1] = points[0] = startp; | 
|---|
| 1402 | points[2] = points[3] = points[4] = lp; | 
|---|
| 1403 | points[5] = points[6] = endp; | 
|---|
| 1404 | ps = points; | 
|---|
| 1405 | pn = 7; | 
|---|
| 1406 | } | 
|---|
| 1407 | else { | 
|---|
| 1408 | lb.LL.x = ND_coord(ln).x - ND_lw(ln); | 
|---|
| 1409 | lb.UR.x = ND_coord(ln).x + ND_rw(ln); | 
|---|
| 1410 | lb.UR.y = ND_coord(ln).y + ND_ht(ln)/2; | 
|---|
| 1411 | ydelta = ND_coord(ln).y - GD_rank(g)[ND_rank(tn)].ht1 - | 
|---|
| 1412 | ND_coord(tn).y + GD_rank(g)[ND_rank(tn)].ht2; | 
|---|
| 1413 | ydelta /= 6.; | 
|---|
| 1414 | lb.LL.y = lb.UR.y - MAX(5.,ydelta); | 
|---|
| 1415 |  | 
|---|
| 1416 | boxn = 0; | 
|---|
| 1417 | makeFlatEnd (g, sp, P, tn, e, &tend, TRUE); | 
|---|
| 1418 | makeFlatEnd (g, sp, P, hn, e, &hend, FALSE); | 
|---|
| 1419 |  | 
|---|
| 1420 | boxes[boxn].LL.x = tend.boxes[tend.boxn - 1].LL.x; | 
|---|
| 1421 | boxes[boxn].LL.y = tend.boxes[tend.boxn - 1].UR.y; | 
|---|
| 1422 | boxes[boxn].UR.x = lb.LL.x; | 
|---|
| 1423 | boxes[boxn].UR.y = lb.LL.y; | 
|---|
| 1424 | boxn++; | 
|---|
| 1425 | boxes[boxn].LL.x = tend.boxes[tend.boxn - 1].LL.x; | 
|---|
| 1426 | boxes[boxn].LL.y = lb.LL.y; | 
|---|
| 1427 | boxes[boxn].UR.x = hend.boxes[hend.boxn - 1].UR.x; | 
|---|
| 1428 | boxes[boxn].UR.y = lb.UR.y; | 
|---|
| 1429 | boxn++; | 
|---|
| 1430 | boxes[boxn].LL.x = lb.UR.x; | 
|---|
| 1431 | boxes[boxn].UR.y = lb.LL.y; | 
|---|
| 1432 | boxes[boxn].LL.y = hend.boxes[hend.boxn - 1].UR.y; | 
|---|
| 1433 | boxes[boxn].UR.x = hend.boxes[hend.boxn - 1].UR.x; | 
|---|
| 1434 | boxn++; | 
|---|
| 1435 |  | 
|---|
| 1436 | for (i = 0; i < tend.boxn; i++) add_box(P, tend.boxes[i]); | 
|---|
| 1437 | for (i = 0; i < boxn; i++) add_box(P, boxes[i]); | 
|---|
| 1438 | for (i = hend.boxn - 1; i >= 0; i--) add_box(P, hend.boxes[i]); | 
|---|
| 1439 |  | 
|---|
| 1440 | if (et == ET_SPLINE) ps = routesplines(P, &pn); | 
|---|
| 1441 | else ps = routepolylines(P, &pn); | 
|---|
| 1442 | if (pn == 0) return; | 
|---|
| 1443 | } | 
|---|
| 1444 | clip_and_install(e, aghead(e), ps, pn, &sinfo); | 
|---|
| 1445 | } | 
|---|
| 1446 |  | 
|---|
| 1447 | /* make_flat_bottom_edges: | 
|---|
| 1448 | */ | 
|---|
| 1449 | static void | 
|---|
| 1450 | make_flat_bottom_edges(graph_t* g, spline_info_t* sp, path * P, edge_t ** edges, int | 
|---|
| 1451 | ind, int cnt, edge_t* e, int splines) | 
|---|
| 1452 | { | 
|---|
| 1453 | node_t *tn, *hn; | 
|---|
| 1454 | int j, i, r; | 
|---|
| 1455 | double stepx, stepy, vspace; | 
|---|
| 1456 | rank_t* nextr; | 
|---|
| 1457 | int pn; | 
|---|
| 1458 | pointf *ps; | 
|---|
| 1459 | pathend_t tend, hend; | 
|---|
| 1460 |  | 
|---|
| 1461 | tn = agtail(e); | 
|---|
| 1462 | hn = aghead(e); | 
|---|
| 1463 | r = ND_rank(tn); | 
|---|
| 1464 | if (r < GD_maxrank(g)) { | 
|---|
| 1465 | nextr = GD_rank(g) + (r+1); | 
|---|
| 1466 | vspace = ND_coord(tn).y - GD_rank(g)[r].pht1 - | 
|---|
| 1467 | (ND_coord(nextr->v[0]).y + nextr->pht2); | 
|---|
| 1468 | } | 
|---|
| 1469 | else { | 
|---|
| 1470 | vspace = GD_ranksep(g); | 
|---|
| 1471 | } | 
|---|
| 1472 | stepx = ((double)(sp->Multisep)) / (cnt+1); | 
|---|
| 1473 | stepy = vspace / (cnt+1); | 
|---|
| 1474 |  | 
|---|
| 1475 | makeBottomFlatEnd (g, sp, P, tn, e, &tend, TRUE); | 
|---|
| 1476 | makeBottomFlatEnd (g, sp, P, hn, e, &hend, FALSE); | 
|---|
| 1477 |  | 
|---|
| 1478 | for (i = 0; i < cnt; i++) { | 
|---|
| 1479 | int boxn; | 
|---|
| 1480 | boxf b; | 
|---|
| 1481 | e = edges[ind + i]; | 
|---|
| 1482 | boxn = 0; | 
|---|
| 1483 |  | 
|---|
| 1484 | b = tend.boxes[tend.boxn - 1]; | 
|---|
| 1485 | boxes[boxn].LL.x = b.LL.x; | 
|---|
| 1486 | boxes[boxn].UR.y = b.LL.y; | 
|---|
| 1487 | boxes[boxn].UR.x = b.UR.x + (i + 1) * stepx; | 
|---|
| 1488 | boxes[boxn].LL.y = b.LL.y - (i + 1) * stepy; | 
|---|
| 1489 | boxn++; | 
|---|
| 1490 | boxes[boxn].LL.x = tend.boxes[tend.boxn - 1].LL.x; | 
|---|
| 1491 | boxes[boxn].UR.y = boxes[boxn-1].LL.y; | 
|---|
| 1492 | boxes[boxn].UR.x = hend.boxes[hend.boxn - 1].UR.x; | 
|---|
| 1493 | boxes[boxn].LL.y = boxes[boxn].UR.y - stepy; | 
|---|
| 1494 | boxn++; | 
|---|
| 1495 | b = hend.boxes[hend.boxn - 1]; | 
|---|
| 1496 | boxes[boxn].UR.x = b.UR.x; | 
|---|
| 1497 | boxes[boxn].UR.y = b.LL.y; | 
|---|
| 1498 | boxes[boxn].LL.x = b.LL.x - (i + 1) * stepx; | 
|---|
| 1499 | boxes[boxn].LL.y = boxes[boxn-1].UR.y; | 
|---|
| 1500 | boxn++; | 
|---|
| 1501 |  | 
|---|
| 1502 | for (j = 0; j < tend.boxn; j++) add_box(P, tend.boxes[j]); | 
|---|
| 1503 | for (j = 0; j < boxn; j++) add_box(P, boxes[j]); | 
|---|
| 1504 | for (j = hend.boxn - 1; j >= 0; j--) add_box(P, hend.boxes[j]); | 
|---|
| 1505 |  | 
|---|
| 1506 | if (splines) ps = routesplines(P, &pn); | 
|---|
| 1507 | else ps = routepolylines(P, &pn); | 
|---|
| 1508 | if (pn == 0) | 
|---|
| 1509 | return; | 
|---|
| 1510 | clip_and_install(e, aghead(e), ps, pn, &sinfo); | 
|---|
| 1511 | P->nbox = 0; | 
|---|
| 1512 | } | 
|---|
| 1513 | } | 
|---|
| 1514 |  | 
|---|
| 1515 | /* make_flat_edge: | 
|---|
| 1516 | * Construct flat edges edges[ind...ind+cnt-1] | 
|---|
| 1517 | * There are 4 main cases: | 
|---|
| 1518 | *  - all edges between a and b where a and b are adjacent | 
|---|
| 1519 | *  - one labeled edge | 
|---|
| 1520 | *  - all non-labeled edges with identical ports between non-adjacent a and b | 
|---|
| 1521 | *     = connecting bottom to bottom/left/right - route along bottom | 
|---|
| 1522 | *     = the rest - route along top | 
|---|
| 1523 | */ | 
|---|
| 1524 | static void | 
|---|
| 1525 | make_flat_edge(graph_t* g, spline_info_t* sp, path * P, edge_t ** edges, int ind, int cnt, int et) | 
|---|
| 1526 | { | 
|---|
| 1527 | node_t *tn, *hn; | 
|---|
| 1528 | Agedgeinfo_t fwdedgei; | 
|---|
| 1529 | Agedgepair_t fwdedge; | 
|---|
| 1530 | edge_t *e; | 
|---|
| 1531 | int j, i, r, isAdjacent; | 
|---|
| 1532 | double stepx, stepy, vspace; | 
|---|
| 1533 | int tside, hside, pn; | 
|---|
| 1534 | pointf *ps; | 
|---|
| 1535 | pathend_t tend, hend; | 
|---|
| 1536 |  | 
|---|
| 1537 | fwdedge.out.base.data = (Agrec_t*)&fwdedgei; | 
|---|
| 1538 |  | 
|---|
| 1539 | /* Get sample edge; normalize to go from left to right */ | 
|---|
| 1540 | e = edges[ind]; | 
|---|
| 1541 | isAdjacent = ED_adjacent(e); | 
|---|
| 1542 | if (ED_tree_index(e) & BWDEDGE) { | 
|---|
| 1543 | MAKEFWDEDGE(&fwdedge.out, e); | 
|---|
| 1544 | e = &fwdedge.out; | 
|---|
| 1545 | } | 
|---|
| 1546 | for (i = 1; i < cnt; i++) { | 
|---|
| 1547 | if (ED_adjacent(edges[ind+i])) { | 
|---|
| 1548 | isAdjacent = 1; | 
|---|
| 1549 | break; | 
|---|
| 1550 | } | 
|---|
| 1551 | } | 
|---|
| 1552 | /* The lead edge edges[ind] might not have been marked earlier as adjacent, | 
|---|
| 1553 | * so check them all. | 
|---|
| 1554 | */ | 
|---|
| 1555 | if (isAdjacent) { | 
|---|
| 1556 | make_flat_adj_edges (g, P, edges, ind, cnt, e, et); | 
|---|
| 1557 | return; | 
|---|
| 1558 | } | 
|---|
| 1559 | if (ED_label(e)) {  /* edges with labels aren't multi-edges */ | 
|---|
| 1560 | make_flat_labeled_edge (g, sp, P, e, et); | 
|---|
| 1561 | return; | 
|---|
| 1562 | } | 
|---|
| 1563 |  | 
|---|
| 1564 | if (et == ET_LINE) { | 
|---|
| 1565 | makeSimpleFlat (agtail(e), aghead(e), edges, ind, cnt, et); | 
|---|
| 1566 | return; | 
|---|
| 1567 | } | 
|---|
| 1568 |  | 
|---|
| 1569 | tside = ED_tail_port(e).side; | 
|---|
| 1570 | hside = ED_head_port(e).side; | 
|---|
| 1571 | if (((tside == BOTTOM) && (hside != TOP)) || | 
|---|
| 1572 | ((hside == BOTTOM) && (tside != TOP))) { | 
|---|
| 1573 | make_flat_bottom_edges (g, sp, P, edges, ind, cnt, e, et == ET_SPLINE); | 
|---|
| 1574 | return; | 
|---|
| 1575 | } | 
|---|
| 1576 |  | 
|---|
| 1577 | tn = agtail(e); | 
|---|
| 1578 | hn = aghead(e); | 
|---|
| 1579 | r = ND_rank(tn); | 
|---|
| 1580 | if (r > 0) { | 
|---|
| 1581 | rank_t* prevr; | 
|---|
| 1582 | if (GD_has_labels(g->root) & EDGE_LABEL) | 
|---|
| 1583 | prevr = GD_rank(g) + (r-2); | 
|---|
| 1584 | else | 
|---|
| 1585 | prevr = GD_rank(g) + (r-1); | 
|---|
| 1586 | vspace = ND_coord(prevr->v[0]).y - prevr->ht1 - ND_coord(tn).y - GD_rank(g)[r].ht2; | 
|---|
| 1587 | } | 
|---|
| 1588 | else { | 
|---|
| 1589 | vspace = GD_ranksep(g); | 
|---|
| 1590 | } | 
|---|
| 1591 | stepx = ((double)sp->Multisep) / (cnt+1); | 
|---|
| 1592 | stepy = vspace / (cnt+1); | 
|---|
| 1593 |  | 
|---|
| 1594 | makeFlatEnd (g, sp, P, tn, e, &tend, TRUE); | 
|---|
| 1595 | makeFlatEnd (g, sp, P, hn, e, &hend, FALSE); | 
|---|
| 1596 |  | 
|---|
| 1597 | for (i = 0; i < cnt; i++) { | 
|---|
| 1598 | int boxn; | 
|---|
| 1599 | boxf b; | 
|---|
| 1600 | e = edges[ind + i]; | 
|---|
| 1601 | boxn = 0; | 
|---|
| 1602 |  | 
|---|
| 1603 | b = tend.boxes[tend.boxn - 1]; | 
|---|
| 1604 | boxes[boxn].LL.x = b.LL.x; | 
|---|
| 1605 | boxes[boxn].LL.y = b.UR.y; | 
|---|
| 1606 | boxes[boxn].UR.x = b.UR.x + (i + 1) * stepx; | 
|---|
| 1607 | boxes[boxn].UR.y = b.UR.y + (i + 1) * stepy; | 
|---|
| 1608 | boxn++; | 
|---|
| 1609 | boxes[boxn].LL.x = tend.boxes[tend.boxn - 1].LL.x; | 
|---|
| 1610 | boxes[boxn].LL.y = boxes[boxn-1].UR.y; | 
|---|
| 1611 | boxes[boxn].UR.x = hend.boxes[hend.boxn - 1].UR.x; | 
|---|
| 1612 | boxes[boxn].UR.y = boxes[boxn].LL.y + stepy; | 
|---|
| 1613 | boxn++; | 
|---|
| 1614 | b = hend.boxes[hend.boxn - 1]; | 
|---|
| 1615 | boxes[boxn].UR.x = b.UR.x; | 
|---|
| 1616 | boxes[boxn].LL.y = b.UR.y; | 
|---|
| 1617 | boxes[boxn].LL.x = b.LL.x - (i + 1) * stepx; | 
|---|
| 1618 | boxes[boxn].UR.y = boxes[boxn-1].LL.y; | 
|---|
| 1619 | boxn++; | 
|---|
| 1620 |  | 
|---|
| 1621 | for (j = 0; j < tend.boxn; j++) add_box(P, tend.boxes[j]); | 
|---|
| 1622 | for (j = 0; j < boxn; j++) add_box(P, boxes[j]); | 
|---|
| 1623 | for (j = hend.boxn - 1; j >= 0; j--) add_box(P, hend.boxes[j]); | 
|---|
| 1624 |  | 
|---|
| 1625 | if (et == ET_SPLINE) ps = routesplines(P, &pn); | 
|---|
| 1626 | else ps = routepolylines(P, &pn); | 
|---|
| 1627 | if (pn == 0) | 
|---|
| 1628 | return; | 
|---|
| 1629 | clip_and_install(e, aghead(e), ps, pn, &sinfo); | 
|---|
| 1630 | P->nbox = 0; | 
|---|
| 1631 | } | 
|---|
| 1632 | } | 
|---|
| 1633 |  | 
|---|
| 1634 | /* ccw: | 
|---|
| 1635 | * Return true if p3 is to left of ray p1->p2 | 
|---|
| 1636 | */ | 
|---|
| 1637 | static int | 
|---|
| 1638 | leftOf (pointf p1, pointf p2, pointf p3) | 
|---|
| 1639 | { | 
|---|
| 1640 | int d; | 
|---|
| 1641 |  | 
|---|
| 1642 | d = ((p1.y - p2.y) * (p3.x - p2.x)) - | 
|---|
| 1643 | ((p3.y - p2.y) * (p1.x - p2.x)); | 
|---|
| 1644 | return (d > 0); | 
|---|
| 1645 | } | 
|---|
| 1646 |  | 
|---|
| 1647 | /* makeLineEdge: | 
|---|
| 1648 | * Create an edge as line segment. We guarantee that the points | 
|---|
| 1649 | * are always drawn downwards. This means that for flipped edges, | 
|---|
| 1650 | * we draw from the head to the tail. The routine returns the | 
|---|
| 1651 | * end node of the edge in *hp. The points are stored in the | 
|---|
| 1652 | * given array of points, and the number of points is returned. | 
|---|
| 1653 | * | 
|---|
| 1654 | * If the edge has a label, the edge is draw as two segments, with | 
|---|
| 1655 | * the bend near the label. | 
|---|
| 1656 | * | 
|---|
| 1657 | * If the endpoints are on adjacent ranks, revert to usual code by | 
|---|
| 1658 | * returning 0. | 
|---|
| 1659 | * This is done because the usual code handles the interaction of | 
|---|
| 1660 | * multiple edges better. | 
|---|
| 1661 | */ | 
|---|
| 1662 | static int | 
|---|
| 1663 | makeLineEdge(graph_t* g, edge_t* fe, pointf* points, node_t** hp) | 
|---|
| 1664 | { | 
|---|
| 1665 | int delr, pn; | 
|---|
| 1666 | node_t* hn; | 
|---|
| 1667 | node_t* tn; | 
|---|
| 1668 | edge_t* e = fe; | 
|---|
| 1669 | pointf startp, endp, lp; | 
|---|
| 1670 | pointf dimen; | 
|---|
| 1671 | double width, height; | 
|---|
| 1672 |  | 
|---|
| 1673 | while (ED_edge_type(e) != NORMAL) | 
|---|
| 1674 | e = ED_to_orig(e); | 
|---|
| 1675 | hn = aghead(e); | 
|---|
| 1676 | tn = agtail(e); | 
|---|
| 1677 | delr = ABS(ND_rank(hn)-ND_rank(tn)); | 
|---|
| 1678 | if ((delr == 1) || ((delr == 2) && (GD_has_labels(g->root) & EDGE_LABEL))) | 
|---|
| 1679 | return 0; | 
|---|
| 1680 | if (agtail(fe) == agtail(e)) { | 
|---|
| 1681 | *hp = hn; | 
|---|
| 1682 | startp = add_pointf(ND_coord(tn), ED_tail_port(e).p); | 
|---|
| 1683 | endp = add_pointf(ND_coord(hn), ED_head_port(e).p); | 
|---|
| 1684 | } | 
|---|
| 1685 | else { | 
|---|
| 1686 | *hp = tn; | 
|---|
| 1687 | startp = add_pointf(ND_coord(hn), ED_head_port(e).p); | 
|---|
| 1688 | endp = add_pointf(ND_coord(tn), ED_tail_port(e).p); | 
|---|
| 1689 | } | 
|---|
| 1690 |  | 
|---|
| 1691 | if (ED_label(e)) { | 
|---|
| 1692 | dimen = ED_label(e)->dimen; | 
|---|
| 1693 | if (GD_flip(agraphof(hn))) { | 
|---|
| 1694 | width = dimen.y; | 
|---|
| 1695 | height = dimen.x; | 
|---|
| 1696 | } | 
|---|
| 1697 | else { | 
|---|
| 1698 | width = dimen.x; | 
|---|
| 1699 | height = dimen.y; | 
|---|
| 1700 | } | 
|---|
| 1701 |  | 
|---|
| 1702 | lp = ED_label(e)->pos; | 
|---|
| 1703 | if (leftOf (endp,startp,lp)) { | 
|---|
| 1704 | lp.x += width/2.0; | 
|---|
| 1705 | lp.y -= height/2.0; | 
|---|
| 1706 | } | 
|---|
| 1707 | else { | 
|---|
| 1708 | lp.x -= width/2.0; | 
|---|
| 1709 | lp.y += height/2.0; | 
|---|
| 1710 | } | 
|---|
| 1711 |  | 
|---|
| 1712 | points[1] = points[0] = startp; | 
|---|
| 1713 | points[2] = points[3] = points[4] = lp; | 
|---|
| 1714 | points[5] = points[6] = endp; | 
|---|
| 1715 | pn = 7; | 
|---|
| 1716 | } | 
|---|
| 1717 | else { | 
|---|
| 1718 | points[1] = points[0] = startp; | 
|---|
| 1719 | points[3] = points[2] = endp; | 
|---|
| 1720 | pn = 4; | 
|---|
| 1721 | } | 
|---|
| 1722 |  | 
|---|
| 1723 | return pn; | 
|---|
| 1724 | } | 
|---|
| 1725 |  | 
|---|
| 1726 | #define NUMPTS 2000 | 
|---|
| 1727 |  | 
|---|
| 1728 | /* make_regular_edge: | 
|---|
| 1729 | */ | 
|---|
| 1730 | static void | 
|---|
| 1731 | make_regular_edge(graph_t* g, spline_info_t* sp, path * P, edge_t ** edges, int ind, int cnt, int et) | 
|---|
| 1732 | { | 
|---|
| 1733 | node_t *tn, *hn; | 
|---|
| 1734 | Agedgeinfo_t fwdedgeai, fwdedgebi, fwdedgei; | 
|---|
| 1735 | Agedgepair_t fwdedgea, fwdedgeb, fwdedge; | 
|---|
| 1736 | edge_t *e, *fe, *le, *segfirst; | 
|---|
| 1737 | pointf *ps; | 
|---|
| 1738 | pathend_t tend, hend; | 
|---|
| 1739 | boxf b; | 
|---|
| 1740 | int boxn, sl, si, smode, i, j, dx, pn, hackflag, longedge; | 
|---|
| 1741 | static pointf* pointfs; | 
|---|
| 1742 | static pointf* pointfs2; | 
|---|
| 1743 | static int numpts; | 
|---|
| 1744 | static int numpts2; | 
|---|
| 1745 | int pointn; | 
|---|
| 1746 |  | 
|---|
| 1747 | fwdedgea.out.base.data = (Agrec_t*)&fwdedgeai; | 
|---|
| 1748 | fwdedgeb.out.base.data = (Agrec_t*)&fwdedgebi; | 
|---|
| 1749 | fwdedge.out.base.data = (Agrec_t*)&fwdedgei; | 
|---|
| 1750 |  | 
|---|
| 1751 | if (!pointfs) { | 
|---|
| 1752 | pointfs = N_GNEW(NUMPTS, pointf); | 
|---|
| 1753 | pointfs2 = N_GNEW(NUMPTS, pointf); | 
|---|
| 1754 | numpts = NUMPTS; | 
|---|
| 1755 | numpts2 = NUMPTS; | 
|---|
| 1756 | } | 
|---|
| 1757 | sl = 0; | 
|---|
| 1758 | e = edges[ind]; | 
|---|
| 1759 | hackflag = FALSE; | 
|---|
| 1760 | if (ABS(ND_rank(agtail(e)) - ND_rank(aghead(e))) > 1) { | 
|---|
| 1761 | fwdedgeai = *(Agedgeinfo_t*)e->base.data; | 
|---|
| 1762 | fwdedgea.out = *e; | 
|---|
| 1763 | fwdedgea.in = *AGOUT2IN(e); | 
|---|
| 1764 | fwdedgea.out.base.data = (Agrec_t*)&fwdedgeai; | 
|---|
| 1765 | if (ED_tree_index(e) & BWDEDGE) { | 
|---|
| 1766 | MAKEFWDEDGE(&fwdedgeb.out, e); | 
|---|
| 1767 | agtail(&fwdedgea.out) = aghead(e); | 
|---|
| 1768 | ED_tail_port(&fwdedgea.out) = ED_head_port(e); | 
|---|
| 1769 | } else { | 
|---|
| 1770 | fwdedgebi = *(Agedgeinfo_t*)e->base.data; | 
|---|
| 1771 | fwdedgeb.out = *e; | 
|---|
| 1772 | fwdedgeb.out.base.data = (Agrec_t*)&fwdedgebi; | 
|---|
| 1773 | agtail(&fwdedgea.out) = agtail(e); | 
|---|
| 1774 | fwdedgeb.in = *AGOUT2IN(e); | 
|---|
| 1775 | } | 
|---|
| 1776 | le = getmainedge(e); | 
|---|
| 1777 | while (ED_to_virt(le)) | 
|---|
| 1778 | le = ED_to_virt(le); | 
|---|
| 1779 | aghead(&fwdedgea.out) = aghead(le); | 
|---|
| 1780 | ED_head_port(&fwdedgea.out).defined = FALSE; | 
|---|
| 1781 | ED_edge_type(&fwdedgea.out) = VIRTUAL; | 
|---|
| 1782 | ED_head_port(&fwdedgea.out).p.x = ED_head_port(&fwdedgea.out).p.y = 0; | 
|---|
| 1783 | ED_to_orig(&fwdedgea.out) = e; | 
|---|
| 1784 | e = &fwdedgea.out; | 
|---|
| 1785 | hackflag = TRUE; | 
|---|
| 1786 | } else { | 
|---|
| 1787 | if (ED_tree_index(e) & BWDEDGE) { | 
|---|
| 1788 | MAKEFWDEDGE(&fwdedgea.out, e); | 
|---|
| 1789 | e = &fwdedgea.out; | 
|---|
| 1790 | } | 
|---|
| 1791 | } | 
|---|
| 1792 | fe = e; | 
|---|
| 1793 |  | 
|---|
| 1794 | /* compute the spline points for the edge */ | 
|---|
| 1795 |  | 
|---|
| 1796 | if ((et == ET_LINE) && (pointn = makeLineEdge (g, fe, pointfs, &hn))) { | 
|---|
| 1797 | } | 
|---|
| 1798 | else { | 
|---|
| 1799 | int splines = et == ET_SPLINE; | 
|---|
| 1800 | boxn = 0; | 
|---|
| 1801 | pointn = 0; | 
|---|
| 1802 | segfirst = e; | 
|---|
| 1803 | tn = agtail(e); | 
|---|
| 1804 | hn = aghead(e); | 
|---|
| 1805 | b = tend.nb = maximal_bbox(g, sp, tn, NULL, e); | 
|---|
| 1806 | beginpath(P, e, REGULAREDGE, &tend, spline_merge(tn)); | 
|---|
| 1807 | b.UR.y = tend.boxes[tend.boxn - 1].UR.y; | 
|---|
| 1808 | b.LL.y = tend.boxes[tend.boxn - 1].LL.y; | 
|---|
| 1809 | b = makeregularend(b, BOTTOM, | 
|---|
| 1810 | ND_coord(tn).y - GD_rank(g)[ND_rank(tn)].ht1); | 
|---|
| 1811 | if (b.LL.x < b.UR.x && b.LL.y < b.UR.y) | 
|---|
| 1812 | tend.boxes[tend.boxn++] = b; | 
|---|
| 1813 | longedge = 0; | 
|---|
| 1814 | smode = FALSE, si = -1; | 
|---|
| 1815 | while (ND_node_type(hn) == VIRTUAL && !sinfo.splineMerge(hn)) { | 
|---|
| 1816 | longedge = 1; | 
|---|
| 1817 | boxes[boxn++] = rank_box(sp, g, ND_rank(tn)); | 
|---|
| 1818 | if (!smode | 
|---|
| 1819 | && ((sl = straight_len(hn)) >= | 
|---|
| 1820 | ((GD_has_labels(g->root) & EDGE_LABEL) ? 4 + 1 : 2 + 1))) { | 
|---|
| 1821 | smode = TRUE; | 
|---|
| 1822 | si = 1, sl -= 2; | 
|---|
| 1823 | } | 
|---|
| 1824 | if (!smode || si > 0) { | 
|---|
| 1825 | si--; | 
|---|
| 1826 | boxes[boxn++] = maximal_bbox(g, sp, hn, e, ND_out(hn).list[0]); | 
|---|
| 1827 | e = ND_out(hn).list[0]; | 
|---|
| 1828 | tn = agtail(e); | 
|---|
| 1829 | hn = aghead(e); | 
|---|
| 1830 | continue; | 
|---|
| 1831 | } | 
|---|
| 1832 | hend.nb = maximal_bbox(g, sp, hn, e, ND_out(hn).list[0]); | 
|---|
| 1833 | endpath(P, e, REGULAREDGE, &hend, spline_merge(aghead(e))); | 
|---|
| 1834 | b = makeregularend(hend.boxes[hend.boxn - 1], TOP, | 
|---|
| 1835 | ND_coord(hn).y + GD_rank(g)[ND_rank(hn)].ht2); | 
|---|
| 1836 | if (b.LL.x < b.UR.x && b.LL.y < b.UR.y) | 
|---|
| 1837 | hend.boxes[hend.boxn++] = b; | 
|---|
| 1838 | P->end.theta = M_PI / 2, P->end.constrained = TRUE; | 
|---|
| 1839 | completeregularpath(P, segfirst, e, &tend, &hend, boxes, boxn, 1); | 
|---|
| 1840 | if (splines) ps = routesplines(P, &pn); | 
|---|
| 1841 | else { | 
|---|
| 1842 | ps = routepolylines (P, &pn); | 
|---|
| 1843 | if ((et == ET_LINE) && (pn > 4)) { | 
|---|
| 1844 | ps[1] = ps[0]; | 
|---|
| 1845 | ps[3] = ps[2] = ps[pn-1]; | 
|---|
| 1846 | pn = 4; | 
|---|
| 1847 | } | 
|---|
| 1848 | } | 
|---|
| 1849 | if (pn == 0) | 
|---|
| 1850 | return; | 
|---|
| 1851 |  | 
|---|
| 1852 | if (pointn + pn > numpts) { | 
|---|
| 1853 | /* This should be enough to include 3 extra points added by | 
|---|
| 1854 | * straight_path below. | 
|---|
| 1855 | */ | 
|---|
| 1856 | numpts = 2*(pointn+pn); | 
|---|
| 1857 | pointfs = RALLOC(numpts, pointfs, pointf); | 
|---|
| 1858 | } | 
|---|
| 1859 | for (i = 0; i < pn; i++) { | 
|---|
| 1860 | pointfs[pointn++] = ps[i]; | 
|---|
| 1861 | } | 
|---|
| 1862 | e = straight_path(ND_out(hn).list[0], sl, pointfs, &pointn); | 
|---|
| 1863 | recover_slack(segfirst, P); | 
|---|
| 1864 | segfirst = e; | 
|---|
| 1865 | tn = agtail(e); | 
|---|
| 1866 | hn = aghead(e); | 
|---|
| 1867 | boxn = 0; | 
|---|
| 1868 | tend.nb = maximal_bbox(g, sp, tn, ND_in(tn).list[0], e); | 
|---|
| 1869 | beginpath(P, e, REGULAREDGE, &tend, spline_merge(tn)); | 
|---|
| 1870 | b = makeregularend(tend.boxes[tend.boxn - 1], BOTTOM, | 
|---|
| 1871 | ND_coord(tn).y - GD_rank(g)[ND_rank(tn)].ht1); | 
|---|
| 1872 | if (b.LL.x < b.UR.x && b.LL.y < b.UR.y) | 
|---|
| 1873 | tend.boxes[tend.boxn++] = b; | 
|---|
| 1874 | P->start.theta = -M_PI / 2, P->start.constrained = TRUE; | 
|---|
| 1875 | smode = FALSE; | 
|---|
| 1876 | } | 
|---|
| 1877 | boxes[boxn++] = rank_box(sp, g, ND_rank(tn)); | 
|---|
| 1878 | b = hend.nb = maximal_bbox(g, sp, hn, e, NULL); | 
|---|
| 1879 | endpath(P, hackflag ? &fwdedgeb.out : e, REGULAREDGE, &hend, spline_merge(aghead(e))); | 
|---|
| 1880 | b.UR.y = hend.boxes[hend.boxn - 1].UR.y; | 
|---|
| 1881 | b.LL.y = hend.boxes[hend.boxn - 1].LL.y; | 
|---|
| 1882 | b = makeregularend(b, TOP, | 
|---|
| 1883 | ND_coord(hn).y + GD_rank(g)[ND_rank(hn)].ht2); | 
|---|
| 1884 | if (b.LL.x < b.UR.x && b.LL.y < b.UR.y) | 
|---|
| 1885 | hend.boxes[hend.boxn++] = b; | 
|---|
| 1886 | completeregularpath(P, segfirst, e, &tend, &hend, boxes, boxn, | 
|---|
| 1887 | longedge); | 
|---|
| 1888 | if (splines) ps = routesplines(P, &pn); | 
|---|
| 1889 | else ps = routepolylines (P, &pn); | 
|---|
| 1890 | if ((et == ET_LINE) && (pn > 4)) { | 
|---|
| 1891 | /* Here we have used the polyline case to handle | 
|---|
| 1892 | * an edge between two nodes on adjacent ranks. If the | 
|---|
| 1893 | * results really is a polyline, straighten it. | 
|---|
| 1894 | */ | 
|---|
| 1895 | ps[1] = ps[0]; | 
|---|
| 1896 | ps[3] = ps[2] = ps[pn-1]; | 
|---|
| 1897 | pn = 4; | 
|---|
| 1898 | } | 
|---|
| 1899 | if (pn == 0) | 
|---|
| 1900 | return; | 
|---|
| 1901 | if (pointn + pn > numpts) { | 
|---|
| 1902 | numpts = 2*(pointn+pn); | 
|---|
| 1903 | pointfs = RALLOC(numpts, pointfs, pointf); | 
|---|
| 1904 | } | 
|---|
| 1905 | for (i = 0; i < pn; i++) { | 
|---|
| 1906 | pointfs[pointn++] = ps[i]; | 
|---|
| 1907 | } | 
|---|
| 1908 | recover_slack(segfirst, P); | 
|---|
| 1909 | hn = hackflag ? aghead(&fwdedgeb.out) : aghead(e); | 
|---|
| 1910 | } | 
|---|
| 1911 |  | 
|---|
| 1912 | /* make copies of the spline points, one per multi-edge */ | 
|---|
| 1913 |  | 
|---|
| 1914 | if (cnt == 1) { | 
|---|
| 1915 | clip_and_install(fe, hn, pointfs, pointn, &sinfo); | 
|---|
| 1916 | return; | 
|---|
| 1917 | } | 
|---|
| 1918 | dx = sp->Multisep * (cnt - 1) / 2; | 
|---|
| 1919 | for (i = 1; i < pointn - 1; i++) | 
|---|
| 1920 | pointfs[i].x -= dx; | 
|---|
| 1921 |  | 
|---|
| 1922 | if (numpts > numpts2) { | 
|---|
| 1923 | numpts2 = numpts; | 
|---|
| 1924 | pointfs2 = RALLOC(numpts2, pointfs2, pointf); | 
|---|
| 1925 | } | 
|---|
| 1926 | for (i = 0; i < pointn; i++) | 
|---|
| 1927 | pointfs2[i] = pointfs[i]; | 
|---|
| 1928 | clip_and_install(fe, hn, pointfs2, pointn, &sinfo); | 
|---|
| 1929 | for (j = 1; j < cnt; j++) { | 
|---|
| 1930 | e = edges[ind + j]; | 
|---|
| 1931 | if (ED_tree_index(e) & BWDEDGE) { | 
|---|
| 1932 | MAKEFWDEDGE(&fwdedge.out, e); | 
|---|
| 1933 | e = &fwdedge.out; | 
|---|
| 1934 | } | 
|---|
| 1935 | for (i = 1; i < pointn - 1; i++) | 
|---|
| 1936 | pointfs[i].x += sp->Multisep; | 
|---|
| 1937 | for (i = 0; i < pointn; i++) | 
|---|
| 1938 | pointfs2[i] = pointfs[i]; | 
|---|
| 1939 | clip_and_install(e, aghead(e), pointfs2, pointn, &sinfo); | 
|---|
| 1940 | } | 
|---|
| 1941 | } | 
|---|
| 1942 |  | 
|---|
| 1943 | /* regular edges */ | 
|---|
| 1944 |  | 
|---|
| 1945 | #define DONT_WANT_ANY_ENDPOINT_PATH_REFINEMENT | 
|---|
| 1946 | #ifdef DONT_WANT_ANY_ENDPOINT_PATH_REFINEMENT | 
|---|
| 1947 | static void | 
|---|
| 1948 | completeregularpath(path * P, edge_t * first, edge_t * last, | 
|---|
| 1949 | pathend_t * tendp, pathend_t * hendp, boxf * boxes, | 
|---|
| 1950 | int boxn, int flag) | 
|---|
| 1951 | { | 
|---|
| 1952 | edge_t *uleft, *uright, *lleft, *lright; | 
|---|
| 1953 | int i, fb, lb; | 
|---|
| 1954 | splines *spl; | 
|---|
| 1955 | pointf *pp; | 
|---|
| 1956 | int pn; | 
|---|
| 1957 |  | 
|---|
| 1958 | fb = lb = -1; | 
|---|
| 1959 | uleft = uright = NULL; | 
|---|
| 1960 | uleft = top_bound(first, -1), uright = top_bound(first, 1); | 
|---|
| 1961 | if (uleft) { | 
|---|
| 1962 | if (!(spl = getsplinepoints(uleft))) return; | 
|---|
| 1963 | pp = spl->list[0].list; | 
|---|
| 1964 | pn = spl->list[0].size; | 
|---|
| 1965 | } | 
|---|
| 1966 | if (uright) { | 
|---|
| 1967 | if (!(spl = getsplinepoints(uright))) return; | 
|---|
| 1968 | pp = spl->list[0].list; | 
|---|
| 1969 | pn = spl->list[0].size; | 
|---|
| 1970 | } | 
|---|
| 1971 | lleft = lright = NULL; | 
|---|
| 1972 | lleft = bot_bound(last, -1), lright = bot_bound(last, 1); | 
|---|
| 1973 | if (lleft) { | 
|---|
| 1974 | if (!(spl = getsplinepoints(lleft))) return; | 
|---|
| 1975 | pp = spl->list[spl->size - 1].list; | 
|---|
| 1976 | pn = spl->list[spl->size - 1].size; | 
|---|
| 1977 | } | 
|---|
| 1978 | if (lright) { | 
|---|
| 1979 | if (!(spl = getsplinepoints(lright))) return; | 
|---|
| 1980 | pp = spl->list[spl->size - 1].list; | 
|---|
| 1981 | pn = spl->list[spl->size - 1].size; | 
|---|
| 1982 | } | 
|---|
| 1983 | for (i = 0; i < tendp->boxn; i++) | 
|---|
| 1984 | add_box(P, tendp->boxes[i]); | 
|---|
| 1985 | fb = P->nbox + 1; | 
|---|
| 1986 | lb = fb + boxn - 3; | 
|---|
| 1987 | for (i = 0; i < boxn; i++) | 
|---|
| 1988 | add_box(P, boxes[i]); | 
|---|
| 1989 | for (i = hendp->boxn - 1; i >= 0; i--) | 
|---|
| 1990 | add_box(P, hendp->boxes[i]); | 
|---|
| 1991 | adjustregularpath(P, fb, lb); | 
|---|
| 1992 | } | 
|---|
| 1993 | #else | 
|---|
| 1994 | void refineregularends(edge_t * left, edge_t * right, pathend_t * endp, | 
|---|
| 1995 | int dir, boxf b, boxf * boxes, int *boxnp); | 
|---|
| 1996 |  | 
|---|
| 1997 | /* box subdivision is obsolete, I think... ek */ | 
|---|
| 1998 | static void | 
|---|
| 1999 | completeregularpath(path * P, edge_t * first, edge_t * last, | 
|---|
| 2000 | pathend_t * tendp, pathend_t * hendp, boxf * boxes, | 
|---|
| 2001 | int boxn, int flag) | 
|---|
| 2002 | { | 
|---|
| 2003 | edge_t *uleft, *uright, *lleft, *lright; | 
|---|
| 2004 | boxf uboxes[NSUB], lboxes[NSUB]; | 
|---|
| 2005 | boxf b; | 
|---|
| 2006 | int uboxn, lboxn, i, y, fb, lb; | 
|---|
| 2007 |  | 
|---|
| 2008 | fb = lb = -1; | 
|---|
| 2009 | uleft = uright = NULL; | 
|---|
| 2010 | if (flag || ND_rank(agtail(first)) + 1 != ND_rank(aghead(last))) | 
|---|
| 2011 | uleft = top_bound(first, -1), uright = top_bound(first, 1); | 
|---|
| 2012 | refineregularends(uleft, uright, tendp, 1, boxes[0], uboxes, &uboxn); | 
|---|
| 2013 | lleft = lright = NULL; | 
|---|
| 2014 | if (flag || ND_rank(agtail(first)) + 1 != ND_rank(aghead(last))) | 
|---|
| 2015 | lleft = bot_bound(last, -1), lright = bot_bound(last, 1); | 
|---|
| 2016 | refineregularends(lleft, lright, hendp, -1, boxes[boxn - 1], lboxes, | 
|---|
| 2017 | &lboxn); | 
|---|
| 2018 | for (i = 0; i < tendp->boxn; i++) | 
|---|
| 2019 | add_box(P, tendp->boxes[i]); | 
|---|
| 2020 | if (ND_rank(agtail(first)) + 1 == ND_rank(aghead(last))) { | 
|---|
| 2021 | if ((!uleft && !uright) && (lleft || lright)) { | 
|---|
| 2022 | b = boxes[0]; | 
|---|
| 2023 | y = b.UR.y - b.LL.y; | 
|---|
| 2024 | for (i = 0; i < NSUB; i++) { | 
|---|
| 2025 | uboxes[i] = b; | 
|---|
| 2026 | uboxes[i].UR.y = b.UR.y - y * i / NSUB; | 
|---|
| 2027 | uboxes[i].LL.y = b.UR.y - y * (i + 1) / NSUB; | 
|---|
| 2028 | } | 
|---|
| 2029 | uboxn = NSUB; | 
|---|
| 2030 | } else if ((uleft || uright) && (!lleft && !lright)) { | 
|---|
| 2031 | b = boxes[boxn - 1]; | 
|---|
| 2032 | y = b.UR.y - b.LL.y; | 
|---|
| 2033 | for (i = 0; i < NSUB; i++) { | 
|---|
| 2034 | lboxes[i] = b; | 
|---|
| 2035 | lboxes[i].UR.y = b.UR.y - y * i / NSUB; | 
|---|
| 2036 | lboxes[i].LL.y = b.UR.y - y * (i + 1) / NSUB; | 
|---|
| 2037 | } | 
|---|
| 2038 | lboxn = NSUB; | 
|---|
| 2039 | } | 
|---|
| 2040 | for (i = 0; i < uboxn; i++) { | 
|---|
| 2041 | uboxes[i].LL.x = MAX(uboxes[i].LL.x, lboxes[i].LL.x); | 
|---|
| 2042 | uboxes[i].UR.x = MIN(uboxes[i].UR.x, lboxes[i].UR.x); | 
|---|
| 2043 | } | 
|---|
| 2044 | for (i = 0; i < uboxn; i++) | 
|---|
| 2045 | add_box(P, uboxes[i]); | 
|---|
| 2046 | } else { | 
|---|
| 2047 | for (i = 0; i < uboxn; i++) | 
|---|
| 2048 | add_box(P, uboxes[i]); | 
|---|
| 2049 | fb = P->nbox; | 
|---|
| 2050 | lb = fb + boxn - 3; | 
|---|
| 2051 | for (i = 1; i < boxn - 1; i++) | 
|---|
| 2052 | add_box(P, boxes[i]); | 
|---|
| 2053 | for (i = 0; i < lboxn; i++) | 
|---|
| 2054 | add_box(P, lboxes[i]); | 
|---|
| 2055 | } | 
|---|
| 2056 | for (i = hendp->boxn - 1; i >= 0; i--) | 
|---|
| 2057 | add_box(P, hendp->boxes[i]); | 
|---|
| 2058 | adjustregularpath(P, fb, lb); | 
|---|
| 2059 | } | 
|---|
| 2060 | #endif | 
|---|
| 2061 |  | 
|---|
| 2062 | /* makeregularend: | 
|---|
| 2063 | * Add box to fill between node and interrank space. Needed because | 
|---|
| 2064 | * nodes in a given rank can differ in height. | 
|---|
| 2065 | * for now, regular edges always go from top to bottom | 
|---|
| 2066 | */ | 
|---|
| 2067 | static boxf makeregularend(boxf b, int side, double y) | 
|---|
| 2068 | { | 
|---|
| 2069 | boxf newb; | 
|---|
| 2070 | switch (side) { | 
|---|
| 2071 | case BOTTOM: | 
|---|
| 2072 | newb = boxfof(b.LL.x, y, b.UR.x, b.LL.y); | 
|---|
| 2073 | break; | 
|---|
| 2074 | case TOP: | 
|---|
| 2075 | newb = boxfof(b.LL.x, b.UR.y, b.UR.x, y); | 
|---|
| 2076 | break; | 
|---|
| 2077 | } | 
|---|
| 2078 | return newb; | 
|---|
| 2079 | } | 
|---|
| 2080 |  | 
|---|
| 2081 | #ifndef DONT_WANT_ANY_ENDPOINT_PATH_REFINEMENT | 
|---|
| 2082 | void refineregularends(left, right, endp, dir, b, boxes, boxnp) | 
|---|
| 2083 | edge_t *left, *right; | 
|---|
| 2084 | pathend_t *endp; | 
|---|
| 2085 | int dir; | 
|---|
| 2086 | box b; | 
|---|
| 2087 | box *boxes; | 
|---|
| 2088 | int *boxnp; | 
|---|
| 2089 | { | 
|---|
| 2090 | splines *lspls, *rspls; | 
|---|
| 2091 | point pp, cp; | 
|---|
| 2092 | box eb; | 
|---|
| 2093 | box *bp; | 
|---|
| 2094 | int y, i, j, k; | 
|---|
| 2095 | int nsub; | 
|---|
| 2096 |  | 
|---|
| 2097 | y = b.UR.y - b.LL.y; | 
|---|
| 2098 | if ((y == 1) || (!left && !right)) { | 
|---|
| 2099 | boxes[0] = b; | 
|---|
| 2100 | *boxnp = 1; | 
|---|
| 2101 | return; | 
|---|
| 2102 | } | 
|---|
| 2103 | nsub = MIN(NSUB, y); | 
|---|
| 2104 | for (i = 0; i < nsub; i++) { | 
|---|
| 2105 | boxes[i] = b; | 
|---|
| 2106 | boxes[i].UR.y = b.UR.y - y * i / nsub; | 
|---|
| 2107 | boxes[i].LL.y = b.UR.y - y * (i + 1) / nsub; | 
|---|
| 2108 | if (boxes[i].UR.y == boxes[i].LL.y) | 
|---|
| 2109 | abort(); | 
|---|
| 2110 | } | 
|---|
| 2111 | *boxnp = nsub; | 
|---|
| 2112 | /* only break big boxes */ | 
|---|
| 2113 | for (j = 0; j < endp->boxn; j++) { | 
|---|
| 2114 | eb = endp->boxes[j]; | 
|---|
| 2115 | y = eb.UR.y - eb.LL.y; | 
|---|
| 2116 | #ifdef STEVE_AND_LEFTY_GRASPING_AT_STRAWS | 
|---|
| 2117 | if (y < 15) | 
|---|
| 2118 | continue; | 
|---|
| 2119 | #else | 
|---|
| 2120 | if (y < nsub) | 
|---|
| 2121 | continue; | 
|---|
| 2122 | #endif | 
|---|
| 2123 | for (k = endp->boxn - 1; k > j; k--) | 
|---|
| 2124 | endp->boxes[k + (nsub - 1)] = endp->boxes[k]; | 
|---|
| 2125 | for (i = 0; i < nsub; i++) { | 
|---|
| 2126 | bp = &endp->boxes[j + ((dir == 1) ? i : (nsub - i - 1))]; | 
|---|
| 2127 | *bp = eb; | 
|---|
| 2128 | bp->UR.y = eb.UR.y - y * i / nsub; | 
|---|
| 2129 | bp->LL.y = eb.UR.y - y * (i + 1) / nsub; | 
|---|
| 2130 | if (bp->UR.y == bp->LL.y) | 
|---|
| 2131 | abort(); | 
|---|
| 2132 | } | 
|---|
| 2133 | endp->boxn += (nsub - 1); | 
|---|
| 2134 | j += nsub - 1; | 
|---|
| 2135 | } | 
|---|
| 2136 | if (left) { | 
|---|
| 2137 | if (!(lspls = getsplinepoints(left))) return; | 
|---|
| 2138 | pp = spline_at_y(lspls, boxes[0].UR.y); | 
|---|
| 2139 | for (i = 0; i < nsub; i++) { | 
|---|
| 2140 | cp = spline_at_y(lspls, boxes[i].LL.y); | 
|---|
| 2141 | /*boxes[i].LL.x = AVG (pp.x, cp.x); */ | 
|---|
| 2142 | boxes[i].LL.x = MAX(pp.x, cp.x); | 
|---|
| 2143 | pp = cp; | 
|---|
| 2144 | } | 
|---|
| 2145 | pp = spline_at_y(lspls, (dir == 1) ? | 
|---|
| 2146 | endp->boxes[1].UR.y : endp->boxes[1].LL.y); | 
|---|
| 2147 | for (i = 1; i < endp->boxn; i++) { | 
|---|
| 2148 | cp = spline_at_y(lspls, (dir == 1) ? | 
|---|
| 2149 | endp->boxes[i].LL.y : endp->boxes[i].UR.y); | 
|---|
| 2150 | endp->boxes[i].LL.x = MIN(endp->nb.UR.x, MAX(pp.x, cp.x)); | 
|---|
| 2151 | pp = cp; | 
|---|
| 2152 | } | 
|---|
| 2153 | i = (dir == 1) ? 0 : *boxnp - 1; | 
|---|
| 2154 | if (boxes[i].LL.x > endp->boxes[endp->boxn - 1].UR.x - MINW) | 
|---|
| 2155 | boxes[i].LL.x = endp->boxes[endp->boxn - 1].UR.x - MINW; | 
|---|
| 2156 | } | 
|---|
| 2157 | if (right) { | 
|---|
| 2158 | if (!(rspls = getsplinepoints(right))) return; | 
|---|
| 2159 | pp = spline_at_y(rspls, boxes[0].UR.y); | 
|---|
| 2160 | for (i = 0; i < nsub; i++) { | 
|---|
| 2161 | cp = spline_at_y(rspls, boxes[i].LL.y); | 
|---|
| 2162 | /*boxes[i].UR.x = AVG (pp.x, cp.x); */ | 
|---|
| 2163 | boxes[i].UR.x = AVG(pp.x, cp.x); | 
|---|
| 2164 | pp = cp; | 
|---|
| 2165 | } | 
|---|
| 2166 | pp = spline_at_y(rspls, (dir == 1) ? | 
|---|
| 2167 | endp->boxes[1].UR.y : endp->boxes[1].LL.y); | 
|---|
| 2168 | for (i = 1; i < endp->boxn; i++) { | 
|---|
| 2169 | cp = spline_at_y(rspls, (dir == 1) ? | 
|---|
| 2170 | endp->boxes[i].LL.y : endp->boxes[i].UR.y); | 
|---|
| 2171 | endp->boxes[i].UR.x = MAX(endp->nb.LL.x, AVG(pp.x, cp.x)); | 
|---|
| 2172 | pp = cp; | 
|---|
| 2173 | } | 
|---|
| 2174 | i = (dir == 1) ? 0 : *boxnp - 1; | 
|---|
| 2175 | if (boxes[i].UR.x < endp->boxes[endp->boxn - 1].LL.x + MINW) | 
|---|
| 2176 | boxes[i].UR.x = endp->boxes[endp->boxn - 1].LL.x + MINW; | 
|---|
| 2177 | } | 
|---|
| 2178 | } | 
|---|
| 2179 | #endif | 
|---|
| 2180 |  | 
|---|
| 2181 | /* adjustregularpath: | 
|---|
| 2182 | * make sure the path is wide enough. | 
|---|
| 2183 | * the % 2 was so that in rank boxes would only be grown if | 
|---|
| 2184 | * they were == 0 while inter-rank boxes could be stretched to a min | 
|---|
| 2185 | * width. | 
|---|
| 2186 | * The list of boxes has three parts: tail boxes, path boxes, and head | 
|---|
| 2187 | * boxes. (Note that because of back edges, the tail boxes might actually | 
|---|
| 2188 | * belong to the head node, and vice versa.) fb is the index of the | 
|---|
| 2189 | * first interrank path box and lb is the last interrank path box. | 
|---|
| 2190 | * If fb > lb, there are none. | 
|---|
| 2191 | * | 
|---|
| 2192 | * The second for loop was added by ek long ago, and apparently is intended | 
|---|
| 2193 | * to guarantee an overlap between adjacent boxes of at least MINW. | 
|---|
| 2194 | * It doesn't do this, and the ifdef'ed part has the potential of moving | 
|---|
| 2195 | * a box within a node for more complex paths. | 
|---|
| 2196 | */ | 
|---|
| 2197 | static void adjustregularpath(path * P, int fb, int lb) | 
|---|
| 2198 | { | 
|---|
| 2199 | boxf *bp1, *bp2; | 
|---|
| 2200 | int i, x; | 
|---|
| 2201 |  | 
|---|
| 2202 | for (i = fb-1; i < lb+1; i++) { | 
|---|
| 2203 | bp1 = &P->boxes[i]; | 
|---|
| 2204 | if ((i - fb) % 2 == 0) { | 
|---|
| 2205 | if (bp1->LL.x >= bp1->UR.x) { | 
|---|
| 2206 | x = (bp1->LL.x + bp1->UR.x) / 2; | 
|---|
| 2207 | bp1->LL.x = x - HALFMINW, bp1->UR.x = x + HALFMINW; | 
|---|
| 2208 | } | 
|---|
| 2209 | } else { | 
|---|
| 2210 | if (bp1->LL.x + MINW > bp1->UR.x) { | 
|---|
| 2211 | x = (bp1->LL.x + bp1->UR.x) / 2; | 
|---|
| 2212 | bp1->LL.x = x - HALFMINW, bp1->UR.x = x + HALFMINW; | 
|---|
| 2213 | } | 
|---|
| 2214 | } | 
|---|
| 2215 | } | 
|---|
| 2216 | for (i = 0; i < P->nbox - 1; i++) { | 
|---|
| 2217 | bp1 = &P->boxes[i], bp2 = &P->boxes[i + 1]; | 
|---|
| 2218 | if (i >= fb && i <= lb && (i - fb) % 2 == 0) { | 
|---|
| 2219 | if (bp1->LL.x + MINW > bp2->UR.x) | 
|---|
| 2220 | bp2->UR.x = bp1->LL.x + MINW; | 
|---|
| 2221 | if (bp1->UR.x - MINW < bp2->LL.x) | 
|---|
| 2222 | bp2->LL.x = bp1->UR.x - MINW; | 
|---|
| 2223 | } else if (i + 1 >= fb && i < lb && (i + 1 - fb) % 2 == 0) { | 
|---|
| 2224 | if (bp1->LL.x + MINW > bp2->UR.x) | 
|---|
| 2225 | bp1->LL.x = bp2->UR.x - MINW; | 
|---|
| 2226 | if (bp1->UR.x - MINW < bp2->LL.x) | 
|---|
| 2227 | bp1->UR.x = bp2->LL.x + MINW; | 
|---|
| 2228 | } | 
|---|
| 2229 | } | 
|---|
| 2230 | } | 
|---|
| 2231 |  | 
|---|
| 2232 | static boxf rank_box(spline_info_t* sp, graph_t * g, int r) | 
|---|
| 2233 | { | 
|---|
| 2234 | boxf b; | 
|---|
| 2235 | node_t /* *right0, *right1, */  * left0, *left1; | 
|---|
| 2236 |  | 
|---|
| 2237 | b = sp->Rank_box[r]; | 
|---|
| 2238 | if (b.LL.x == b.UR.x) { | 
|---|
| 2239 | left0 = GD_rank(g)[r].v[0]; | 
|---|
| 2240 | /* right0 = GD_rank(g)[r].v[GD_rank(g)[r].n - 1]; */ | 
|---|
| 2241 | left1 = GD_rank(g)[r + 1].v[0]; | 
|---|
| 2242 | /* right1 = GD_rank(g)[r + 1].v[GD_rank(g)[r + 1].n - 1]; */ | 
|---|
| 2243 | b.LL.x = sp->LeftBound; | 
|---|
| 2244 | b.LL.y = ND_coord(left1).y + GD_rank(g)[r + 1].ht2; | 
|---|
| 2245 | b.UR.x = sp->RightBound; | 
|---|
| 2246 | b.UR.y = ND_coord(left0).y - GD_rank(g)[r].ht1; | 
|---|
| 2247 | sp->Rank_box[r] = b; | 
|---|
| 2248 | } | 
|---|
| 2249 | return b; | 
|---|
| 2250 | } | 
|---|
| 2251 |  | 
|---|
| 2252 | /* returns count of vertically aligned edges starting at n */ | 
|---|
| 2253 | static int straight_len(node_t * n) | 
|---|
| 2254 | { | 
|---|
| 2255 | int cnt = 0; | 
|---|
| 2256 | node_t *v; | 
|---|
| 2257 |  | 
|---|
| 2258 | v = n; | 
|---|
| 2259 | while (1) { | 
|---|
| 2260 | v = aghead(ND_out(v).list[0]); | 
|---|
| 2261 | if (ND_node_type(v) != VIRTUAL) | 
|---|
| 2262 | break; | 
|---|
| 2263 | if ((ND_out(v).size != 1) || (ND_in(v).size != 1)) | 
|---|
| 2264 | break; | 
|---|
| 2265 | if (ND_coord(v).x != ND_coord(n).x) | 
|---|
| 2266 | break; | 
|---|
| 2267 | cnt++; | 
|---|
| 2268 | } | 
|---|
| 2269 | return cnt; | 
|---|
| 2270 | } | 
|---|
| 2271 |  | 
|---|
| 2272 | static edge_t *straight_path(edge_t * e, int cnt, pointf * plist, int *np) | 
|---|
| 2273 | { | 
|---|
| 2274 | int n = *np; | 
|---|
| 2275 | edge_t *f = e; | 
|---|
| 2276 |  | 
|---|
| 2277 | while (cnt--) | 
|---|
| 2278 | f = ND_out(aghead(f)).list[0]; | 
|---|
| 2279 | plist[(*np)++] = plist[n - 1]; | 
|---|
| 2280 | plist[(*np)++] = plist[n - 1]; | 
|---|
| 2281 | plist[(*np)] = ND_coord(agtail(f));  /* will be overwritten by next spline */ | 
|---|
| 2282 |  | 
|---|
| 2283 | return f; | 
|---|
| 2284 | } | 
|---|
| 2285 |  | 
|---|
| 2286 | static void recover_slack(edge_t * e, path * p) | 
|---|
| 2287 | { | 
|---|
| 2288 | int b; | 
|---|
| 2289 | node_t *vn; | 
|---|
| 2290 |  | 
|---|
| 2291 | b = 0;			/* skip first rank box */ | 
|---|
| 2292 | for (vn = aghead(e); | 
|---|
| 2293 | ND_node_type(vn) == VIRTUAL && !sinfo.splineMerge(vn); | 
|---|
| 2294 | vn = aghead(ND_out(vn).list[0])) { | 
|---|
| 2295 | while ((b < p->nbox) && (p->boxes[b].LL.y > ND_coord(vn).y)) | 
|---|
| 2296 | b++; | 
|---|
| 2297 | if (b >= p->nbox) | 
|---|
| 2298 | break; | 
|---|
| 2299 | if (p->boxes[b].UR.y < ND_coord(vn).y) | 
|---|
| 2300 | continue; | 
|---|
| 2301 | if (ND_label(vn)) | 
|---|
| 2302 | resize_vn(vn, p->boxes[b].LL.x, p->boxes[b].UR.x, | 
|---|
| 2303 | p->boxes[b].UR.x + ND_rw(vn)); | 
|---|
| 2304 | else | 
|---|
| 2305 | resize_vn(vn, p->boxes[b].LL.x, (p->boxes[b].LL.x + | 
|---|
| 2306 | p->boxes[b].UR.x) / 2, | 
|---|
| 2307 | p->boxes[b].UR.x); | 
|---|
| 2308 | } | 
|---|
| 2309 | } | 
|---|
| 2310 |  | 
|---|
| 2311 | static void resize_vn(vn, lx, cx, rx) | 
|---|
| 2312 | node_t *vn; | 
|---|
| 2313 | int lx, cx, rx; | 
|---|
| 2314 | { | 
|---|
| 2315 | ND_coord(vn).x = cx; | 
|---|
| 2316 | ND_lw(vn) = cx - lx, ND_rw(vn) = rx - cx; | 
|---|
| 2317 | } | 
|---|
| 2318 |  | 
|---|
| 2319 | /* side > 0 means right. side < 0 means left */ | 
|---|
| 2320 | static edge_t *top_bound(edge_t * e, int side) | 
|---|
| 2321 | { | 
|---|
| 2322 | edge_t *f, *ans = NULL; | 
|---|
| 2323 | int i; | 
|---|
| 2324 |  | 
|---|
| 2325 | for (i = 0; (f = ND_out(agtail(e)).list[i]); i++) { | 
|---|
| 2326 | #if 0				/* were we out of our minds? */ | 
|---|
| 2327 | if (ED_tail_port(e).p.x != ED_tail_port(f).p.x) | 
|---|
| 2328 | continue; | 
|---|
| 2329 | #endif | 
|---|
| 2330 | if (side * (ND_order(aghead(f)) - ND_order(aghead(e))) <= 0) | 
|---|
| 2331 | continue; | 
|---|
| 2332 | if ((ED_spl(f) == NULL) | 
|---|
| 2333 | && ((ED_to_orig(f) == NULL) || (ED_spl(ED_to_orig(f)) == NULL))) | 
|---|
| 2334 | continue; | 
|---|
| 2335 | if ((ans == NULL) | 
|---|
| 2336 | || (side * (ND_order(aghead(ans)) - ND_order(aghead(f))) > 0)) | 
|---|
| 2337 | ans = f; | 
|---|
| 2338 | } | 
|---|
| 2339 | return ans; | 
|---|
| 2340 | } | 
|---|
| 2341 |  | 
|---|
| 2342 | static edge_t *bot_bound(edge_t * e, int side) | 
|---|
| 2343 | { | 
|---|
| 2344 | edge_t *f, *ans = NULL; | 
|---|
| 2345 | int i; | 
|---|
| 2346 |  | 
|---|
| 2347 | for (i = 0; (f = ND_in(aghead(e)).list[i]); i++) { | 
|---|
| 2348 | #if 0				/* same here */ | 
|---|
| 2349 | if (ED_head_port(e).p.x != ED_head_port(f).p.x) | 
|---|
| 2350 | continue; | 
|---|
| 2351 | #endif | 
|---|
| 2352 | if (side * (ND_order(agtail(f)) - ND_order(agtail(e))) <= 0) | 
|---|
| 2353 | continue; | 
|---|
| 2354 | if ((ED_spl(f) == NULL) | 
|---|
| 2355 | && ((ED_to_orig(f) == NULL) || (ED_spl(ED_to_orig(f)) == NULL))) | 
|---|
| 2356 | continue; | 
|---|
| 2357 | if ((ans == NULL) | 
|---|
| 2358 | || (side * (ND_order(agtail(ans)) - ND_order(agtail(f))) > 0)) | 
|---|
| 2359 | ans = f; | 
|---|
| 2360 | } | 
|---|
| 2361 | return ans; | 
|---|
| 2362 | } | 
|---|
| 2363 |  | 
|---|
| 2364 | /* common routines */ | 
|---|
| 2365 |  | 
|---|
| 2366 | static int cl_vninside(graph_t * cl, node_t * n) | 
|---|
| 2367 | { | 
|---|
| 2368 | return (BETWEEN(GD_bb(cl).LL.x, (double)(ND_coord(n).x), GD_bb(cl).UR.x) && | 
|---|
| 2369 | BETWEEN(GD_bb(cl).LL.y, (double)(ND_coord(n).y), GD_bb(cl).UR.y)); | 
|---|
| 2370 | } | 
|---|
| 2371 |  | 
|---|
| 2372 | /* All nodes belong to some cluster, which may be the root graph. | 
|---|
| 2373 | * For the following, we only want a cluster if it is a real cluster | 
|---|
| 2374 | * It is not clear this will handle all potential problems. It seems one | 
|---|
| 2375 | * could have hcl and tcl contained in cl, which would also cause problems. | 
|---|
| 2376 | */ | 
|---|
| 2377 | #define REAL_CLUSTER(n) (ND_clust(n)==g?NULL:ND_clust(n)) | 
|---|
| 2378 |  | 
|---|
| 2379 | /* returns the cluster of (adj) that interferes with n, | 
|---|
| 2380 | */ | 
|---|
| 2381 | static Agraph_t *cl_bound(graph_t* g,  node_t *n, node_t *adj) | 
|---|
| 2382 | { | 
|---|
| 2383 | graph_t *rv, *cl, *tcl, *hcl; | 
|---|
| 2384 | edge_t *orig; | 
|---|
| 2385 |  | 
|---|
| 2386 | rv = NULL; | 
|---|
| 2387 | if (ND_node_type(n) == NORMAL) | 
|---|
| 2388 | tcl = hcl = ND_clust(n); | 
|---|
| 2389 | else { | 
|---|
| 2390 | orig = ED_to_orig(ND_out(n).list[0]); | 
|---|
| 2391 | tcl = ND_clust(agtail(orig)); | 
|---|
| 2392 | hcl = ND_clust(aghead(orig)); | 
|---|
| 2393 | } | 
|---|
| 2394 | if (ND_node_type(adj) == NORMAL) { | 
|---|
| 2395 | cl = REAL_CLUSTER(adj); | 
|---|
| 2396 | if (cl && (cl != tcl) && (cl != hcl)) | 
|---|
| 2397 | rv = cl; | 
|---|
| 2398 | } else { | 
|---|
| 2399 | orig = ED_to_orig(ND_out(adj).list[0]); | 
|---|
| 2400 | cl = REAL_CLUSTER(agtail(orig)); | 
|---|
| 2401 | if (cl && (cl != tcl) && (cl != hcl) && cl_vninside(cl, adj)) | 
|---|
| 2402 | rv = cl; | 
|---|
| 2403 | else { | 
|---|
| 2404 | cl = REAL_CLUSTER(aghead(orig)); | 
|---|
| 2405 | if (cl && (cl != tcl) && (cl != hcl) && cl_vninside(cl, adj)) | 
|---|
| 2406 | rv = cl; | 
|---|
| 2407 | } | 
|---|
| 2408 | } | 
|---|
| 2409 | return rv; | 
|---|
| 2410 | } | 
|---|
| 2411 |  | 
|---|
| 2412 | /* maximal_bbox: | 
|---|
| 2413 | * Return an initial bounding box to be used for building the | 
|---|
| 2414 | * beginning or ending of the path of boxes. | 
|---|
| 2415 | * Height reflects height of tallest node on rank. | 
|---|
| 2416 | * The extra space provided by FUDGE allows begin/endpath to create a box | 
|---|
| 2417 | * FUDGE-2 away from the node, so the routing can avoid the node and the | 
|---|
| 2418 | * box is at least 2 wide. | 
|---|
| 2419 | */ | 
|---|
| 2420 | #define FUDGE 4 | 
|---|
| 2421 |  | 
|---|
| 2422 | static boxf maximal_bbox(graph_t* g, spline_info_t* sp, node_t* vn, edge_t* ie, edge_t* oe) | 
|---|
| 2423 | { | 
|---|
| 2424 | double b, nb; | 
|---|
| 2425 | graph_t *left_cl, *right_cl; | 
|---|
| 2426 | node_t *left, *right; | 
|---|
| 2427 | boxf rv; | 
|---|
| 2428 |  | 
|---|
| 2429 | left_cl = right_cl = NULL; | 
|---|
| 2430 |  | 
|---|
| 2431 | /* give this node all the available space up to its neighbors */ | 
|---|
| 2432 | b = (double)(ND_coord(vn).x - ND_lw(vn) - FUDGE); | 
|---|
| 2433 | if ((left = neighbor(g, vn, ie, oe, -1))) { | 
|---|
| 2434 | if ((left_cl = cl_bound(g, vn, left))) | 
|---|
| 2435 | nb = GD_bb(left_cl).UR.x + (double)(sp->Splinesep); | 
|---|
| 2436 | else { | 
|---|
| 2437 | nb = (double)(ND_coord(left).x + ND_mval(left)); | 
|---|
| 2438 | if (ND_node_type(left) == NORMAL) | 
|---|
| 2439 | nb += GD_nodesep(g) / 2.; | 
|---|
| 2440 | else | 
|---|
| 2441 | nb += (double)(sp->Splinesep); | 
|---|
| 2442 | } | 
|---|
| 2443 | if (nb < b) | 
|---|
| 2444 | b = nb; | 
|---|
| 2445 | rv.LL.x = ROUND(b); | 
|---|
| 2446 | } else | 
|---|
| 2447 | rv.LL.x = MIN(ROUND(b), sp->LeftBound); | 
|---|
| 2448 |  | 
|---|
| 2449 | /* we have to leave room for our own label! */ | 
|---|
| 2450 | if ((ND_node_type(vn) == VIRTUAL) && (ND_label(vn))) | 
|---|
| 2451 | b = (double)(ND_coord(vn).x + 10); | 
|---|
| 2452 | else | 
|---|
| 2453 | b = (double)(ND_coord(vn).x + ND_rw(vn) + FUDGE); | 
|---|
| 2454 | if ((right = neighbor(g, vn, ie, oe, 1))) { | 
|---|
| 2455 | if ((right_cl = cl_bound(g, vn, right))) | 
|---|
| 2456 | nb = GD_bb(right_cl).LL.x - (double)(sp->Splinesep); | 
|---|
| 2457 | else { | 
|---|
| 2458 | nb = ND_coord(right).x - ND_lw(right); | 
|---|
| 2459 | if (ND_node_type(right) == NORMAL) | 
|---|
| 2460 | nb -= GD_nodesep(g) / 2.; | 
|---|
| 2461 | else | 
|---|
| 2462 | nb -= (double)(sp->Splinesep); | 
|---|
| 2463 | } | 
|---|
| 2464 | if (nb > b) | 
|---|
| 2465 | b = nb; | 
|---|
| 2466 | rv.UR.x = ROUND(b); | 
|---|
| 2467 | } else | 
|---|
| 2468 | rv.UR.x = MAX(ROUND(b), sp->RightBound); | 
|---|
| 2469 |  | 
|---|
| 2470 | if ((ND_node_type(vn) == VIRTUAL) && (ND_label(vn))) { | 
|---|
| 2471 | rv.UR.x -= ND_rw(vn); | 
|---|
| 2472 | if (rv.UR.x < rv.LL.x) rv.UR.x = ND_coord(vn).x; | 
|---|
| 2473 | } | 
|---|
| 2474 |  | 
|---|
| 2475 | rv.LL.y = ND_coord(vn).y - GD_rank(g)[ND_rank(vn)].ht1; | 
|---|
| 2476 | rv.UR.y = ND_coord(vn).y + GD_rank(g)[ND_rank(vn)].ht2; | 
|---|
| 2477 | return rv; | 
|---|
| 2478 | } | 
|---|
| 2479 |  | 
|---|
| 2480 | static node_t * | 
|---|
| 2481 | neighbor(graph_t* g, node_t *vn, edge_t *ie, edge_t *oe, int dir) | 
|---|
| 2482 | { | 
|---|
| 2483 | int i; | 
|---|
| 2484 | node_t *n, *rv = NULL; | 
|---|
| 2485 | rank_t *rank = &(GD_rank(g)[ND_rank(vn)]); | 
|---|
| 2486 |  | 
|---|
| 2487 | for (i = ND_order(vn) + dir; ((i >= 0) && (i < rank->n)); i += dir) { | 
|---|
| 2488 | n = rank->v[i]; | 
|---|
| 2489 | if ((ND_node_type(n) == VIRTUAL) && (ND_label(n))) { | 
|---|
| 2490 | rv = n; | 
|---|
| 2491 | break; | 
|---|
| 2492 | } | 
|---|
| 2493 | if (ND_node_type(n) == NORMAL) { | 
|---|
| 2494 | rv = n; | 
|---|
| 2495 | break; | 
|---|
| 2496 | } | 
|---|
| 2497 | if (pathscross(n, vn, ie, oe) == FALSE) { | 
|---|
| 2498 | rv = n; | 
|---|
| 2499 | break; | 
|---|
| 2500 | } | 
|---|
| 2501 | } | 
|---|
| 2502 | return rv; | 
|---|
| 2503 | } | 
|---|
| 2504 |  | 
|---|
| 2505 | static boolean pathscross(n0, n1, ie1, oe1) | 
|---|
| 2506 | node_t *n0, *n1; | 
|---|
| 2507 | edge_t *ie1, *oe1; | 
|---|
| 2508 | { | 
|---|
| 2509 | edge_t *e0, *e1; | 
|---|
| 2510 | node_t *na, *nb; | 
|---|
| 2511 | int order, cnt; | 
|---|
| 2512 |  | 
|---|
| 2513 | order = (ND_order(n0) > ND_order(n1)); | 
|---|
| 2514 | if ((ND_out(n0).size != 1) && (ND_out(n0).size != 1)) | 
|---|
| 2515 | return FALSE; | 
|---|
| 2516 | e1 = oe1; | 
|---|
| 2517 | if (ND_out(n0).size == 1 && e1) { | 
|---|
| 2518 | e0 = ND_out(n0).list[0]; | 
|---|
| 2519 | for (cnt = 0; cnt < 2; cnt++) { | 
|---|
| 2520 | if ((na = aghead(e0)) == (nb = aghead(e1))) | 
|---|
| 2521 | break; | 
|---|
| 2522 | if (order != (ND_order(na) > ND_order(nb))) | 
|---|
| 2523 | return TRUE; | 
|---|
| 2524 | if ((ND_out(na).size != 1) || (ND_node_type(na) == NORMAL)) | 
|---|
| 2525 | break; | 
|---|
| 2526 | e0 = ND_out(na).list[0]; | 
|---|
| 2527 | if ((ND_out(nb).size != 1) || (ND_node_type(nb) == NORMAL)) | 
|---|
| 2528 | break; | 
|---|
| 2529 | e1 = ND_out(nb).list[0]; | 
|---|
| 2530 | } | 
|---|
| 2531 | } | 
|---|
| 2532 | e1 = ie1; | 
|---|
| 2533 | if (ND_in(n0).size == 1 && e1) { | 
|---|
| 2534 | e0 = ND_in(n0).list[0]; | 
|---|
| 2535 | for (cnt = 0; cnt < 2; cnt++) { | 
|---|
| 2536 | if ((na = agtail(e0)) == (nb = agtail(e1))) | 
|---|
| 2537 | break; | 
|---|
| 2538 | if (order != (ND_order(na) > ND_order(nb))) | 
|---|
| 2539 | return TRUE; | 
|---|
| 2540 | if ((ND_in(na).size != 1) || (ND_node_type(na) == NORMAL)) | 
|---|
| 2541 | break; | 
|---|
| 2542 | e0 = ND_in(na).list[0]; | 
|---|
| 2543 | if ((ND_in(nb).size != 1) || (ND_node_type(nb) == NORMAL)) | 
|---|
| 2544 | break; | 
|---|
| 2545 | e1 = ND_in(nb).list[0]; | 
|---|
| 2546 | } | 
|---|
| 2547 | } | 
|---|
| 2548 | return FALSE; | 
|---|
| 2549 | } | 
|---|
| 2550 |  | 
|---|
| 2551 | #ifdef DEBUG | 
|---|
| 2552 | void showpath(path * p) | 
|---|
| 2553 | { | 
|---|
| 2554 | int i; | 
|---|
| 2555 | pointf LL, UR; | 
|---|
| 2556 |  | 
|---|
| 2557 | fprintf(stderr, "%%!PS\n"); | 
|---|
| 2558 | for (i = 0; i < p->nbox; i++) { | 
|---|
| 2559 | LL = p->boxes[i].LL; | 
|---|
| 2560 | UR = p->boxes[i].UR; | 
|---|
| 2561 | fprintf(stderr, | 
|---|
| 2562 | "newpath %.04f %.04f moveto %.04f %.04f lineto %.04f %.04f lineto %.04f %.04f lineto closepath stroke\n", | 
|---|
| 2563 | LL.x, LL.y, UR.x, LL.y, UR.x, UR.y, LL.x, UR.y); | 
|---|
| 2564 | } | 
|---|
| 2565 | fprintf(stderr, "showpage\n"); | 
|---|
| 2566 | } | 
|---|
| 2567 | #endif | 
|---|
| 2568 |  | 
|---|