/misc/src/release/graphviz-2.18-1/src/graphviz-2.18/lib/neatogen/constraint.c

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

Generated on Mon Mar 31 19:03:27 2008 for Graphviz by  doxygen 1.5.1