Graphviz  2.29.20120523.0446
lib/neatogen/constraint.c
Go to the documentation of this file.
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 }