Graphviz  2.29.20120523.0446
lib/fdpgen/clusteredges.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 /* clusteredges.c:
00016  * Written by Emden R. Gansner
00017  *
00018  * Code for handling spline edges around clusters.
00019  */
00020 
00021 /* uses PRIVATE interface */
00022 #define FDP_PRIVATE 1
00023 
00024 #ifdef HAVE_CONFIG_H
00025 #include <config.h>
00026 #endif
00027 
00028 #include <clusteredges.h>
00029 #include <fdp.h>
00030 #include <neatoprocs.h>
00031 #include "vispath.h"
00032 
00033 typedef struct {
00034     int cnt;
00035     int sz;
00036     Ppoly_t **obs;
00037 } objlist;
00038 
00039 /* addObj:
00040  * Add an object to the list. The array is increased if necessary.
00041  */
00042 #define INIT_SZ 100
00043 
00044 #ifdef DEBUG
00045 static void dumpObj(Ppoly_t * p)
00046 {
00047     int j;
00048     Ppoint_t pt;
00049     for (j = 0; j < p->pn; j++) {
00050         pt = p->ps[j];
00051         fprintf(stderr, " %.5g %.5g", pt.x, pt.y);
00052     }
00053     fputs("\n", stderr);
00054 }
00055 
00056 static void dumpObjlist(objlist * l)
00057 {
00058     int i;
00059     for (i = 0; i < l->cnt; i++) {
00060         dumpObj(l->obs[i]);
00061     }
00062 }
00063 #endif
00064 
00065 static void addObj(objlist * l, Ppoly_t * obj)
00066 {
00067     if (l->sz == l->cnt) {
00068         if (l->obs) {
00069             l->sz *= 2;
00070             l->obs = RALLOC(l->sz, l->obs, Ppoly_t *);
00071         } else {
00072             l->obs = N_GNEW(INIT_SZ, Ppoly_t *);
00073             l->sz = INIT_SZ;
00074         }
00075     }
00076     l->obs[l->cnt++] = obj;
00077 }
00078 
00079 /* freeObjlist:
00080  * Release memory.
00081  */
00082 static void freeObjlist(objlist * l)
00083 {
00084     if (l) {
00085         free(l->obs);
00086         free(l);
00087     }
00088 }
00089 
00090 /* resetObjlist:
00091  * Reset objlist so it can be reused, using
00092  * the same memory.
00093  */
00094 static void resetObjlist(objlist * l)
00095 {
00096     l->cnt = 0;
00097 }
00098 
00099 /* makeClustObs:
00100  * Create an obstacle corresponding to a cluster's bbox.
00101  */
00102 static Ppoly_t *makeClustObs(graph_t * g, expand_t* pm)
00103 {
00104     Ppoly_t *obs = NEW(Ppoly_t);
00105     boxf bb;
00106     boxf newbb;
00107     Ppoint_t ctr;
00108 
00109     bb = GD_bb(g);
00110     obs->pn = 4;
00111     obs->ps = N_NEW(4, Ppoint_t);
00112 
00113     ctr.x = (bb.UR.x + bb.LL.x) / 2.0;
00114     ctr.y = (bb.UR.y + bb.LL.y) / 2.0;
00115 
00116     if (pm->doAdd) {
00117         newbb.UR.x = bb.UR.x + pm->x;
00118         newbb.UR.y = bb.UR.y + pm->y;
00119         newbb.LL.x = bb.LL.x - pm->x;
00120         newbb.LL.y = bb.LL.y - pm->y;
00121     }
00122     else {
00123         double deltax = pm->x - 1.0;
00124         double deltay = pm->y - 1.0;
00125         newbb.UR.x = pm->x * bb.UR.x - deltax * ctr.x;
00126         newbb.UR.y = pm->y * bb.UR.y - deltay * ctr.y;
00127         newbb.LL.x = pm->x * bb.LL.x - deltax * ctr.x;
00128         newbb.LL.y = pm->y * bb.LL.y - deltay * ctr.y;
00129     }
00130 
00131     /* CW order */
00132     obs->ps[0].x = newbb.LL.x;
00133     obs->ps[0].y = newbb.LL.y;
00134     obs->ps[1].x = newbb.LL.x;
00135     obs->ps[1].y = newbb.UR.y;
00136     obs->ps[2].x = newbb.UR.x;
00137     obs->ps[2].y = newbb.UR.y;
00138     obs->ps[3].x = newbb.UR.x;
00139     obs->ps[3].y = newbb.LL.y;
00140 
00141     return obs;
00142 }
00143 
00144 /* addGraphObjs:
00145  * Add all top-level clusters and nodes with g as their smallest
00146  * containing graph to the list l.
00147  * Don't add any objects equal to tex or hex.
00148  * Return the list.
00149  */
00150 static void
00151 addGraphObjs(objlist * l, graph_t * g, void *tex, void *hex, expand_t* pm)
00152 {
00153     node_t *n;
00154     graph_t *sg;
00155     int i;
00156 
00157     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00158         if ((PARENT(n) == g) && (n != tex) && (n != hex)
00159             && !IS_CLUST_NODE(n)) {
00160             addObj(l, makeObstacle(n, pm));
00161         }
00162     }
00163     for (i = 1; i <= GD_n_cluster(g); i++) {
00164         sg = GD_clust(g)[i];
00165         if ((sg != tex) && (sg != hex)) {
00166             addObj(l, makeClustObs(sg, pm));
00167         }
00168     }
00169 }
00170 
00171 /* raiseLevel:
00172  * Add barrier objects for node n, in graph *gp of level maxlvl, up to
00173  * level minlvl. 
00174  * Assume maxlvl > minlvl.
00175  * Return appended list, plus pass back last cluster processed in gp.
00176  */
00177 static void
00178 raiseLevel(objlist * l, int maxlvl, void *ex, int minlvl, graph_t ** gp,
00179            expand_t* pm)
00180 {
00181     graph_t *g = *gp;
00182     int i;
00183 
00184     for (i = maxlvl; i > minlvl; i--) {
00185         addGraphObjs(l, g, ex, NULL, pm);
00186         ex = g;
00187         g = GPARENT(g);
00188     }
00189     *gp = (graph_t *) ex;
00190 }
00191 
00192 /* objectList:
00193  * Create array of all objects (nodes and clusters) to be avoided
00194  * when routing edge e. Make sure it never adds the endpoints of the
00195  * edge, or any graph containing the endpoints.
00196  * Return the list.
00197  * Assume e is not a loop.
00198  */
00199 static objlist *objectList(edge_t * ep, expand_t* pm)
00200 {
00201     node_t *h = aghead(ep);
00202     node_t *t = agtail(ep);
00203     graph_t *hg = PARENT(h);
00204     graph_t *tg = PARENT(t);
00205     int hlevel;
00206     int tlevel;
00207     void *hex;                  /* Objects to be excluded from list */
00208     void *tex;
00209     objlist *list = NEW(objlist);
00210 
00211     /* If either endpoint is a cluster node, we move up one level */
00212     if (IS_CLUST_NODE(h)) {
00213         hex = hg;
00214         hg = GPARENT(hg);
00215     } else
00216         hex = h;
00217     if (IS_CLUST_NODE(t)) {
00218         tex = tg;
00219         tg = GPARENT(tg);
00220     } else
00221         tex = t;
00222 
00223     hlevel = LEVEL(hg);
00224     tlevel = LEVEL(tg);
00225     if (hlevel > tlevel) {
00226         raiseLevel(list, hlevel, hex, tlevel, &hg, pm);
00227         hex = hg;
00228         hg = GPARENT(hg);
00229     } else if (tlevel > hlevel) {
00230         raiseLevel(list, tlevel, tex, hlevel, &tg, pm);
00231         tex = tg;
00232         tg = GPARENT(tg);
00233     }
00234 
00235     /* hg and tg always have the same level */
00236     while (hg != tg) {
00237         addGraphObjs(list, hg, NULL, hex, pm);
00238         addGraphObjs(list, tg, tex, NULL, pm);
00239         hex = hg;
00240         hg = GPARENT(hg);
00241         tex = tg;
00242         tg = GPARENT(tg);
00243     }
00244     addGraphObjs(list, tg, tex, hex, pm);
00245 
00246     return list;
00247 }
00248 
00249 /* compoundEdges:
00250  * Construct edges as splines, avoiding clusters when required.
00251  * We still don't implement spline multiedges, so we just copy
00252  * one spline to all the other edges.
00253  * Returns 0 on success. Failure indicates the obstacle configuration
00254  * for some edge had overlaps.
00255  */
00256 int compoundEdges(graph_t * g, expand_t* pm, int edgetype)
00257 {
00258     node_t *n;
00259     node_t *head;
00260     edge_t *e;
00261     edge_t *e0;
00262     objlist *objl = NULL;
00263     path *P = NULL;
00264     vconfig_t *vconfig;
00265     int rv = 0;
00266 
00267     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00268         for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
00269             head = aghead(e);
00270             if ((n == head) && ED_count(e)) {   /* self arc */
00271                 if (!P) {
00272                     P = NEW(path);
00273                     P->boxes = N_NEW(agnnodes(g) + 20 * 2 * 9, boxf);
00274                 }
00275                 makeSelfArcs(P, e, GD_nodesep(g));
00276             } else if (ED_count(e)) {
00277                 objl = objectList(e, pm);
00278                 if (Plegal_arrangement(objl->obs, objl->cnt)) {
00279                     vconfig = Pobsopen(objl->obs, objl->cnt);
00280                     if (!vconfig) {
00281                         agerr(AGWARN, "compoundEdges: could not construct obstacles - falling back to straight line edges\n");
00282                         rv = 1;
00283                         continue;
00284                     }
00285                 }
00286                 else {
00287                     if (Verbose)
00288                         fprintf(stderr,
00289                                 "nodes touch - falling back to straight line edges\n");
00290                     rv = 1;
00291                     continue;
00292                 }
00293 
00294                 /* For efficiency, it should be possible to copy the spline
00295                  * from the first edge to the rest. However, one has to deal
00296                  * with change in direction, different arrowheads, labels, etc.
00297                  */
00298                 for (e0 = e; e0; e0 = ED_to_virt(e0)) {
00299                     ED_path(e0) =
00300                         getPath(e0, vconfig, 0, objl->obs, objl->cnt);
00301                     makeSpline(g, e0, objl->obs, objl->cnt, FALSE);
00302                 }
00303                 resetObjlist(objl);
00304             }
00305         }
00306     }
00307     freeObjlist(objl);
00308     if (P) {
00309         free(P->boxes);
00310         free(P);
00311     }
00312     return rv;
00313 }