|
Graphviz
2.29.20120523.0446
|
00001 /* $Id$ $Revision$ */ 00002 /* vim:set shiftwidth=4 ts=8: */ 00003 00004 /************************************************************************* 00005 * Copyright (c) 2011 AT&T Intellectual Property 00006 * All rights reserved. This program and the accompanying materials 00007 * are made available under the terms of the Eclipse Public License v1.0 00008 * which accompanies this distribution, and is available at 00009 * http://www.eclipse.org/legal/epl-v10.html 00010 * 00011 * Contributors: See CVS logs. Details at http://www.graphviz.org/ 00012 *************************************************************************/ 00013 00014 00015 #ifdef HAVE_CONFIG_H 00016 #include "config.h" 00017 #endif 00018 00019 #include "neato.h" 00020 #include "adjust.h" 00021 00022 /* For precision, scale up before algorithms, then scale down */ 00023 #define SCALE 10 00024 #define SCALE2 (SCALE/2) 00025 00026 typedef struct nitem { 00027 Dtlink_t link; 00028 int val; 00029 point pos; /* position for sorting */ 00030 node_t *np; /* base node */ 00031 node_t *cnode; /* corresponding node in constraint graph */ 00032 node_t *vnode; /* corresponding node in neighbor graph */ 00033 box bb; 00034 } nitem; 00035 00036 typedef int (*distfn) (box *, box *); 00037 typedef int (*intersectfn) (nitem *, nitem *); 00038 00039 static int cmpitem(Dt_t * d, int *p1, int *p2, Dtdisc_t * disc) 00040 { 00041 NOTUSED(d); 00042 NOTUSED(disc); 00043 00044 return (*p1 - *p2); 00045 } 00046 00047 static Dtdisc_t constr = { 00048 offsetof(nitem, val), 00049 sizeof(int), 00050 offsetof(nitem, link), 00051 NIL(Dtmake_f), 00052 NIL(Dtfree_f), 00053 (Dtcompar_f) cmpitem, 00054 NIL(Dthash_f), 00055 NIL(Dtmemory_f), 00056 NIL(Dtevent_f) 00057 }; 00058 00059 static int distY(box * b1, box * b2) 00060 { 00061 return ((b1->UR.y - b1->LL.y) + (b2->UR.y - b2->LL.y)) / 2; 00062 } 00063 00064 static int distX(box * b1, box * b2) 00065 { 00066 return ((b1->UR.x - b1->LL.x) + (b2->UR.x - b2->LL.x)) / 2; 00067 } 00068 00069 /* intersectX0: 00070 * Return true if boxes could overlap if shifted in y but don't, 00071 * or if actually overlap and an y move is smallest to remove overlap. 00072 * Otherwise (no x overlap or a x move is smaller), return false. 00073 * Assume q pos to above of p pos. 00074 */ 00075 static int intersectX0(nitem * p, nitem * q) 00076 { 00077 int xdelta, ydelta; 00078 int v = ((p->bb.LL.x <= q->bb.UR.x) && (q->bb.LL.x <= p->bb.UR.x)); 00079 if (v == 0) /* no x overlap */ 00080 return 0; 00081 if (p->bb.UR.y < q->bb.LL.y) /* but boxes don't really overlap */ 00082 return 1; 00083 ydelta = distY(&p->bb,&q->bb) - (q->pos.y - p->pos.y); 00084 if (q->pos.x >= p->pos.x) 00085 xdelta = distX(&p->bb,&q->bb) - (q->pos.x - p->pos.x); 00086 else 00087 xdelta = distX(&p->bb,&q->bb) - (p->pos.x - q->pos.x); 00088 return (ydelta <= xdelta); 00089 } 00090 00091 /* intersectY0: 00092 * Return true if boxes could overlap if shifted in x but don't, 00093 * or if actually overlap and an x move is smallest to remove overlap. 00094 * Otherwise (no y overlap or a y move is smaller), return false. 00095 * Assume q pos to right of p pos. 00096 */ 00097 static int intersectY0(nitem * p, nitem * q) 00098 { 00099 int xdelta, ydelta; 00100 int v = ((p->bb.LL.y <= q->bb.UR.y) && (q->bb.LL.y <= p->bb.UR.y)); 00101 if (v == 0) /* no y overlap */ 00102 return 0; 00103 if (p->bb.UR.x < q->bb.LL.x) /* but boxes don't really overlap */ 00104 return 1; 00105 xdelta = distX(&p->bb,&q->bb) - (q->pos.x - p->pos.x); 00106 if (q->pos.y >= p->pos.y) 00107 ydelta = distY(&p->bb,&q->bb) - (q->pos.y - p->pos.y); 00108 else 00109 ydelta = distY(&p->bb,&q->bb) - (p->pos.y - q->pos.y); 00110 return (xdelta <= ydelta); 00111 } 00112 00113 static int intersectY(nitem * p, nitem * q) 00114 { 00115 return ((p->bb.LL.y <= q->bb.UR.y) && (q->bb.LL.y <= p->bb.UR.y)); 00116 } 00117 00118 static int intersectX(nitem * p, nitem * q) 00119 { 00120 return ((p->bb.LL.x <= q->bb.UR.x) && (q->bb.LL.x <= p->bb.UR.x)); 00121 } 00122 00123 /* mapGraphs: 00124 */ 00125 static void mapGraphs(graph_t * g, graph_t * cg, distfn dist) 00126 { 00127 node_t *n; 00128 edge_t *e; 00129 edge_t *ce; 00130 node_t *t; 00131 node_t *h; 00132 nitem *tp; 00133 nitem *hp; 00134 int delta; 00135 00136 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00137 tp = (nitem *) ND_alg(n); 00138 t = tp->cnode; 00139 for (e = agfstout(g, n); e; e = agnxtout(g, e)) { 00140 hp = (nitem *) ND_alg(aghead(e)); 00141 delta = dist(&tp->bb, &hp->bb); 00142 h = hp->cnode; 00143 #ifndef WITH_CGRAPH 00144 ce = agedge(cg, t, h); 00145 #else 00146 ce = agedge(cg, t, h, NULL, 1); 00147 agbindrec(ce, "Agedgeinfo_t", sizeof(Agedgeinfo_t), TRUE); 00148 #endif 00149 ED_weight(ce) = 1; 00150 if (ED_minlen(ce) < delta) { 00151 if (ED_minlen(ce) == 0.0) { 00152 elist_append(ce, ND_out(t)); 00153 elist_append(ce, ND_in(h)); 00154 } 00155 ED_minlen(ce) = delta; 00156 } 00157 } 00158 } 00159 } 00160 00161 #ifdef DEBUG 00162 static int 00163 indegree (graph_t * g, node_t *n) 00164 { 00165 edge_t *e; 00166 int cnt = 0; 00167 for (e = agfstin(g,n); e; e = agnxtin(g,e)) cnt++; 00168 return cnt; 00169 } 00170 00171 static int 00172 outdegree (graph_t * g, node_t *n) 00173 { 00174 edge_t *e; 00175 int cnt = 0; 00176 for (e = agfstout(g,n); e; e = agnxtout(g,e)) cnt++; 00177 return cnt; 00178 } 00179 00180 static void 00181 validate(graph_t * g) 00182 { 00183 node_t *n; 00184 edge_t *e; 00185 int i, cnt; 00186 00187 cnt = 0; 00188 for (n = GD_nlist(g);n; n = ND_next(n)) { 00189 assert(outdegree(g,n) == ND_out(n).size); 00190 for (i = 0; (e = ND_out(n).list[i]); i++) { 00191 assert(e->tail == n); 00192 assert( e == agfindedge(g, n, e->head)); 00193 } 00194 assert(indegree(g,n) == ND_in(n).size); 00195 for (i = 0; (e = ND_in(n).list[i]); i++) { 00196 assert(e->head == n); 00197 assert( e == agfindedge(g, e->tail, n)); 00198 } 00199 cnt++; 00200 } 00201 00202 assert (agnnodes(g) == cnt); 00203 } 00204 #endif 00205 00206 #ifdef OLD 00207 static node_t *newNode(graph_t * g) 00208 { 00209 static int id = 0; 00210 char buf[100]; 00211 00212 sprintf(buf, "n%d", id++); 00213 return agnode(g, buf); 00214 } 00215 #endif 00216 00217 /* mkNConstraintG: 00218 * Similar to mkConstraintG, except it doesn't enforce orthogonal 00219 * ordering. If there is overlap, as defined by intersect, the 00220 * nodes will kept/pushed apart in the current order. If not, no 00221 * constraint is enforced. If a constraint edge is added, and it 00222 * corresponds to a real edge, we increase the weight in an attempt 00223 * to keep the resulting shift short. 00224 */ 00225 static graph_t *mkNConstraintG(graph_t * g, Dt_t * list, 00226 intersectfn intersect, distfn dist) 00227 { 00228 nitem *p; 00229 nitem *nxp; 00230 #ifndef WITH_CGRAPH 00231 graph_t *cg = agopen("cg", AGDIGRAPHSTRICT); 00232 #else 00233 graph_t *cg = agopen("cg", Agstrictdirected, NIL(Agdisc_t *)); 00234 agbindrec(cg, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE); // graph custom data 00235 #endif 00236 node_t *n; 00237 edge_t *e; 00238 node_t *lastn = NULL; 00239 00240 for (p = (nitem *) dtflatten(list); p; 00241 p = (nitem *) dtlink(list, (Dtlink_t *) p)) { 00242 #ifndef WITH_CGRAPH 00243 n = agnode(cg, agnameof(p->np)); /* FIX */ 00244 #else 00245 n = agnode(cg, agnameof(p->np), 1); /* FIX */ 00246 agbindrec(n, "Agnodeinfo_t", sizeof(Agnodeinfo_t), TRUE); //node custom data 00247 #endif 00248 ND_alg(n) = p; 00249 p->cnode = n; 00250 alloc_elist(0, ND_in(n)); 00251 alloc_elist(0, ND_out(n)); 00252 if (lastn) { 00253 ND_next(lastn) = n; 00254 lastn = n; 00255 } else { 00256 lastn = GD_nlist(cg) = n; 00257 } 00258 } 00259 for (p = (nitem *) dtflatten(list); p; 00260 p = (nitem *) dtlink(list, (Dtlink_t *) p)) { 00261 for (nxp = (nitem *) dtlink(link, (Dtlink_t *) p); nxp; 00262 nxp = (nitem *) dtlink(list, (Dtlink_t *) nxp)) { 00263 e = NULL; 00264 if (intersect(p, nxp)) { 00265 double delta = dist(&p->bb, &nxp->bb); 00266 #ifndef WITH_CGRAPH 00267 e = agedge(cg, p->cnode, nxp->cnode); 00268 #else 00269 e = agedge(cg, p->cnode, nxp->cnode, NULL, 1); 00270 agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), TRUE); // edge custom data 00271 #endif 00272 assert (delta <= 0xFFFF); 00273 ED_minlen(e) = delta; 00274 ED_weight(e) = 1; 00275 } 00276 if (e && agfindedge(g,p->np, nxp->np)) { 00277 ED_weight(e) = 100; 00278 } 00279 #if 0 00280 if (agfindedge(g,p->np, nxp->np)) { 00281 if (e == NULL) 00282 e = agedge(cg, p->cnode, nxp->cnode); 00283 ED_weight(e) = 100; 00284 /* If minlen < SCALE, the nodes can't conflict or there's 00285 * an overlap but it will be removed in the orthogonal pass. 00286 * So we just keep the node's basically where they are. 00287 */ 00288 if (SCALE > ED_minlen(e)) 00289 ED_minlen(e) = SCALE; 00290 } 00291 #endif 00292 } 00293 } 00294 00295 for (p = (nitem *) dtflatten(list); p; 00296 p = (nitem *) dtlink(list, (Dtlink_t *) p)) { 00297 n = p->cnode; 00298 for (e = agfstout(cg,n); e; e = agnxtout(cg,e)) { 00299 elist_append(e, ND_out(n)); 00300 elist_append(e, ND_in(aghead(e))); 00301 } 00302 } 00303 00304 /* We could remove redundant constraints here. However, the cost of doing 00305 * this may be a good deal more than the time saved in network simplex. 00306 * Also, if the graph is changed, the ND_in and ND_out data has to be 00307 * updated. 00308 */ 00309 return cg; 00310 } 00311 /* mkConstraintG: 00312 */ 00313 static graph_t *mkConstraintG(graph_t * g, Dt_t * list, 00314 intersectfn intersect, distfn dist) 00315 { 00316 nitem *p; 00317 nitem *nxt = NULL; 00318 nitem *nxp; 00319 #ifndef WITH_CGRAPH 00320 graph_t *cg = agopen("cg", AGDIGRAPHSTRICT); 00321 #else 00322 graph_t *cg = agopen("cg", Agstrictdirected, NIL(Agdisc_t *)); 00323 agbindrec(cg, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE); // graph custom data 00324 #endif 00325 graph_t *vg; 00326 node_t *prev = NULL; 00327 node_t *root = NULL; 00328 node_t *n = NULL; 00329 edge_t *e; 00330 int lcnt, cnt; 00331 int oldval = -INT_MAX; 00332 #ifdef OLD 00333 double root_val; 00334 #endif 00335 node_t *lastn = NULL; 00336 00337 /* count distinct nodes */ 00338 cnt = 0; 00339 for (p = (nitem *) dtflatten(list); p; 00340 p = (nitem *) dtlink(list, (Dtlink_t *) p)) { 00341 if (oldval != p->val) { 00342 oldval = p->val; 00343 cnt++; 00344 } 00345 } 00346 00347 /* construct basic chain to enforce left to right order */ 00348 oldval = -INT_MAX; 00349 lcnt = 0; 00350 for (p = (nitem *) dtflatten(list); p; 00351 p = (nitem *) dtlink(list, (Dtlink_t *) p)) { 00352 if (oldval != p->val) { 00353 oldval = p->val; 00354 /* n = newNode (cg); */ 00355 #ifndef WITH_CGRAPH 00356 n = agnode(cg, agnameof(p->np)); /* FIX */ 00357 #else 00358 n = agnode(cg, agnameof(p->np), 1); /* FIX */ 00359 agbindrec(n, "Agnodeinfo_t", sizeof(Agnodeinfo_t), TRUE); //node custom data 00360 #endif 00361 ND_alg(n) = p; 00362 if (root) { 00363 ND_next(lastn) = n; 00364 lastn = n; 00365 } else { 00366 root = n; 00367 #ifdef OLD 00368 root_val = p->val; 00369 #endif 00370 lastn = GD_nlist(cg) = n; 00371 } 00372 alloc_elist(lcnt, ND_in(n)); 00373 if (prev) { 00374 if (prev == root) 00375 alloc_elist(2 * (cnt - 1), ND_out(prev)); 00376 else 00377 alloc_elist(cnt - lcnt - 1, ND_out(prev)); 00378 #ifndef WITH_CGRAPH 00379 e = agedge(cg, prev, n); 00380 #else 00381 e = agedge(cg, prev, n, NULL, 1); 00382 agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), TRUE); // edge custom data 00383 #endif 00384 ED_minlen(e) = SCALE; 00385 ED_weight(e) = 1; 00386 elist_append(e, ND_out(prev)); 00387 elist_append(e, ND_in(n)); 00388 } 00389 lcnt++; 00390 prev = n; 00391 } 00392 p->cnode = n; 00393 } 00394 alloc_elist(0, ND_out(prev)); 00395 00396 /* add immediate right neighbor constraints 00397 * Construct visibility graph, then perform transitive reduction. 00398 * Remaining outedges are immediate right neighbors. 00399 * FIX: Incremental algorithm to construct trans. reduction? 00400 */ 00401 #ifndef WITH_CGRAPH 00402 vg = agopen("vg", AGDIGRAPHSTRICT); 00403 #else 00404 vg = agopen("vg", Agstrictdirected, NIL(Agdisc_t *)); 00405 #endif 00406 for (p = (nitem *) dtflatten(list); p; 00407 p = (nitem *) dtlink(list, (Dtlink_t *) p)) { 00408 #ifndef WITH_CGRAPH 00409 n = agnode(vg, agnameof(p->np)); /* FIX */ 00410 #else 00411 n = agnode(vg, agnameof(p->np), 1); /* FIX */ 00412 agbindrec(n, "Agnodeinfo_t", sizeof(Agnodeinfo_t), TRUE); //node custom data 00413 #endif 00414 p->vnode = n; 00415 ND_alg(n) = p; 00416 } 00417 oldval = -INT_MAX; 00418 for (p = (nitem *) dtflatten(list); p; 00419 p = (nitem *) dtlink(list, (Dtlink_t *) p)) { 00420 if (oldval != p->val) { /* new pos: reset nxt */ 00421 oldval = p->val; 00422 for (nxt = (nitem *) dtlink(link, (Dtlink_t *) p); nxt; 00423 nxt = (nitem *) dtlink(list, (Dtlink_t *) nxt)) { 00424 if (nxt->val != oldval) 00425 break; 00426 } 00427 if (!nxt) 00428 break; 00429 } 00430 for (nxp = nxt; nxp; 00431 nxp = (nitem *) dtlink(list, (Dtlink_t *) nxp)) { 00432 if (intersect(p, nxp)) 00433 #ifndef WITH_CGRAPH 00434 agedge(vg, p->vnode, nxp->vnode); 00435 #else 00436 agedge(vg, p->vnode, nxp->vnode, NULL, 1); 00437 #endif 00438 } 00439 } 00440 00441 /* Remove redundant constraints here. However, the cost of doing this 00442 * may be a good deal more than the time saved in network simplex. Also, 00443 * if the graph is changed, the ND_in and ND_out data has to be updated. 00444 */ 00445 mapGraphs(vg, cg, dist); 00446 agclose(vg); 00447 00448 /* add dummy constraints for absolute values and initial positions */ 00449 #ifdef OLD 00450 for (n = agfstnode(cg); n; n = agnxtnode(cg, n)) { 00451 node_t *vn; /* slack node for absolute value */ 00452 node_t *an; /* node representing original position */ 00453 00454 p = (nitem *) ND_alg(n); 00455 if ((n == root) || (!p)) 00456 continue; 00457 vn = newNode(cg); 00458 ND_next(lastn) = vn; 00459 lastn = vn; 00460 alloc_elist(0, ND_out(vn)); 00461 alloc_elist(2, ND_in(vn)); 00462 an = newNode(cg); 00463 ND_next(lastn) = an; 00464 lastn = an; 00465 alloc_elist(1, ND_in(an)); 00466 alloc_elist(1, ND_out(an)); 00467 00468 #ifndef WITH_CGRAPH 00469 e = agedge(cg, root, an); 00470 #else 00471 e = agedge(cg, root, an, 1); 00472 #endif 00473 ED_minlen(e) = p->val - root_val; 00474 elist_append(e, ND_out(root)); 00475 elist_append(e, ND_in(an)); 00476 00477 #ifndef WITH_CGRAPH 00478 e = agedge(cg, an, vn); 00479 #else 00480 e = agedge(cg, an, vn, 1); 00481 #endif 00482 elist_append(e, ND_out(an)); 00483 elist_append(e, ND_in(vn)); 00484 00485 #ifndef WITH_CGRAPH 00486 e = agedge(cg, n, vn); 00487 #else 00488 e = agedge(cg, n, vn, 1); 00489 #endif 00490 elist_append(e, ND_out(n)); 00491 elist_append(e, ND_in(vn)); 00492 } 00493 #endif /* OLD */ 00494 00495 return cg; 00496 } 00497 00498 static void closeGraph(graph_t * cg) 00499 { 00500 node_t *n; 00501 for (n = agfstnode(cg); n; n = agnxtnode(cg, n)) { 00502 free_list(ND_in(n)); 00503 free_list(ND_out(n)); 00504 } 00505 agclose(cg); 00506 } 00507 00508 /* constrainX: 00509 * Create the X constrains and solve. We use a linear objective function 00510 * (absolute values rather than squares), so we can reuse network simplex. 00511 * The constraints are encoded as a dag with edges having a minimum length. 00512 */ 00513 static void constrainX(graph_t* g, nitem* nlist, int nnodes, intersectfn ifn, 00514 int ortho) 00515 { 00516 Dt_t *list = dtopen(&constr, Dtobag); 00517 nitem *p = nlist; 00518 graph_t *cg; 00519 int i; 00520 00521 for (i = 0; i < nnodes; i++) { 00522 p->val = p->pos.x; 00523 dtinsert(list, p); 00524 p++; 00525 } 00526 if (ortho) 00527 cg = mkConstraintG(g, list, ifn, distX); 00528 else 00529 cg = mkNConstraintG(g, list, ifn, distX); 00530 rank(cg, 2, INT_MAX); 00531 00532 p = nlist; 00533 for (i = 0; i < nnodes; i++) { 00534 int newpos, oldpos, delta; 00535 oldpos = p->pos.x; 00536 newpos = ND_rank(p->cnode); 00537 delta = newpos - oldpos; 00538 p->pos.x = newpos; 00539 p->bb.LL.x += delta; 00540 p->bb.UR.x += delta; 00541 p++; 00542 } 00543 00544 closeGraph(cg); 00545 dtclose(list); 00546 } 00547 00548 /* constrainY: 00549 * See constrainX. 00550 */ 00551 static void constrainY(graph_t* g, nitem* nlist, int nnodes, intersectfn ifn, 00552 int ortho) 00553 { 00554 Dt_t *list = dtopen(&constr, Dtobag); 00555 nitem *p = nlist; 00556 graph_t *cg; 00557 int i; 00558 00559 for (i = 0; i < nnodes; i++) { 00560 p->val = p->pos.y; 00561 dtinsert(list, p); 00562 p++; 00563 } 00564 if (ortho) 00565 cg = mkConstraintG(g, list, ifn, distY); 00566 else 00567 cg = mkNConstraintG(g, list, ifn, distY); 00568 rank(cg, 2, INT_MAX); 00569 #ifdef DEBUG 00570 { 00571 #ifndef WITH_CGRAPH 00572 Agsym_t *mlsym = agedgeattr(cg, "minlen", ""); 00573 Agsym_t *rksym = agnodeattr(cg, "rank", ""); 00574 #else 00575 Agsym_t *mlsym = agattr(cg, AGEDGE, "minlen", ""); 00576 Agsym_t *rksym = agattr(cg, AGNODE, "rank", ""); 00577 #endif 00578 char buf[100]; 00579 node_t *n; 00580 edge_t *e; 00581 for (n = agfstnode(cg); n; n = agnxtnode(cg, n)) { 00582 sprintf(buf, "%d", ND_rank(n)); 00583 agxset(n, rksym->index, buf); 00584 for (e = agfstedge(cg, n); e; e = agnxtedge(cg, e, n)) { 00585 sprintf(buf, "%d", ED_minlen(e)); 00586 agxset(e, mlsym->index, buf); 00587 } 00588 } 00589 } 00590 #endif 00591 00592 p = nlist; 00593 for (i = 0; i < nnodes; i++) { 00594 int newpos, oldpos, delta; 00595 oldpos = p->pos.y; 00596 newpos = ND_rank(p->cnode); 00597 delta = newpos - oldpos; 00598 p->pos.y = newpos; 00599 p->bb.LL.y += delta; 00600 p->bb.UR.y += delta; 00601 p++; 00602 } 00603 00604 closeGraph(cg); 00605 dtclose(list); 00606 } 00607 00608 #define overlap(pb,qb) \ 00609 ((pb.LL.x <= qb.UR.x) && (qb.LL.x <= pb.UR.x) && \ 00610 (pb.LL.y <= qb.UR.y) && (qb.LL.y <= pb.UR.y)) 00611 00612 /* overlaps: 00613 */ 00614 static int overlaps(nitem * p, int cnt) 00615 { 00616 int i, j; 00617 nitem *pi = p; 00618 nitem *pj; 00619 00620 for (i = 0; i < cnt - 1; i++) { 00621 pj = pi + 1; 00622 for (j = i + 1; j < cnt; j++) { 00623 if (overlap(pi->bb, pj->bb)) 00624 return 1; 00625 pj++; 00626 } 00627 pi++; 00628 } 00629 return 0; 00630 } 00631 00632 /* initItem: 00633 */ 00634 static void initItem(node_t * n, nitem * p, expand_t margin) 00635 { 00636 int x = POINTS(SCALE * ND_pos(n)[0]); 00637 int y = POINTS(SCALE * ND_pos(n)[1]); 00638 int w2, h2; 00639 box b; 00640 00641 if (margin.doAdd) { 00642 w2 = SCALE * (POINTS(ND_width(n)/2.0) + margin.x); 00643 h2 = SCALE * (POINTS(ND_height(n)/2.0) + margin.y); 00644 } 00645 else { 00646 w2 = POINTS(margin.x * SCALE2 * ND_width(n)); 00647 h2 = POINTS(margin.y * SCALE2 * ND_height(n)); 00648 } 00649 00650 b.LL.x = x - w2; 00651 b.LL.y = y - h2; 00652 b.UR.x = x + w2; 00653 b.UR.y = y + h2; 00654 00655 p->pos.x = x; 00656 p->pos.y = y; 00657 p->np = n; 00658 p->bb = b; 00659 } 00660 00661 /* cAdjust: 00662 * Use optimization to remove overlaps. 00663 * Modifications; 00664 * - do y;x then x;y and use the better one 00665 * - for all overlaps (or if overlap with leftmost nodes), add a constraint; 00666 * constraint could move both x and y away, or the smallest, or some 00667 * mixture. 00668 * - follow by a scale down using actual shapes 00669 * We use an optimization based on Marriott, Stuckey, Tam and He, 00670 * "Removing Node Overlapping in Graph Layout Using Constrained Optimization", 00671 * Constraints,8(2):143--172, 2003. 00672 * We solve 2 constraint problem, one in X, one in Y. In each dimension, 00673 * we require relative positions to remain the same. That is, if two nodes 00674 * have the same x originally, they have the same x at the end, and if one 00675 * node is to the left of another, it remains to the left. In addition, if 00676 * two nodes could overlap by moving their X coordinates, we insert a constraint * to keep the two nodes sufficiently apart. Similarly, for Y. 00677 * 00678 * mode = AM_ORTHOXY => first X, then Y 00679 * mode = AM_ORTHOYX => first Y, then X 00680 * mode = AM_ORTHO => first X, then Y 00681 * mode = AM_ORTHO_YX => first Y, then X 00682 * In the last 2 cases, relax the constraints as follows: during the X pass, 00683 * if two nodes actually intersect and a smaller move in the Y direction 00684 * will remove the overlap, we don't force the nodes apart in the X direction, 00685 * but leave it for the Y pass to remove any remaining overlaps. Without this, 00686 * the X pass will remove all overlaps, and the Y pass only compresses in the 00687 * Y direction, causing a skewing of the aspect ratio. 00688 * 00689 * mode = AM_ORTHOXY => first X, then Y 00690 * mode = AM_ORTHOYX => first Y, then X 00691 * mode = AM_ORTHO => first X, then Y 00692 * mode = AM_ORTHO_YX => first Y, then X 00693 */ 00694 int cAdjust(graph_t * g, int mode) 00695 { 00696 expand_t margin; 00697 int ret, i, nnodes = agnnodes(g); 00698 nitem *nlist = N_GNEW(nnodes, nitem); 00699 nitem *p = nlist; 00700 node_t *n; 00701 00702 margin = sepFactor (g); 00703 00704 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00705 initItem(n, p, margin); 00706 p++; 00707 } 00708 00709 if (overlaps(nlist, nnodes)) { 00710 point pt; 00711 00712 switch ((adjust_mode)mode) { 00713 case AM_ORTHOXY: 00714 constrainX(g, nlist, nnodes, intersectY, 1); 00715 constrainY(g, nlist, nnodes, intersectX, 1); 00716 break; 00717 case AM_ORTHOYX: 00718 constrainY(g, nlist, nnodes, intersectX, 1); 00719 constrainX(g, nlist, nnodes, intersectY, 1); 00720 break; 00721 case AM_ORTHO : 00722 constrainX(g, nlist, nnodes, intersectY0, 1); 00723 constrainY(g, nlist, nnodes, intersectX, 1); 00724 case AM_ORTHO_YX : 00725 constrainY(g, nlist, nnodes, intersectX0, 1); 00726 constrainX(g, nlist, nnodes, intersectY, 1); 00727 case AM_PORTHOXY: 00728 constrainX(g, nlist, nnodes, intersectY, 0); 00729 constrainY(g, nlist, nnodes, intersectX, 0); 00730 break; 00731 case AM_PORTHOYX: 00732 constrainY(g, nlist, nnodes, intersectX, 0); 00733 constrainX(g, nlist, nnodes, intersectY, 0); 00734 break; 00735 case AM_PORTHO_YX : 00736 constrainY(g, nlist, nnodes, intersectX0, 0); 00737 constrainX(g, nlist, nnodes, intersectY, 0); 00738 break; 00739 case AM_PORTHO : 00740 default : 00741 constrainX(g, nlist, nnodes, intersectY0, 0); 00742 constrainY(g, nlist, nnodes, intersectX, 0); 00743 break; 00744 } 00745 p = nlist; 00746 for (i = 0; i < nnodes; i++) { 00747 n = p->np; 00748 pt = p->pos; 00749 ND_pos(n)[0] = PS2INCH(pt.x) / SCALE; 00750 ND_pos(n)[1] = PS2INCH(pt.y) / SCALE; 00751 p++; 00752 } 00753 ret = 1; 00754 } 00755 else ret = 0; 00756 free(nlist); 00757 return ret; 00758 } 00759 00760 typedef struct { 00761 pointf pos; /* position for sorting */ 00762 boxf bb; 00763 double wd2; 00764 double ht2; 00765 node_t *np; 00766 } info; 00767 00768 typedef int (*sortfn_t) (const void *, const void *); 00769 00770 static int sortf(pointf * p, pointf * q) 00771 { 00772 if (p->x < q->x) 00773 return -1; 00774 else if (p->x > q->x) 00775 return 1; 00776 else if (p->y < q->y) 00777 return -1; 00778 else if (p->y > q->y) 00779 return 1; 00780 else 00781 return 0; 00782 } 00783 00784 static double compress(info * nl, int nn) 00785 { 00786 info *p = nl; 00787 info *q; 00788 int i, j; 00789 double s, sc = 0; 00790 pointf pt; 00791 00792 for (i = 0; i < nn; i++) { 00793 q = p + 1; 00794 for (j = i + 1; j < nn; j++) { 00795 if (overlap(p->bb, q->bb)) 00796 return 0; 00797 if (p->pos.x == q->pos.x) 00798 pt.x = HUGE_VAL; 00799 else { 00800 pt.x = (p->wd2 + q->wd2) / fabs(p->pos.x - q->pos.x); 00801 } 00802 if (p->pos.y == q->pos.y) 00803 pt.y = HUGE_VAL; 00804 else { 00805 pt.y = (p->ht2 + q->ht2) / fabs(p->pos.y - q->pos.y); 00806 } 00807 if (pt.y < pt.x) 00808 s = pt.y; 00809 else 00810 s = pt.x; 00811 if (s > sc) 00812 sc = s; 00813 q++; 00814 } 00815 p++; 00816 } 00817 return sc; 00818 } 00819 00820 static pointf *mkOverlapSet(info * nl, int nn, int *cntp) 00821 { 00822 info *p = nl; 00823 info *q; 00824 int sz = nn; 00825 pointf *S = N_GNEW(sz + 1, pointf); 00826 int i, j; 00827 int cnt = 0; 00828 00829 for (i = 0; i < nn; i++) { 00830 q = p + 1; 00831 for (j = i + 1; j < nn; j++) { 00832 if (overlap(p->bb, q->bb)) { 00833 pointf pt; 00834 if (cnt == sz) { 00835 sz += nn; 00836 S = RALLOC(sz + 1, S, pointf); 00837 } 00838 if (p->pos.x == q->pos.x) 00839 pt.x = HUGE_VAL; 00840 else { 00841 pt.x = (p->wd2 + q->wd2) / fabs(p->pos.x - q->pos.x); 00842 if (pt.x < 1) 00843 pt.x = 1; 00844 } 00845 if (p->pos.y == q->pos.y) 00846 pt.y = HUGE_VAL; 00847 else { 00848 pt.y = (p->ht2 + q->ht2) / fabs(p->pos.y - q->pos.y); 00849 if (pt.y < 1) 00850 pt.y = 1; 00851 } 00852 S[++cnt] = pt; 00853 } 00854 q++; 00855 } 00856 p++; 00857 } 00858 00859 S = RALLOC(cnt + 1, S, pointf); 00860 *cntp = cnt; 00861 return S; 00862 } 00863 00864 static pointf computeScaleXY(pointf * aarr, int m) 00865 { 00866 pointf *barr; 00867 double cost, bestcost; 00868 int k, best = 0; 00869 pointf scale; 00870 00871 aarr[0].x = 1; 00872 aarr[0].y = HUGE_VAL; 00873 qsort(aarr + 1, m, sizeof(pointf), (sortfn_t) sortf); 00874 00875 barr = N_GNEW(m + 1, pointf); 00876 barr[m].x = aarr[m].x; 00877 barr[m].y = 1; 00878 for (k = m - 1; k >= 0; k--) { 00879 barr[k].x = aarr[k].x; 00880 barr[k].y = MAX(aarr[k + 1].y, barr[k + 1].y); 00881 } 00882 00883 bestcost = HUGE_VAL; 00884 for (k = 0; k <= m; k++) { 00885 cost = barr[k].x * barr[k].y; 00886 if (cost < bestcost) { 00887 bestcost = cost; 00888 best = k; 00889 } 00890 } 00891 assert(bestcost < HUGE_VAL); 00892 scale.x = barr[best].x; 00893 scale.y = barr[best].y; 00894 00895 return scale; 00896 } 00897 00898 /* computeScale: 00899 * For each (x,y) in aarr, scale has to be bigger than the smallest one. 00900 * So, the scale is the max min. 00901 */ 00902 static double computeScale(pointf * aarr, int m) 00903 { 00904 int i; 00905 double sc = 0; 00906 double v; 00907 pointf p; 00908 00909 aarr++; 00910 for (i = 1; i <= m; i++) { 00911 p = *aarr++; 00912 v = MIN(p.x, p.y); 00913 if (v > sc) 00914 sc = v; 00915 } 00916 return sc; 00917 } 00918 00919 /* scAdjust: 00920 * Scale the layout. 00921 * equal > 0 => scale uniformly in x and y to remove overlaps 00922 * equal = 0 => scale separately in x and y to remove overlaps 00923 * equal < 0 => scale down uniformly in x and y to remove excess space 00924 * The last assumes there are no overlaps at present. 00925 * Based on Marriott, Stuckey, Tam and He, 00926 * "Removing Node Overlapping in Graph Layout Using Constrained Optimization", 00927 * Constraints,8(2):143--172, 2003. 00928 */ 00929 int scAdjust(graph_t * g, int equal) 00930 { 00931 int nnodes = agnnodes(g); 00932 info *nlist = N_GNEW(nnodes, info); 00933 info *p = nlist; 00934 node_t *n; 00935 pointf s; 00936 int i; 00937 expand_t margin; 00938 pointf *aarr; 00939 int m; 00940 00941 margin = sepFactor (g); 00942 if (margin.doAdd) { 00943 /* we use inches below */ 00944 margin.x = PS2INCH(margin.x); 00945 margin.y = PS2INCH(margin.y); 00946 } 00947 00948 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00949 double w2, h2; 00950 if (margin.doAdd) { 00951 w2 = (ND_width(n) / 2.0) + margin.x; 00952 h2 = (ND_height(n) / 2.0) + margin.y; 00953 } 00954 else { 00955 w2 = margin.x * ND_width(n) / 2.0; 00956 h2 = margin.y * ND_height(n) / 2.0; 00957 } 00958 p->pos.x = ND_pos(n)[0]; 00959 p->pos.y = ND_pos(n)[1]; 00960 p->bb.LL.x = p->pos.x - w2; 00961 p->bb.LL.y = p->pos.y - h2; 00962 p->bb.UR.x = p->pos.x + w2; 00963 p->bb.UR.y = p->pos.y + h2; 00964 p->wd2 = w2; 00965 p->ht2 = h2; 00966 p->np = n; 00967 p++; 00968 } 00969 00970 if (equal < 0) { 00971 s.x = s.y = compress(nlist, nnodes); 00972 if (s.x == 0) { /* overlaps exist */ 00973 free(nlist); 00974 return 0; 00975 } 00976 fprintf(stderr, "compress %g \n", s.x); 00977 } else { 00978 aarr = mkOverlapSet(nlist, nnodes, &m); 00979 00980 if (m == 0) { /* no overlaps */ 00981 free(aarr); 00982 free(nlist); 00983 return 0; 00984 } 00985 00986 if (equal) { 00987 s.x = s.y = computeScale(aarr, m); 00988 } else { 00989 s = computeScaleXY(aarr, m); 00990 } 00991 free(aarr); 00992 } 00993 00994 p = nlist; 00995 for (i = 0; i < nnodes; i++) { 00996 ND_pos(p->np)[0] = s.x * p->pos.x; 00997 ND_pos(p->np)[1] = s.y * p->pos.y; 00998 p++; 00999 } 01000 01001 free(nlist); 01002 return 1; 01003 }
1.7.5