From: Emden R. Gansner Date: Wed, 26 Feb 2014 21:03:34 +0000 (-0500) Subject: Fix neato to avoid unnecessary translations. This will allow nodes that have a fixed... X-Git-Tag: 2.38.0~56 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=53dfdd612a00ce4f4e8f638ffe9563c64c0e53a6;p=graphviz Fix neato to avoid unnecessary translations. This will allow nodes that have a fixed position to have the same position on output. --- diff --git a/lib/common/postproc.c b/lib/common/postproc.c index ffc911c33..163bf30e4 100644 --- a/lib/common/postproc.c +++ b/lib/common/postproc.c @@ -573,7 +573,7 @@ static void addXLabels(Agraph_t * gp) free(lbls); } -/* dotneato_postprocess: +/* gv_postprocess: * Set graph and cluster label positions. * Add space for root graph label and translate graph accordingly. * Set final nodesize using ns. @@ -668,6 +668,8 @@ void gv_postprocess(Agraph_t * g, int allowTranslation) } } +/* dotneato_postprocess: + */ void dotneato_postprocess(Agraph_t * g) { gv_postprocess(g, 1); diff --git a/lib/fdpgen/layout.c b/lib/fdpgen/layout.c index 0409f27ff..bba7a0064 100644 --- a/lib/fdpgen/layout.c +++ b/lib/fdpgen/layout.c @@ -1083,8 +1083,6 @@ void fdpLayout(graph_t * g) /* Set bbox info for g and all clusters. This is needed for * spline drawing. We already know the graph bbox is at the origin. - * (We pass the origin to spline_edges0. This really isn't true, - * as the algorithm has done many translations.) * On return from spline drawing, all bounding boxes should be correct. */ setBB(g); @@ -1138,6 +1136,6 @@ void fdp_layout(graph_t * g) if (EDGE_TYPE(g) != ET_NONE) fdpSplines (g); - dotneato_postprocess(g); + gv_postprocess(g, 0); PSinputscale = save_scale; } diff --git a/lib/neatogen/neatoinit.c b/lib/neatogen/neatoinit.c index 915855194..f2bbb29ec 100644 --- a/lib/neatogen/neatoinit.c +++ b/lib/neatogen/neatoinit.c @@ -400,6 +400,24 @@ static pos_edge nop_init_edges(Agraph_t * g) return NoEdges; } +/* freeEdgeInfo: + */ +static void freeEdgeInfo (Agraph_t * g) +{ + node_t *n; + edge_t *e; + + for (n = agfstnode(g); n; n = agnxtnode(g, n)) { + for (e = agfstout(g, n); e; e = agnxtout(g, e)) { + gv_free_splines(e); + free_label(ED_label(e)); + free_label(ED_xlabel(e)); + free_label(ED_head_label(e)); + free_label(ED_tail_label(e)); + } + } +} + /* chkBB: * Scans for a correct bb attribute. If available, sets it * in the graph and returns 1. @@ -492,107 +510,9 @@ nop_init_graphs(Agraph_t * g, attrsym_t * G_lp, attrsym_t * G_bb) } } -/* translateE: - * Translate edge by offset. - * Assume ED_spl(e) != NULL - */ -static void translateE(edge_t * e, pointf offset) -{ - int i, j; - pointf *pt; - bezier *bez; - - bez = ED_spl(e)->list; - for (i = 0; i < ED_spl(e)->size; i++) { - pt = bez->list; - for (j = 0; j < bez->size; j++) { - pt->x -= offset.x; - pt->y -= offset.y; - pt++; - } - if (bez->sflag) { - bez->sp.x -= offset.x; - bez->sp.y -= offset.y; - } - if (bez->eflag) { - bez->ep.x -= offset.x; - bez->ep.y -= offset.y; - } - bez++; - } - - if (ED_label(e) && ED_label(e)->set) { - ED_label(e)->pos.x -= offset.x; - ED_label(e)->pos.y -= offset.y; - } - if (ED_xlabel(e) && ED_xlabel(e)->set) { - ED_xlabel(e)->pos.x -= offset.x; - ED_xlabel(e)->pos.y -= offset.y; - } - if (ED_head_label(e) && ED_head_label(e)->set) { - ED_head_label(e)->pos.x -= offset.x; - ED_head_label(e)->pos.y -= offset.y; - } - if (ED_tail_label(e) && ED_tail_label(e)->set) { - ED_tail_label(e)->pos.x -= offset.x; - ED_tail_label(e)->pos.y -= offset.y; - } -} - -/* translateG: - */ -static void translateG(Agraph_t * g, pointf offset) -{ - int i; - - GD_bb(g).UR.x -= offset.x; - GD_bb(g).UR.y -= offset.y; - GD_bb(g).LL.x -= offset.x; - GD_bb(g).LL.y -= offset.y; - - if (GD_label(g) && GD_label(g)->set) { - GD_label(g)->pos.x -= offset.x; - GD_label(g)->pos.y -= offset.y; - } - - for (i = 1; i <= GD_n_cluster(g); i++) - translateG(GD_clust(g)[i], offset); -} - -/* translate: - */ -static void translate(Agraph_t * g, pos_edge posEdges) -{ - node_t *n; - edge_t *e; - pointf offset; - pointf ll; - - ll = GD_bb(g).LL; - - offset.x = PS2INCH(ll.x); - offset.y = PS2INCH(ll.y); - for (n = agfstnode(g); n; n = agnxtnode(g, n)) { - ND_pos(n)[0] -= offset.x; - ND_pos(n)[1] -= offset.y; - if (ND_xlabel(n) && ND_xlabel(n)->set) { - ND_xlabel(n)->pos.x -= ll.x; - ND_xlabel(n)->pos.y -= ll.y; - } - } - if (posEdges != NoEdges) { - for (n = agfstnode(g); n; n = agnxtnode(g, n)) { - for (e = agfstout(g, n); e; e = agnxtout(g, e)) - if (ED_spl(e)) - translateE(e, ll); - } - } - translateG(g, ll); -} - /* init_nop: * This assumes all nodes have been positioned. - * It also assumes none of the * relevant fields in A*info_t have been set. + * It also assumes none of the relevant fields in A*info_t have been set. * The input may provide additional position information for * clusters, edges and labels. If certain position information * is missing, init_nop will use a standard neato technique to @@ -602,7 +522,7 @@ static void translate(Agraph_t * g, pos_edge posEdges) * of the basic graph information. No tweaking of positions or * filling in edge splines is done. * - * Returns 0 on normal success, 1 if postprocess should be avoided, and -1 + * Returns 0 on normal success, 1 if layout has a background, and -1 * on failure. */ int init_nop(Agraph_t * g, int adjust) @@ -649,23 +569,14 @@ int init_nop(Agraph_t * g, int adjust) */ } -#if 0 - /* If g does not have a good "bb" attribute or we adjusted the nodes, - * compute it. - */ - if (!chkBB(g, G_bb) || didAdjust) -#endif - compute_bb(g); + compute_bb(g); /* Adjust bounding box for any background */ if (haveBackground) GD_bb(g) = xdotBB (g); /* At this point, all bounding boxes should be correctly defined. - * If necessary, we translate the graph to the origin. */ - if (adjust && !haveBackground && (ROUND(abs(GD_bb(g).LL.x)) || ROUND(abs(GD_bb(g).LL.y)))) - translate(g, posEdges); if (!adjust) { node_t *n; @@ -675,12 +586,19 @@ int init_nop(Agraph_t * g, int adjust) ND_coord(n).y = POINTS_PER_INCH * (ND_pos(n)[1]); } } - else if (posEdges != AllEdges) - spline_edges0(g); else { - State = GVSPLINES; - neato_set_aspect(g); + boolean didShift = neato_set_aspect(g); + /* if we have some edge positions and we either shifted or adjusted, free edge positions */ + if ((posEdges != NoEdges) && (didShift || didAdjust)) { + freeEdgeInfo (g); + posEdges = NoEdges; + } + if (posEdges != AllEdges) + spline_edges0(g, FALSE); /* add edges */ + else + State = GVSPLINES; } + return haveBackground; } @@ -1470,6 +1388,16 @@ addCluster (graph_t* g) } #endif +/* doEdges: + * Simple wrapper to compute graph's bb, then route edges after + * a possible aspect ratio adjustment. + */ +static void doEdges(Agraph_t* g) +{ + compute_bb(g); + spline_edges0(g, TRUE); +} + /* neato_layout: */ void neato_layout(Agraph_t * g) @@ -1491,7 +1419,7 @@ void neato_layout(Agraph_t * g) agerr(AGPREV, "as required by the -n flag\n"); return; } - else gv_postprocess(g, !ret); + else gv_postprocess(g, 0); } else { PSinputscale = get_inputscale (g); neato_init_graph(g); @@ -1527,7 +1455,7 @@ void neato_layout(Agraph_t * g) neatoLayout(g, gc, layoutMode, model, &am); removeOverlapWith(gc, &am); setEdgeType (gc, ET_LINE); - spline_edges(gc); + doEdges(gc); } if (pin) { bp = N_NEW(n_cc, boolean); @@ -1544,7 +1472,7 @@ void neato_layout(Agraph_t * g) else { neatoLayout(g, g, layoutMode, model, &am); removeOverlapWith(g, &am); - spline_edges(g); + doEdges(g); } compute_bb(g); addZ (g); @@ -1564,9 +1492,9 @@ void neato_layout(Agraph_t * g) neatoLayout(g, g, layoutMode, model, &am); removeOverlapWith(g, &am); addZ (g); - spline_edges(g); + doEdges(g); } - dotneato_postprocess(g); + gv_postprocess(g, 0); } PSinputscale = save_scale; } diff --git a/lib/neatogen/neatoprocs.h b/lib/neatogen/neatoprocs.h index 48a3c6c4d..0a8dce67b 100644 --- a/lib/neatogen/neatoprocs.h +++ b/lib/neatogen/neatoprocs.h @@ -66,11 +66,11 @@ extern "C" { extern void solve_model(graph_t *, int); extern int solveCircuit(int nG, double **Gm, double **Gm_inv); extern void spline_edges(Agraph_t *); - extern void spline_edges0(Agraph_t *); + extern void spline_edges0(Agraph_t *, boolean); extern int spline_edges1(graph_t * g, int); extern int splineEdges(graph_t *, int (*edgefn) (graph_t *, expand_t*, int), int); - extern void neato_set_aspect(graph_t * g); + extern boolean neato_set_aspect(graph_t * g); extern void toggle(int); extern int user_pos(Agsym_t *, Agsym_t *, Agnode_t *, int); extern double **new_array(int i, int j, double val); diff --git a/lib/neatogen/neatosplines.c b/lib/neatogen/neatosplines.c index ba6c376bf..80d4198e7 100644 --- a/lib/neatogen/neatosplines.c +++ b/lib/neatogen/neatosplines.c @@ -754,10 +754,10 @@ int spline_edges1(graph_t * g, int edgetype) * when output in dot or plain format. * */ -void spline_edges0(graph_t * g) +void spline_edges0(graph_t * g, boolean set_aspect) { int et = EDGE_TYPE (g); - neato_set_aspect(g); + if (set_aspect) neato_set_aspect(g); if (et == ET_NONE) return; #ifndef ORTHO if (et == ET_ORTHO) { @@ -805,7 +805,7 @@ void spline_edges(graph_t * g) } shiftClusters (g, GD_bb(g).LL); - spline_edges0(g); + spline_edges0(g, TRUE); } /* scaleEdge: @@ -888,22 +888,120 @@ static void scaleBB(graph_t * g, double xf, double yf) scaleBB(GD_clust(g)[i], xf, yf); } +/* translateE: + * Translate edge by offset. + * Assume ED_spl(e) != NULL + */ +static void translateE(edge_t * e, pointf offset) +{ + int i, j; + pointf *pt; + bezier *bez; + + bez = ED_spl(e)->list; + for (i = 0; i < ED_spl(e)->size; i++) { + pt = bez->list; + for (j = 0; j < bez->size; j++) { + pt->x -= offset.x; + pt->y -= offset.y; + pt++; + } + if (bez->sflag) { + bez->sp.x -= offset.x; + bez->sp.y -= offset.y; + } + if (bez->eflag) { + bez->ep.x -= offset.x; + bez->ep.y -= offset.y; + } + bez++; + } + + if (ED_label(e) && ED_label(e)->set) { + ED_label(e)->pos.x -= offset.x; + ED_label(e)->pos.y -= offset.y; + } + if (ED_xlabel(e) && ED_xlabel(e)->set) { + ED_xlabel(e)->pos.x -= offset.x; + ED_xlabel(e)->pos.y -= offset.y; + } + if (ED_head_label(e) && ED_head_label(e)->set) { + ED_head_label(e)->pos.x -= offset.x; + ED_head_label(e)->pos.y -= offset.y; + } + if (ED_tail_label(e) && ED_tail_label(e)->set) { + ED_tail_label(e)->pos.x -= offset.x; + ED_tail_label(e)->pos.y -= offset.y; + } +} + +/* translateG: + */ +static void translateG(Agraph_t * g, pointf offset) +{ + int i; + + GD_bb(g).UR.x -= offset.x; + GD_bb(g).UR.y -= offset.y; + GD_bb(g).LL.x -= offset.x; + GD_bb(g).LL.y -= offset.y; + + if (GD_label(g) && GD_label(g)->set) { + GD_label(g)->pos.x -= offset.x; + GD_label(g)->pos.y -= offset.y; + } + + for (i = 1; i <= GD_n_cluster(g); i++) + translateG(GD_clust(g)[i], offset); +} + +/* translate: + */ +static void translate(Agraph_t * g) +{ + node_t *n; + edge_t *e; + pointf offset; + pointf ll; + + ll = GD_bb(g).LL; + + offset.x = PS2INCH(ll.x); + offset.y = PS2INCH(ll.y); + for (n = agfstnode(g); n; n = agnxtnode(g, n)) { + ND_pos(n)[0] -= offset.x; + ND_pos(n)[1] -= offset.y; + if (ND_xlabel(n) && ND_xlabel(n)->set) { + ND_xlabel(n)->pos.x -= ll.x; + ND_xlabel(n)->pos.y -= ll.y; + } + } + for (n = agfstnode(g); n; n = agnxtnode(g, n)) { + for (e = agfstout(g, n); e; e = agnxtout(g, e)) + if (ED_spl(e)) + translateE(e, ll); + } + translateG(g, ll); +} + /* _neato_set_aspect; - * Assume all bounding boxes are correct and - * that GD_bb(g).LL is at origin. - * Also assume g is the root graph + * Assume all bounding boxes are correct. + * Return false if no transform is performed. This includes + * the possiblity that a translation was done. */ -static void _neato_set_aspect(graph_t * g) +static boolean _neato_set_aspect(graph_t * g) { - /* int i; */ double xf, yf, actual, desired; node_t *n; + if (g->root != g) + return FALSE; + /* compute_bb(g); */ if (GD_drawing(g)->ratio_kind) { + if (ROUND(abs(GD_bb(g).LL.x)) || ROUND(abs(GD_bb(g).LL.y))) + translate (g); /* normalize */ - assert(ROUND(GD_bb(g).LL.x) == 0); - assert(ROUND(GD_bb(g).LL.y) == 0); if (GD_flip(g)) { double t = GD_bb(g).UR.x; GD_bb(g).UR.x = GD_bb(g).UR.y; @@ -912,7 +1010,7 @@ static void _neato_set_aspect(graph_t * g) if (GD_drawing(g)->ratio_kind == R_FILL) { /* fill is weird because both X and Y can stretch */ if (GD_drawing(g)->size.x <= 0) - return; + return FALSE; xf = (double) GD_drawing(g)->size.x / GD_bb(g).UR.x; yf = (double) GD_drawing(g)->size.y / GD_bb(g).UR.y; /* handle case where one or more dimensions is too big */ @@ -927,14 +1025,14 @@ static void _neato_set_aspect(graph_t * g) } } else if (GD_drawing(g)->ratio_kind == R_EXPAND) { if (GD_drawing(g)->size.x <= 0) - return; + return FALSE; xf = (double) GD_drawing(g)->size.x / GD_bb(g).UR.x; yf = (double) GD_drawing(g)->size.y / GD_bb(g).UR.y; if ((xf > 1.0) && (yf > 1.0)) { double scale = MIN(xf, yf); xf = yf = scale; } else - return; + return FALSE; } else if (GD_drawing(g)->ratio_kind == R_VALUE) { desired = GD_drawing(g)->ratio; actual = (GD_bb(g).UR.y) / (GD_bb(g).UR.x); @@ -946,7 +1044,7 @@ static void _neato_set_aspect(graph_t * g) yf = 1.0; } } else - return; + return FALSE; if (GD_flip(g)) { double t = xf; xf = yf; @@ -969,24 +1067,30 @@ static void _neato_set_aspect(graph_t * g) ND_pos(n)[1] = ND_pos(n)[1] * yf; } scaleBB(g, xf, yf); + return TRUE; } + else + return FALSE; } /* neato_set_aspect: * Sets aspect ratio if necessary; real work done in _neato_set_aspect; * This also copies the internal layout coordinates (ND_pos) to the * external ones (ND_coord). + * + * Return true if some node moved. */ -void neato_set_aspect(graph_t * g) +boolean neato_set_aspect(graph_t * g) { node_t *n; + boolean moved = FALSE; /* setting aspect ratio only makes sense on root graph */ - if (g->root == g) - _neato_set_aspect(g); + moved = _neato_set_aspect(g); for (n = agfstnode(g); n; n = agnxtnode(g, n)) { ND_coord(n).x = POINTS_PER_INCH * (ND_pos(n)[0]); ND_coord(n).y = POINTS_PER_INCH * (ND_pos(n)[1]); } + return moved; } diff --git a/lib/osage/osageinit.c b/lib/osage/osageinit.c index 9b770a9be..ab03a248c 100644 --- a/lib/osage/osageinit.c +++ b/lib/osage/osageinit.c @@ -364,7 +364,7 @@ void osage_layout(Agraph_t *g) ND_pos(n)[0] = PS2INCH(ND_coord(n).x); ND_pos(n)[1] = PS2INCH(ND_coord(n).y); } - spline_edges0(g); + spline_edges0(g, TRUE); } else { int et = EDGE_TYPE (g);