Graphviz  2.29.20120524.0446
lib/common/postproc.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 #include "render.h"
00016 #include "xlabels.h"
00017 
00018 static int Rankdir;
00019 static boolean Flip;
00020 static pointf Offset;
00021 
00022 static void place_flip_graph_label(graph_t * g);
00023 
00024 #define M1 \
00025 "/pathbox {\n\
00026     /Y exch %.5g sub def\n\
00027     /X exch %.5g sub def\n\
00028     /y exch %.5g sub def\n\
00029     /x exch %.5g sub def\n\
00030     newpath x y moveto\n\
00031     X y lineto\n\
00032     X Y lineto\n\
00033     x Y lineto\n\
00034     closepath stroke\n \
00035 } def\n\
00036 /dbgstart { gsave %.5g %.5g translate } def\n\
00037 /arrowlength 10 def\n\
00038 /arrowwidth arrowlength 2 div def\n\
00039 /arrowhead {\n\
00040     gsave\n\
00041     rotate\n\
00042     currentpoint\n\
00043     newpath\n\
00044     moveto\n\
00045     arrowlength arrowwidth 2 div rlineto\n\
00046     0 arrowwidth neg rlineto\n\
00047     closepath fill\n\
00048     grestore\n\
00049 } bind def\n\
00050 /makearrow {\n\
00051     currentpoint exch pop sub exch currentpoint pop sub atan\n\
00052     arrowhead\n\
00053 } bind def\n\
00054 /point {\
00055     newpath\
00056     2 0 360 arc fill\
00057 } def\
00058 /makevec {\n\
00059     /Y exch def\n\
00060     /X exch def\n\
00061     /y exch def\n\
00062     /x exch def\n\
00063     newpath x y moveto\n\
00064     X Y lineto stroke\n\
00065     X Y moveto\n\
00066     x y makearrow\n\
00067 } def\n"
00068 
00069 #define M2 \
00070 "/pathbox {\n\
00071     /X exch neg %.5g sub def\n\
00072     /Y exch %.5g sub def\n\
00073     /x exch neg %.5g sub def\n\
00074     /y exch %.5g sub def\n\
00075     newpath x y moveto\n\
00076     X y lineto\n\
00077     X Y lineto\n\
00078     x Y lineto\n\
00079     closepath stroke\n\
00080 } def\n"
00081 
00082 static pointf map_point(pointf p)
00083 {
00084     p = ccwrotatepf(p, Rankdir * 90);
00085     p.x -= Offset.x;
00086     p.y -= Offset.y;
00087     return p;
00088 }
00089 
00090 static void map_edge(edge_t * e)
00091 {
00092     int j, k;
00093     bezier bz;
00094 
00095     if (ED_spl(e) == NULL) {
00096         if ((Concentrate == FALSE) && (ED_edge_type(e) != IGNORED))
00097             agerr(AGERR, "lost %s %s edge\n", agnameof(agtail(e)),
00098                   agnameof(aghead(e)));
00099         return;
00100     }
00101     for (j = 0; j < ED_spl(e)->size; j++) {
00102         bz = ED_spl(e)->list[j];
00103         for (k = 0; k < bz.size; k++)
00104             bz.list[k] = map_point(bz.list[k]);
00105         if (bz.sflag)
00106             ED_spl(e)->list[j].sp = map_point(ED_spl(e)->list[j].sp);
00107         if (bz.eflag)
00108             ED_spl(e)->list[j].ep = map_point(ED_spl(e)->list[j].ep);
00109     }
00110     if (ED_label(e))
00111         ED_label(e)->pos = map_point(ED_label(e)->pos);
00112     if (ED_xlabel(e))
00113         ED_xlabel(e)->pos = map_point(ED_xlabel(e)->pos);
00114     /* vladimir */
00115     if (ED_head_label(e))
00116         ED_head_label(e)->pos = map_point(ED_head_label(e)->pos);
00117     if (ED_tail_label(e))
00118         ED_tail_label(e)->pos = map_point(ED_tail_label(e)->pos);
00119 }
00120 
00121 void translate_bb(graph_t * g, int rankdir)
00122 {
00123     int c;
00124     boxf bb, new_bb;
00125 
00126     bb = GD_bb(g);
00127     if (rankdir == RANKDIR_LR || rankdir == RANKDIR_BT) {
00128         new_bb.LL = map_point(pointfof(bb.LL.x, bb.UR.y));
00129         new_bb.UR = map_point(pointfof(bb.UR.x, bb.LL.y));
00130     } else {
00131         new_bb.LL = map_point(pointfof(bb.LL.x, bb.LL.y));
00132         new_bb.UR = map_point(pointfof(bb.UR.x, bb.UR.y));
00133     }
00134     GD_bb(g) = new_bb;
00135     if (GD_label(g)) {
00136         GD_label(g)->pos = map_point(GD_label(g)->pos);
00137     }
00138     for (c = 1; c <= GD_n_cluster(g); c++)
00139         translate_bb(GD_clust(g)[c], rankdir);
00140 }
00141 
00142 /* translate_drawing:
00143  * Translate and/or rotate nodes, spline points, and bbox info if
00144  * necessary. Also, if Rankdir (!= RANKDIR_BT), reset ND_lw, ND_rw, 
00145  * and ND_ht to correct value.
00146  */
00147 static void translate_drawing(graph_t * g)
00148 {
00149     node_t *v;
00150     edge_t *e;
00151     int shift = (Offset.x || Offset.y);
00152 
00153     if (!shift && !Rankdir)
00154         return;
00155     for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
00156         if (Rankdir)
00157             gv_nodesize(v, FALSE);
00158         ND_coord(v) = map_point(ND_coord(v));
00159         if (ND_xlabel(v))
00160             ND_xlabel(v)->pos = map_point(ND_xlabel(v)->pos);
00161         if (State == GVSPLINES)
00162             for (e = agfstout(g, v); e; e = agnxtout(g, e))
00163                 map_edge(e);
00164     }
00165     translate_bb(g, GD_rankdir(g));
00166 }
00167 
00168 /* place_root_label:
00169  * Set position of root graph label.
00170  * Note that at this point, after translate_drawing, a
00171  * flipped drawing has been transposed, so we don't have
00172  * to worry about switching x and y.
00173  */
00174 static void place_root_label(graph_t * g, pointf d)
00175 {
00176     pointf p;
00177 
00178     if (GD_label_pos(g) & LABEL_AT_RIGHT) {
00179         p.x = GD_bb(g).UR.x - d.x / 2;
00180     } else if (GD_label_pos(g) & LABEL_AT_LEFT) {
00181         p.x = GD_bb(g).LL.x + d.x / 2;
00182     } else {
00183         p.x = (GD_bb(g).LL.x + GD_bb(g).UR.x) / 2;
00184     }
00185 
00186     if (GD_label_pos(g) & LABEL_AT_TOP) {
00187         p.y = GD_bb(g).UR.y - d.y / 2;
00188     } else {
00189         p.y = GD_bb(g).LL.y + d.y / 2;
00190     }
00191 
00192     GD_label(g)->pos = p;
00193     GD_label(g)->set = TRUE;
00194 }
00195 
00196 /* centerPt:
00197  * Calculate the center point of the xlabel. The returned positions for
00198  * xlabels always correspond to the lower left corner. 
00199  */
00200 static pointf
00201 centerPt (xlabel_t* xlp) {
00202   pointf p;
00203 
00204   p = xlp->pos;
00205   p.x += (xlp->sz.x)/2.0;
00206   p.y += (xlp->sz.y)/2.0;
00207 
00208   return p;
00209 }
00210 
00211 static int
00212 printData (object_t* objs, int n_objs, xlabel_t* lbls, int n_lbls,
00213            label_params_t* params) {
00214   int i;
00215   xlabel_t* xp;
00216   fprintf (stderr, "%d objs %d xlabels force=%d bb=(%.02f,%.02f) (%.02f,%.02f)\n",
00217            n_objs, n_lbls, params->force, params->bb.LL.x, params->bb.LL.y,
00218            params->bb.UR.x, params->bb.UR.y);
00219   if (Verbose < 2) return 0;
00220   fprintf(stderr, "objects\n");
00221   for (i = 0; i < n_objs; i++) {
00222     xp = objs->lbl;
00223     fprintf (stderr, " [%d] (%.02f,%.02f) (%.02f,%.02f) %p \"%s\"\n",
00224             i, objs->pos.x,objs->pos.y,objs->sz.x,objs->sz.y, objs->lbl, 
00225             (xp?((textlabel_t*)(xp->lbl))->text:""));
00226     objs++;
00227   }
00228   fprintf(stderr, "xlabels\n");
00229   for (i = 0; i < n_lbls; i++) {
00230     fprintf (stderr, " [%d] %p set %d (%.02f,%.02f) (%.02f,%.02f) %s\n",
00231              i, lbls,  lbls->set, lbls->pos.x,lbls->pos.y, lbls->sz.x,lbls->sz.y, ((textlabel_t*)lbls->lbl)->text);  
00232     lbls++;
00233   }
00234   return 0;
00235 }
00236 
00237 static pointf
00238 edgeTailpoint (Agedge_t* e)
00239 {
00240     splines *spl;
00241     bezier *bez;
00242 
00243     if ((spl = getsplinepoints(e)) == NULL) {
00244         pointf p;
00245         p.x = p.y = 0;
00246         return p;
00247     }
00248     bez = &spl->list[0];
00249     if (bez->sflag) {
00250         return bez->sp;
00251     } else {
00252         return bez->list[0];
00253     }
00254 }
00255 
00256 static pointf
00257 edgeHeadpoint (Agedge_t* e)
00258 {
00259     splines *spl;
00260     bezier *bez;
00261 
00262     if ((spl = getsplinepoints(e)) == NULL) {
00263         pointf p;
00264         p.x = p.y = 0;
00265         return p;
00266     }
00267     bez = &spl->list[spl->size - 1];
00268     if (bez->eflag) {
00269         return bez->ep;
00270     } else {
00271         return bez->list[bez->size - 1];
00272     }
00273 }
00274 
00275 /* adjustBB:
00276  */
00277 static boxf
00278 adjustBB (object_t* objp, boxf bb)
00279 {
00280     pointf ur;
00281 
00282     /* Adjust bounding box */
00283     bb.LL.x = MIN(bb.LL.x, objp->pos.x);
00284     bb.LL.y = MIN(bb.LL.y, objp->pos.y);
00285     ur.x = objp->pos.x + objp->sz.x;
00286     ur.y = objp->pos.y + objp->sz.y;
00287     bb.UR.x = MAX(bb.UR.x, ur.x);
00288     bb.UR.y = MAX(bb.UR.y, ur.y);
00289 
00290     return bb;
00291 }
00292 
00293 /* addXLabel:
00294  * Set up xlabel_t object and connect with related object.
00295  * If initObj is set, initialize the object.
00296  */
00297 static void
00298 addXLabel (textlabel_t* lp, object_t* objp, xlabel_t* xlp, int initObj, pointf pos)
00299 {
00300     if (initObj) {
00301         objp->sz.x = 0;
00302         objp->sz.y = 0;
00303         objp->pos = pos;
00304     }
00305 
00306     if (Flip) {
00307         xlp->sz.x = lp->dimen.y;
00308         xlp->sz.y = lp->dimen.x;
00309     }
00310     else {
00311         xlp->sz = lp->dimen;
00312     }
00313     xlp->lbl = lp;
00314     xlp->set = 0;
00315     objp->lbl = xlp;
00316 }
00317 
00318 /* addLabelObj:
00319  * Set up obstacle object based on set external label.
00320  * This includes dot edge labels.
00321  * Use label information to determine size and position of object.
00322  * Then adjust given bounding box bb to include label and return new bb.
00323  */
00324 static boxf
00325 addLabelObj (textlabel_t* lp, object_t* objp, boxf bb)
00326 {
00327     if (Flip) {
00328         objp->sz.x = lp->dimen.y; 
00329         objp->sz.y = lp->dimen.x;
00330     }
00331     else {
00332         objp->sz.x = lp->dimen.x; 
00333         objp->sz.y = lp->dimen.y;
00334     }
00335     objp->pos = lp->pos;
00336     objp->pos.x -= (objp->sz.x) / 2.0;
00337     objp->pos.y -= (objp->sz.y) / 2.0;
00338 
00339     return adjustBB(objp, bb);
00340 }
00341 
00342 /* addNodeOjb:
00343  * Set up obstacle object based on a node.
00344  * Use node information to determine size and position of object.
00345  * Then adjust given bounding box bb to include label and return new bb.
00346  */
00347 static boxf
00348 addNodeObj (node_t* np, object_t* objp, boxf bb)
00349 {
00350     if (Flip) {
00351         objp->sz.x = INCH2PS(ND_height(np));
00352         objp->sz.y = INCH2PS(ND_width(np));
00353     }
00354     else {
00355         objp->sz.x = INCH2PS(ND_width(np));
00356         objp->sz.y = INCH2PS(ND_height(np));
00357     }
00358     objp->pos = ND_coord(np);
00359     objp->pos.x -= (objp->sz.x) / 2.0;
00360     objp->pos.y -= (objp->sz.y) / 2.0;
00361 
00362     return adjustBB(objp, bb);
00363 }
00364 
00365 typedef struct {
00366     boxf bb;
00367     object_t* objp;
00368 } cinfo_t;
00369 
00370 static cinfo_t
00371 addClusterObj (Agraph_t* g, cinfo_t info)
00372 {
00373     int c;
00374 
00375     for (c = 1; c <= GD_n_cluster(g); c++)
00376         info = addClusterObj (GD_clust(g)[c], info);
00377     if ((g != agroot(g)) && (GD_label(g)) && GD_label(g)->set) {
00378         object_t* objp = info.objp;
00379         info.bb = addLabelObj (GD_label(g), objp, info.bb);
00380         info.objp++;
00381     }
00382 
00383     return info;
00384 }
00385 
00386 static int
00387 countClusterLabels (Agraph_t* g)
00388 {
00389     int c, i = 0;
00390     if ((g != agroot(g)) && (GD_label(g)) && GD_label(g)->set)
00391         i++;
00392     for (c = 1; c <= GD_n_cluster(g); c++)
00393         i += countClusterLabels (GD_clust(g)[c]);
00394     return i;
00395 }
00396 
00397 /* addXLabels:
00398  * Position xlabels and any unpositioned edge labels using
00399  * a map placement algorithm to avoid overlap.
00400  *
00401  * TODO: interaction with spline=ortho
00402  */
00403 static void addXLabels(Agraph_t * gp)
00404 {
00405     Agnode_t *np;
00406     Agedge_t *ep;
00407     int cnt, i, n_objs, n_lbls;
00408     int n_nlbls = 0;            /* # of unset node xlabels */
00409     int n_elbls = 0;            /* # of unset edge labels or xlabels */
00410     int n_set_lbls = 0;         /* # of set xlabels and edge labels */
00411     int n_clbls = 0;            /* # of set cluster labels */
00412     boxf bb;
00413     pointf ur;
00414     textlabel_t* lp;
00415     label_params_t params;
00416     object_t* objs;
00417     xlabel_t* lbls;
00418     object_t* objp;
00419     xlabel_t* xlp;
00420     Agsym_t* force;
00421     int et = EDGE_TYPE(gp);
00422 
00423     if (!(GD_has_labels(gp) & NODE_XLABEL) &&
00424         !(GD_has_labels(gp) & EDGE_XLABEL) &&
00425         !(GD_has_labels(gp) & TAIL_LABEL) &&
00426         !(GD_has_labels(gp) & HEAD_LABEL) &&
00427         (!(GD_has_labels(gp) & EDGE_LABEL) || EdgeLabelsDone))
00428         return;
00429 
00430     for (np = agfstnode(gp); np; np = agnxtnode(gp, np)) {
00431         if (ND_xlabel(np)) {
00432             if (ND_xlabel(np)->set)
00433                 n_set_lbls++;
00434             else
00435                 n_nlbls++;
00436         }
00437         for (ep = agfstout(gp, np); ep; ep = agnxtout(gp, ep)) {
00438             if (ED_xlabel(ep)) {
00439                 if (ED_xlabel(ep)->set)
00440                     n_set_lbls++;
00441                 else if (et != ET_NONE)
00442                     n_elbls++;
00443             }
00444             if (ED_head_label(ep)) {
00445                 if (ED_head_label(ep)->set)
00446                     n_set_lbls++;
00447                 else if (et != ET_NONE)
00448                     n_elbls++;
00449             }
00450             if (ED_tail_label(ep)) {
00451                 if (ED_tail_label(ep)->set)
00452                     n_set_lbls++;
00453                 else if (et != ET_NONE)
00454                     n_elbls++;
00455             }
00456             if (ED_label(ep)) {
00457                 if (ED_label(ep)->set)
00458                     n_set_lbls++;
00459                 else if (et != ET_NONE)
00460                     n_elbls++;
00461             }
00462         }
00463     }
00464     if (GD_has_labels(gp) & GRAPH_LABEL)
00465         n_clbls = countClusterLabels (gp);
00466 
00467     /* A label for each unpositioned external label */
00468     n_lbls = n_nlbls + n_elbls;
00469     if (n_lbls == 0) return;
00470 
00471     /* An object for each node, each positioned external label, any cluster label, 
00472      * and all unset edge labels and xlabels.
00473      */
00474     n_objs = agnnodes(gp) + n_set_lbls + n_clbls + n_elbls;
00475     objp = objs = N_NEW(n_objs, object_t);
00476     xlp = lbls = N_NEW(n_lbls, xlabel_t);
00477     bb.LL = pointfof(INT_MAX, INT_MAX);
00478     bb.UR = pointfof(-INT_MAX, -INT_MAX);
00479 
00480     for (np = agfstnode(gp); np; np = agnxtnode(gp, np)) {
00481 
00482         bb = addNodeObj (np, objp, bb);
00483         if ((lp = ND_xlabel(np))) {
00484             if (lp->set) {
00485                 objp++;
00486                 bb = addLabelObj (lp, objp, bb);
00487             }
00488             else {
00489                 addXLabel (lp, objp, xlp, 0, ur); 
00490                 xlp++;
00491             }
00492         }
00493         objp++;
00494         for (ep = agfstout(gp, np); ep; ep = agnxtout(gp, ep)) {
00495             if ((lp = ED_label(ep))) {
00496                 if (lp->set) {
00497                     bb = addLabelObj (lp, objp, bb);
00498                 }
00499                 else if (et != ET_NONE) {
00500                     addXLabel (lp, objp, xlp, 1, edgeMidpoint(gp, ep)); 
00501                     xlp++;
00502                 }
00503                 objp++;
00504             }
00505             if ((lp = ED_tail_label(ep))) {
00506                 if (lp->set) {
00507                     bb = addLabelObj (lp, objp, bb);
00508                 }
00509                 else if (et != ET_NONE) {
00510                     addXLabel (lp, objp, xlp, 1, edgeTailpoint(ep)); 
00511                     xlp++;
00512                 }
00513                 objp++;
00514             }
00515             if ((lp = ED_head_label(ep))) {
00516                 if (lp->set) {
00517                     bb = addLabelObj (lp, objp, bb);
00518                 }
00519                 else if (et != ET_NONE) {
00520                     addXLabel (lp, objp, xlp, 1, edgeHeadpoint(ep)); 
00521                     xlp++;
00522                 }
00523                 objp++;
00524             }
00525             if ((lp = ED_xlabel(ep))) {
00526                 if (lp->set) {
00527                     bb = addLabelObj (lp, objp, bb);
00528                 }
00529                 else if (et != ET_NONE) {
00530                     addXLabel (lp, objp, xlp, 1, edgeMidpoint(gp, ep)); 
00531                     xlp++;
00532                 }
00533                 objp++;
00534             }
00535         }
00536     }
00537     if (n_clbls) {
00538         cinfo_t info;
00539         info.bb = bb;
00540         info.objp = objp;
00541         info = addClusterObj (gp, info);
00542         bb = info.bb;
00543     }
00544 
00545     force = agfindgraphattr(gp, "forcelabels");
00546 
00547     params.force = late_bool(gp, force, TRUE);
00548     params.bb = bb;
00549     placeLabels(objs, n_objs, lbls, n_lbls, &params);
00550     if (Verbose)
00551         printData(objs, n_objs, lbls, n_lbls, &params);
00552 
00553     xlp = lbls;
00554     cnt = 0;
00555     for (i = 0; i < n_lbls; i++) {
00556         if (xlp->set) {
00557             cnt++;
00558             lp = (textlabel_t *) (xlp->lbl);
00559             lp->set = 1;
00560             lp->pos = centerPt(xlp);
00561             updateBB (gp, lp);
00562         }
00563         xlp++;
00564     }
00565     if (Verbose)
00566         fprintf (stderr, "%d out of %d labels positioned.\n", cnt, n_lbls);
00567     else if (cnt != n_lbls)
00568         agerr(AGWARN, "%d out of %d exterior labels positioned.\n", cnt, n_lbls);
00569     free(objs);
00570     free(lbls);
00571 }
00572 
00573 /* dotneato_postprocess:
00574  * Set graph and cluster label positions.
00575  * Add space for root graph label and translate graph accordingly.
00576  * Set final nodesize using ns.
00577  * Assumes the boxes of all clusters have been computed.
00578  * When done, the bounding box of g has LL at origin.
00579  */
00580 void gv_postprocess(Agraph_t * g, int allowTranslation)
00581 {
00582     double diff;
00583     pointf dimen = { 0., 0. };
00584 
00585 
00586     Rankdir = GD_rankdir(g);
00587     Flip = GD_flip(g);
00588     /* Handle cluster labels */
00589     if (Flip)
00590         place_flip_graph_label(g);
00591     else
00592         place_graph_label(g);
00593 
00594     /* Everything has been placed except the root graph label, if any.
00595      * The graph positions have not yet been rotated back if necessary.
00596      */
00597     addXLabels(g);
00598 
00599     /* Add space for graph label if necessary */
00600     if (GD_label(g) && !GD_label(g)->set) {
00601         dimen = GD_label(g)->dimen;
00602         PAD(dimen);
00603         if (Flip) {
00604             if (GD_label_pos(g) & LABEL_AT_TOP) {
00605                 GD_bb(g).UR.x += dimen.y;
00606             } else {
00607                 GD_bb(g).LL.x -= dimen.y;
00608             }
00609 
00610             if (dimen.x > (GD_bb(g).UR.y - GD_bb(g).LL.y)) {
00611                 diff = dimen.x - (GD_bb(g).UR.y - GD_bb(g).LL.y);
00612                 diff = diff / 2.;
00613                 GD_bb(g).LL.y -= diff;
00614                 GD_bb(g).UR.y += diff;
00615             }
00616         } else {
00617             if (GD_label_pos(g) & LABEL_AT_TOP) {
00618                 if (Rankdir == RANKDIR_TB)
00619                     GD_bb(g).UR.y += dimen.y;
00620                 else
00621                     GD_bb(g).LL.y -= dimen.y;
00622             } else {
00623                 if (Rankdir == RANKDIR_TB)
00624                     GD_bb(g).LL.y -= dimen.y;
00625                 else
00626                     GD_bb(g).UR.y += dimen.y;
00627             }
00628 
00629             if (dimen.x > (GD_bb(g).UR.x - GD_bb(g).LL.x)) {
00630                 diff = dimen.x - (GD_bb(g).UR.x - GD_bb(g).LL.x);
00631                 diff = diff / 2.;
00632                 GD_bb(g).LL.x -= diff;
00633                 GD_bb(g).UR.x += diff;
00634             }
00635         }
00636     }
00637     if (allowTranslation) {
00638         switch (Rankdir) {
00639         case RANKDIR_TB:
00640             Offset = GD_bb(g).LL;
00641             break;
00642         case RANKDIR_LR:
00643             Offset = pointfof(-GD_bb(g).UR.y, GD_bb(g).LL.x);
00644             break;
00645         case RANKDIR_BT:
00646             Offset = pointfof(GD_bb(g).LL.x, -GD_bb(g).UR.y);
00647             break;
00648         case RANKDIR_RL:
00649             Offset = pointfof(GD_bb(g).LL.y, GD_bb(g).LL.x);
00650             break;
00651         }
00652         translate_drawing(g);
00653     }
00654     if (GD_label(g) && !GD_label(g)->set)
00655         place_root_label(g, dimen);
00656 
00657     if (Show_boxes) {
00658         char buf[BUFSIZ];
00659         if (Flip)
00660             sprintf(buf, M2, Offset.x, Offset.y, Offset.x, Offset.y);
00661         else
00662             sprintf(buf, M1, Offset.y, Offset.x, Offset.y, Offset.x,
00663                     -Offset.x, -Offset.y);
00664         Show_boxes[0] = strdup(buf);
00665     }
00666 }
00667 
00668 void dotneato_postprocess(Agraph_t * g)
00669 {
00670     gv_postprocess(g, 1);
00671 }
00672 
00673 /* place_flip_graph_label:
00674  * Put cluster labels recursively in the flip case.
00675  */
00676 static void place_flip_graph_label(graph_t * g)
00677 {
00678     int c;
00679     pointf p, d;
00680 
00681     if ((g != agroot(g)) && (GD_label(g)) && !GD_label(g)->set) {
00682         if (GD_label_pos(g) & LABEL_AT_TOP) {
00683             d = GD_border(g)[RIGHT_IX];
00684             p.x = GD_bb(g).UR.x - d.x / 2;
00685         } else {
00686             d = GD_border(g)[LEFT_IX];
00687             p.x = GD_bb(g).LL.x + d.x / 2;
00688         }
00689 
00690         if (GD_label_pos(g) & LABEL_AT_RIGHT) {
00691             p.y = GD_bb(g).LL.y + d.y / 2;
00692         } else if (GD_label_pos(g) & LABEL_AT_LEFT) {
00693             p.y = GD_bb(g).UR.y - d.y / 2;
00694         } else {
00695             p.y = (GD_bb(g).LL.y + GD_bb(g).UR.y) / 2;
00696         }
00697         GD_label(g)->pos = p;
00698         GD_label(g)->set = TRUE;
00699     }
00700 
00701     for (c = 1; c <= GD_n_cluster(g); c++)
00702         place_flip_graph_label(GD_clust(g)[c]);
00703 }
00704 
00705 /* place_graph_label:
00706  * Put cluster labels recursively in the non-flip case.
00707  * The adjustments to the bounding boxes should no longer
00708  * be necessary, since we now guarantee the label fits in
00709  * the cluster.
00710  */
00711 void place_graph_label(graph_t * g)
00712 {
00713     int c;
00714     pointf p, d;
00715 
00716     if ((g != agroot(g)) && (GD_label(g)) && !GD_label(g)->set) {
00717         if (GD_label_pos(g) & LABEL_AT_TOP) {
00718             d = GD_border(g)[TOP_IX];
00719             p.y = GD_bb(g).UR.y - d.y / 2;
00720         } else {
00721             d = GD_border(g)[BOTTOM_IX];
00722             p.y = GD_bb(g).LL.y + d.y / 2;
00723         }
00724 
00725         if (GD_label_pos(g) & LABEL_AT_RIGHT) {
00726             p.x = GD_bb(g).UR.x - d.x / 2;
00727         } else if (GD_label_pos(g) & LABEL_AT_LEFT) {
00728             p.x = GD_bb(g).LL.x + d.x / 2;
00729         } else {
00730             p.x = (GD_bb(g).LL.x + GD_bb(g).UR.x) / 2;
00731         }
00732         GD_label(g)->pos = p;
00733         GD_label(g)->set = TRUE;
00734     }
00735 
00736     for (c = 1; c <= GD_n_cluster(g); c++)
00737         place_graph_label(GD_clust(g)[c]);
00738 }