From: erg Date: Tue, 8 Mar 2005 23:34:10 +0000 (+0000) Subject: Make many functions in dotprocs.h static to help me keep my sanity X-Git-Tag: LAST_LIBGRAPH~32^2~7838 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6b15da14069781cb34428e4a489ea184a9aa5840;p=graphviz Make many functions in dotprocs.h static to help me keep my sanity trying to track down who can be called. --- diff --git a/lib/dotgen/dotinit.c b/lib/dotgen/dotinit.c index 9467d728b..96128bd0e 100644 --- a/lib/dotgen/dotinit.c +++ b/lib/dotgen/dotinit.c @@ -37,7 +37,8 @@ void dot_nodesize(node_t * n, boolean flip) ND_ht_i(n) = POINTS(y); } -void dot_init_node(node_t * n) +static void +dot_init_node(node_t * n) { common_init_node(n); dot_nodesize(n, GD_flip(n->graph)); @@ -49,7 +50,8 @@ void dot_init_node(node_t * n) ND_UF_size(n) = 1; } -void dot_init_edge(edge_t * e) +static void +dot_init_edge(edge_t * e) { char *tailgroup, *headgroup; @@ -72,7 +74,8 @@ void dot_init_edge(edge_t * e) ED_minlen(e) = late_int(e, E_minlen, 1, 0); } -void dot_init_node_edge(graph_t * g) +static void +dot_init_node_edge(graph_t * g) { node_t *n; edge_t *e; @@ -98,7 +101,8 @@ static void free_edge_list(elist L) } #endif -void dot_cleanup_node(node_t * n) +static void +dot_cleanup_node(node_t * n) { free_list(ND_in(n)); free_list(ND_out(n)); @@ -111,7 +115,8 @@ void dot_cleanup_node(node_t * n) memset(&(n->u), 0, sizeof(Agnodeinfo_t)); } -void dot_free_splines(edge_t * e) +static void +dot_free_splines(edge_t * e) { int i; if (ED_spl(e)) { @@ -123,7 +128,8 @@ void dot_free_splines(edge_t * e) ED_spl(e) = NULL; } -void dot_cleanup_edge(edge_t * e) +static void +dot_cleanup_edge(edge_t * e) { dot_free_splines(e); free_label(ED_label(e)); @@ -163,7 +169,8 @@ static void free_virtual_node_list(node_t * vn) } } -void dot_cleanup_graph(graph_t * g) +static void +dot_cleanup_graph(graph_t * g) { int i, c; graph_t *clust; @@ -200,7 +207,8 @@ void dot_cleanup(graph_t * g) dot_cleanup_graph(g); } -void dot_init_graph(Agraph_t * g) +static void +dot_init_graph(Agraph_t * g) { UseRankdir = TRUE; graph_init(g); diff --git a/lib/dotgen/dotprocs.h b/lib/dotgen/dotprocs.h index f26f7b53f..7ff0c48a0 100644 --- a/lib/dotgen/dotprocs.h +++ b/lib/dotgen/dotprocs.h @@ -26,127 +26,48 @@ extern "C" { #endif extern void acyclic(Agraph_t *); - extern void add_to_component(Agnode_t *); extern void allocate_ranks(Agraph_t *); - extern void basic_merge(Agedge_t *, Agedge_t *); - extern void begin_component(void); extern void build_ranks(Agraph_t *, int); extern void build_skeleton(Agraph_t *, Agraph_t *); extern void class1(Agraph_t *); extern void class2(Agraph_t *); - extern void cleanup1(Agraph_t *); - extern Agnode_t *clone_vn(Agraph_t *, Agnode_t *); - extern void cluster_leader(Agraph_t *); - extern void collapse_cluster(Agraph_t *, Agraph_t *); - extern void collapse_leaves(Agraph_t *); - extern void collapse_rankset(Agraph_t *, Agraph_t *, int); - extern void collapse_sets(Agraph_t *); extern void decompose(Agraph_t *, int); extern void delete_fast_edge(Agedge_t *); extern void delete_fast_node(Agraph_t *, Agnode_t *); extern void delete_flat_edge(Agedge_t *); - extern void delete_other_edge(Agedge_t *); - extern void dfs(Agnode_t *); - extern void do_ordering(Agraph_t *, int); extern void dot_cleanup(graph_t * g); - extern void dot_cleanup_edge(edge_t * e); - extern void dot_cleanup_graph(graph_t * g); - extern void dot_cleanup_node(node_t * n); - extern void dot_free_splines(edge_t * e); - extern void dot_init_node_edge(Agraph_t *); - extern void dot_init_edge(Agedge_t *); - extern void dot_init_node(Agnode_t *); extern void dot_layout(Agraph_t * g); - extern void dot_init_graph(graph_t * g); - extern void edgelabel_ranks(Agraph_t *); - extern void end_component(void); - extern void exchange(Agnode_t *, Agnode_t *); extern void expand_cluster(Agraph_t *); - extern void expand_ranksets(Agraph_t *); extern Agedge_t *fast_edge(Agedge_t *); extern void fast_node(Agraph_t *, Agnode_t *); extern void fast_nodeapp(Agnode_t *, Agnode_t *); - extern void find_clusters(Agraph_t *); extern Agedge_t *find_fast_edge(Agnode_t *, Agnode_t *); - extern Agnode_t *find_fast_node(Agraph_t *, Agnode_t *); extern Agedge_t *find_flat_edge(Agnode_t *, Agnode_t *); extern void flat_edge(Agraph_t *, Agedge_t *); extern int flat_edges(Agraph_t *); - extern int flat_mval(Agnode_t *); - extern int in_cross(Agnode_t *, Agnode_t *); - extern void incr_width(Agraph_t *, Agnode_t *); - extern int inside_cluster(Agraph_t *, Agnode_t *); extern void install_cluster(Agraph_t *, Agnode_t *, int, queue *); extern void install_in_rank(Agraph_t *, Agnode_t *); - extern void interclexp(Agraph_t *); - extern void interclrep(Agraph_t *, Agedge_t *); - extern void interclust1(Agraph_t *, Agnode_t *, Agnode_t *, - Agedge_t *); - extern int is_a_normal_node_of(Agraph_t *, Agnode_t *); - extern int is_a_vnode_of_an_edge_of(Agraph_t *, Agnode_t *); extern int is_cluster(Agraph_t *); - extern int is_cluster_edge(Agedge_t *); - extern int is_fast_node(Agraph_t *, Agnode_t *); - extern Agnode_t *label_vnode(Agraph_t *, Agedge_t *); - extern Agnode_t *leader_of(Agraph_t *, Agnode_t *); - extern int left2right(Agraph_t *, Agnode_t *, Agnode_t *); - extern int local_cross(elist, int); extern void dot_compoundEdges(Agraph_t *); extern Agedge_t *make_aux_edge(Agnode_t *, Agnode_t *, int, int); - extern void make_chain(Agraph_t *, Agnode_t *, Agnode_t *, Agedge_t *); - extern void make_interclust_chain(Agraph_t *, Agnode_t *, Agnode_t *, - Agedge_t *); - extern int make_new_cluster(Agraph_t *, Agraph_t *); - extern void make_slots(Agraph_t *, int, int, int); - extern Agnode_t *map_interclust_node(Agnode_t *); - extern void map_path(Agnode_t *, Agnode_t *, Agedge_t *, Agedge_t *, - int); extern void mark_clusters(Agraph_t *); extern void mark_lowclusters(Agraph_t *); extern int mergeable(edge_t * e, edge_t * f); extern void merge_chain(Agraph_t *, Agedge_t *, Agedge_t *, int); - extern void merge_components(Agraph_t *); - extern Agnode_t *merge_leaves(Agraph_t *, Agnode_t *, Agnode_t *); extern void merge_oneway(Agedge_t *, Agedge_t *); - extern void merge_ranks(Agraph_t *); - extern void minmax_edges(Agraph_t *); extern int ncross(Agraph_t *); extern Agedge_t *new_virtual_edge(Agnode_t *, Agnode_t *, Agedge_t *); - extern void node_induce(Agraph_t *, Agraph_t *); extern int nonconstraint_edge(Agedge_t *); - extern int ordercmpf(int *, int *); - extern void ordered_edges(Agraph_t *); extern void other_edge(Agedge_t *); - extern int out_cross(Agnode_t *, Agnode_t *); - extern Agnode_t *plain_vnode(Agraph_t *, Agedge_t *); extern int portcmp(port p0, port p1); - extern void potential_leaf(Agraph_t *, Agedge_t *, Agnode_t *); extern int ports_eq(edge_t *, edge_t *); - extern void rank1(Agraph_t *); extern void rank(Agraph_t *, int, int); - extern int rank_set_class(Agraph_t *); - extern int rcross(Agraph_t *, int); extern void rec_reset_vlists(Agraph_t *); extern void rec_save_vlists(Agraph_t *); - extern void remove_rankleaders(Agraph_t *); - extern void renewlist(elist *); - extern void reorder(Agraph_t *, int, int, int); extern void reverse_edge(Agedge_t *); - extern void safe_delete_fast_edge(Agedge_t *); - extern void safe_list_append(Agedge_t *, elist *); extern void safe_other_edge(Agedge_t *); extern void save_vlist(Agraph_t *); - extern void scan_ranks(Agraph_t *); - extern void scan_result(void); - extern void search_component(Agraph_t *, Agnode_t *); - extern void set_minmax(Agraph_t *); - extern void setup_page(Agraph_t *, point); - extern int strccnt(char *, char); - extern void transpose(Agraph_t *, int); - extern int transpose_step(Agraph_t *, int, int); extern void unmerge_oneway(Agedge_t *); - extern void update(Agedge_t *, Agedge_t *); - extern void update_bb(Agraph_t *, point); extern Agedge_t *virtual_edge(Agnode_t *, Agnode_t *, Agedge_t *); extern Agnode_t *virtual_node(Agraph_t *); extern void virtual_weight(Agedge_t *); diff --git a/lib/dotgen/fastgr.c b/lib/dotgen/fastgr.c index 98253a4f6..b300403fc 100644 --- a/lib/dotgen/fastgr.c +++ b/lib/dotgen/fastgr.c @@ -47,7 +47,8 @@ edge_t *find_fast_edge(node_t * u, node_t * v) return ffe(u, ND_out(u), v, ND_in(v)); } -node_t *find_fast_node(graph_t * g, node_t * n) +static node_t* +find_fast_node(graph_t * g, node_t * n) { node_t *v; for (v = GD_nlist(g); v; v = ND_next(v)) @@ -62,7 +63,8 @@ edge_t *find_flat_edge(node_t * u, node_t * v) } /* safe_list_append - append e to list L only if e not already a member */ -void safe_list_append(edge_t * e, elist * L) +static void +safe_list_append(edge_t * e, elist * L) { int i; @@ -120,7 +122,8 @@ void delete_fast_edge(edge_t * e) zapinlist(&(ND_in(e->head)), e); } -void safe_delete_fast_edge(edge_t * e) +static void +safe_delete_fast_edge(edge_t * e) { int i; edge_t *f; @@ -144,11 +147,14 @@ void safe_other_edge(edge_t * e) safe_list_append(e, &(ND_other(e->tail))); } -void delete_other_edge(edge_t * e) +#ifdef OBSOLETET +static void +delete_other_edge(edge_t * e) { assert(e != NULL); zapinlist(&(ND_other(e->tail)), e); } +#endif /* orig might be an input edge, reverse of an input edge, or virtual edge */ edge_t *new_virtual_edge(node_t * u, node_t * v, edge_t * orig) @@ -302,18 +308,8 @@ void fastgr(graph_t * g) } #endif -void merge_oneway(edge_t * e, edge_t * rep) -{ - if (rep == ED_to_virt(e)) { - agerr(AGWARN, "merge_oneway glitch\n"); - return; - } - assert(ED_to_virt(e) == NULL); - ED_to_virt(e) = rep; - basic_merge(e, rep); -} - -void basic_merge(edge_t * e, edge_t * rep) +static void +basic_merge(edge_t * e, edge_t * rep) { if (ED_minlen(rep) < ED_minlen(e)) ED_minlen(rep) = ED_minlen(e); @@ -325,7 +321,20 @@ void basic_merge(edge_t * e, edge_t * rep) } } -static void unrep(edge_t * rep, edge_t * e) +void +merge_oneway(edge_t * e, edge_t * rep) +{ + if (rep == ED_to_virt(e)) { + agerr(AGWARN, "merge_oneway glitch\n"); + return; + } + assert(ED_to_virt(e) == NULL); + ED_to_virt(e) = rep; + basic_merge(e, rep); +} + +static void +unrep(edge_t * rep, edge_t * e) { ED_count(rep) -= ED_count(e); ED_xpenalty(rep) -= ED_xpenalty(e); @@ -352,7 +361,9 @@ void unmerge_oneway(edge_t * e) ED_to_virt(e) = NULL; } -int is_fast_node(graph_t * g, node_t * v) +#ifdef OBSOLETET +static int +is_fast_node(graph_t * g, node_t * v) { node_t *n; @@ -361,3 +372,4 @@ int is_fast_node(graph_t * g, node_t * v) return TRUE; return FALSE; } +#endif diff --git a/lib/dotgen/mincross.c b/lib/dotgen/mincross.c index db64720df..621412563 100644 --- a/lib/dotgen/mincross.c +++ b/lib/dotgen/mincross.c @@ -67,6 +67,23 @@ static edge_t **TE_list; static int *TI_list; static boolean ReMincross; +static void +dumpRanks (graph_t * g) +{ + int i, j; + node_t* u; + rank_t *rank = GD_rank(g); + for (i = GD_minrank(g); i <= GD_maxrank(g); i++) { + fprintf (stderr, "[%d] :", i); + for (j = 0; j < rank[i].n; j++) { + u = rank[i].v[j]; + fprintf (stderr, " %s", u->name); + + } + fprintf (stderr, "\n"); + } +} + void dot_mincross(graph_t * g) { int c, nc; @@ -101,6 +118,7 @@ void dot_mincross(graph_t * g) #endif } cleanup2(g, nc); + dumpRanks (g); } static adjmatrix_t *new_matrix(int i, int j) @@ -122,7 +140,8 @@ static void free_matrix(adjmatrix_t * p) #define ELT(M,i,j) (M->data[((i)*M->ncols)+(j)]) -static void init_mccomp(graph_t * g, int c) +static void +init_mccomp(graph_t * g, int c) { int r; @@ -135,7 +154,91 @@ static void init_mccomp(graph_t * g, int c) } } -static int mincross_clust(graph_t * par, graph_t * g) +static int betweenclust(edge_t * e) +{ + while (ED_to_orig(e)) + e = ED_to_orig(e); + return (ND_clust(e->tail) != ND_clust(e->head)); +} + +static void +do_ordering(graph_t * g, int outflag) +{ + int i, ne; + node_t *n, *u, *v; + edge_t *e, *f, *fe; + edge_t **sortlist = TE_list; + + for (n = agfstnode(g); n; n = agnxtnode(g, n)) { + if (ND_clust(n)) + continue; + if (outflag) { + for (i = ne = 0; (e = ND_out(n).list[i]); i++) + if (!betweenclust(e)) + sortlist[ne++] = e; + } else { + for (i = ne = 0; (e = ND_in(n).list[i]); i++) + if (!betweenclust(e)) + sortlist[ne++] = e; + } + if (ne <= 1) + continue; + /* write null terminator at end of list. + requires +1 in TE_list alloccation */ + sortlist[ne] = 0; + qsort(sortlist, ne, sizeof(sortlist[0]), (qsort_cmpf) edgeidcmpf); + for (ne = 1; (f = sortlist[ne]); ne++) { + e = sortlist[ne - 1]; + if (outflag) { + u = e->head; + v = f->head; + } else { + u = e->tail; + v = f->tail; + } + if (find_flat_edge(u, v)) + continue; + fe = new_virtual_edge(u, v, NULL); + ED_edge_type(fe) = FLATORDER; + flat_edge(g, fe); + } + } +} + +static void +ordered_edges(graph_t * g) +{ + char *ordering; + + if ((ordering = agget(g, "ordering"))) { + if (streq(ordering, "out")) + do_ordering(g, TRUE); + else if (streq(ordering, "in")) + do_ordering(g, FALSE); + else if (ordering[0]) + agerr(AGERR, "ordering '%s' not recognized.\n", ordering); + } + + else { + /* search meta-graph to find subgraphs that may be ordered */ + graph_t *mg, *subg; + node_t *mm, *mn; + edge_t *me; + + mm = g->meta_node; + mg = mm->graph; + for (me = agfstout(mg, mm); me; me = agnxtout(mg, me)) { + mn = me->head; + subg = agusergraph(mn); + /* clusters are processed by seperate calls to ordered_edges */ + if (!is_cluster(subg)) + ordered_edges(subg); + } + } +} + +static int +mincross_clust(graph_t * par, graph_t * g) { int c, nc; @@ -152,6 +255,166 @@ static int mincross_clust(graph_t * par, graph_t * g) return nc; } +static int +left2right(graph_t * g, node_t * v, node_t * w) +{ + adjmatrix_t *M; + int rv; + + /* CLUSTER indicates orig nodes of clusters, and vnodes of skeletons */ + if (ReMincross == FALSE) { + if ((ND_clust(v) != ND_clust(w)) && (ND_clust(v)) && (ND_clust(w))) { + /* the following allows cluster skeletons to be swapped */ + if ((ND_ranktype(v) == CLUSTER) + && (ND_node_type(v) == VIRTUAL)) + return FALSE; + if ((ND_ranktype(w) == CLUSTER) + && (ND_node_type(w) == VIRTUAL)) + return FALSE; + return TRUE; + /*return ((ND_ranktype(v) != CLUSTER) && (ND_ranktype(w) != CLUSTER)); */ + } + } else { + if ((ND_clust(v)) != (ND_clust(w))) + return TRUE; + } + M = GD_rank(g)[ND_rank(v)].flat; + if (M == NULL) + rv = FALSE; + else { + if (GD_flip(g)) { + node_t *t = v; + v = w; + w = t; + } + rv = ELT(M, flatindex(v), flatindex(w)); + } + return rv; +} + +static int +in_cross(node_t * v, node_t * w) +{ + register edge_t **e1, **e2; + register int inv, cross = 0, t; + + for (e2 = ND_in(w).list; *e2; e2++) { + register int cnt = (*e2)->u.xpenalty; + inv = ((*e2)->tail)->u.order; + + for (e1 = ND_in(v).list; *e1; e1++) { + t = ((*e1)->tail)->u.order - inv; + if ((t > 0) + || ((t == 0) + && ((*e1)->u.tail_port.p.x > (*e2)->u.tail_port.p.x))) + cross += (*e1)->u.xpenalty * cnt; + } + } + return cross; +} + +static int +out_cross(node_t * v, node_t * w) +{ + register edge_t **e1, **e2; + register int inv, cross = 0, t; + + for (e2 = ND_out(w).list; *e2; e2++) { + register int cnt = (*e2)->u.xpenalty; + inv = ((*e2)->head)->u.order; + + for (e1 = ND_out(v).list; *e1; e1++) { + t = ((*e1)->head)->u.order - inv; + if ((t > 0) + || ((t == 0) + && ((*e1)->u.head_port.p.x > (*e2)->u.head_port.p.x))) + cross += (*e1)->u.xpenalty * cnt; + } + } + return cross; + +} + +static void +exchange(node_t * v, node_t * w) +{ + int vi, wi, r; + + r = ND_rank(v); + vi = ND_order(v); + wi = ND_order(w); + ND_order(v) = wi; + GD_rank(Root)[r].v[wi] = v; + ND_order(w) = vi; + GD_rank(Root)[r].v[vi] = w; +} + +static int +transpose_step(graph_t * g, int r, int reverse) +{ + int i, c0, c1, rv; + node_t *v, *w; + + rv = 0; + GD_rank(g)[r].candidate = FALSE; + for (i = 0; i < GD_rank(g)[r].n - 1; i++) { + v = GD_rank(g)[r].v[i]; + w = GD_rank(g)[r].v[i + 1]; + assert(ND_order(v) < ND_order(w)); + if (left2right(g, v, w)) + continue; + c0 = c1 = 0; + if (r > 0) { + c0 += in_cross(v, w); + c1 += in_cross(w, v); + } + if (GD_rank(g)[r + 1].n > 0) { + c0 += out_cross(v, w); + c1 += out_cross(w, v); + } + if ((c1 < c0) || ((c0 > 0) && reverse && (c1 == c0))) { + exchange(v, w); + rv += (c0 - c1); + GD_rank(Root)[r].valid = FALSE; + GD_rank(g)[r].candidate = TRUE; + + if (r > GD_minrank(g)) { + GD_rank(Root)[r - 1].valid = FALSE; + GD_rank(g)[r - 1].candidate = TRUE; + } + if (r < GD_maxrank(g)) { + GD_rank(Root)[r + 1].valid = FALSE; + GD_rank(g)[r + 1].candidate = TRUE; + } + } + } + return rv; +} + +static void +transpose(graph_t * g, int reverse) +{ + int r, delta; + + for (r = GD_minrank(g); r <= GD_maxrank(g); r++) + GD_rank(g)[r].candidate = TRUE; + do { + delta = 0; +#ifdef NOTDEF + /* don't run both the upward and downward passes- they cancel. + i tried making it depend on whether an odd or even pass, + but that didn't help. */ + for (r = GD_maxrank(g); r >= GD_minrank(g); r--) + if (GD_rank(g)[r].candidate) + delta += transpose_step(g, r, reverse); +#endif + for (r = GD_minrank(g); r <= GD_maxrank(g); r++) + if (GD_rank(g)[r].candidate) + delta += transpose_step(g, r, reverse); + /*} while (delta > ncross(g)*(1.0 - Convergence)); */ + } while (delta >= 1); +} + static int mincross(graph_t * g, int startpass, int endpass) { int maxthispass, iter, trying, pass; @@ -233,28 +496,54 @@ static void save_best(graph_t * g) saveorder(n) = ND_order(n); } -/* merge connected components, create globally consistent rank lists */ -static void merge2(graph_t * g) +/* merges the connected components of g */ +static void +merge_components(graph_t * g) { - int i, r; - node_t *v; - - /* merge the components and rank limits */ - merge_components(g); + int c; + node_t *u, *v; - /* install complete ranks */ - for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { - GD_rank(g)[r].n = GD_rank(g)[r].an; - GD_rank(g)[r].v = GD_rank(g)[r].av; - for (i = 0; i < GD_rank(g)[r].n; i++) { - v = GD_rank(g)[r].v[i]; - if (v == NULL) { - if (Verbose) - fprintf(stderr, - "merge2: graph %s, rank %d has only %d < %d nodes\n", - g->name, r, i, GD_rank(g)[r].n); - GD_rank(g)[r].n = i; - break; + if (GD_comp(g).size <= 1) + return; + u = NULL; + for (c = 0; c < GD_comp(g).size; c++) { + v = GD_comp(g).list[c]; + if (u) + ND_next(u) = v; + ND_prev(v) = u; + while (ND_next(v)) { + v = ND_next(v); + } + u = v; + } + GD_comp(g).size = 1; + GD_nlist(g) = GD_comp(g).list[0]; + GD_minrank(g) = GlobalMinRank; + GD_maxrank(g) = GlobalMaxRank; +} + +/* merge connected components, create globally consistent rank lists */ +static void merge2(graph_t * g) +{ + int i, r; + node_t *v; + + /* merge the components and rank limits */ + merge_components(g); + + /* install complete ranks */ + for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { + GD_rank(g)[r].n = GD_rank(g)[r].an; + GD_rank(g)[r].v = GD_rank(g)[r].av; + for (i = 0; i < GD_rank(g)[r].n; i++) { + v = GD_rank(g)[r].v[i]; + if (v == NULL) { + if (Verbose) + fprintf(stderr, + "merge2: graph %s, rank %d has only %d < %d nodes\n", + g->name, r, i, GD_rank(g)[r].n); + GD_rank(g)[r].n = i; + break; } ND_order(v) = i; } @@ -313,17 +602,14 @@ static node_t *neighbor(node_t * v, int dir) return rv; } -int inside_cluster(graph_t * g, node_t * v) -{ - return (is_a_normal_node_of(g, v) | is_a_vnode_of_an_edge_of(g, v)); -} - -int is_a_normal_node_of(graph_t * g, node_t * v) +static int +is_a_normal_node_of(graph_t * g, node_t * v) { return ((ND_node_type(v) == NORMAL) && agcontains(g, v)); } -int is_a_vnode_of_an_edge_of(graph_t * g, node_t * v) +static int +is_a_vnode_of_an_edge_of(graph_t * g, node_t * v) { if ((ND_node_type(v) == VIRTUAL) && (ND_in(v).size == 1) && (ND_out(v).size == 1)) { @@ -336,6 +622,12 @@ int is_a_vnode_of_an_edge_of(graph_t * g, node_t * v) return FALSE; } +static int +inside_cluster(graph_t * g, node_t * v) +{ + return (is_a_normal_node_of(g, v) | is_a_vnode_of_an_edge_of(g, v)); +} + static node_t *furthestnode(graph_t * g, node_t * v, int dir) { node_t *u, *rv; @@ -500,42 +792,6 @@ static void flat_breakcycles(graph_t * g) } } -int left2right(graph_t * g, node_t * v, node_t * w) -{ - adjmatrix_t *M; - int rv; - - /* CLUSTER indicates orig nodes of clusters, and vnodes of skeletons */ - if (ReMincross == FALSE) { - if ((ND_clust(v) != ND_clust(w)) && (ND_clust(v)) && (ND_clust(w))) { - /* the following allows cluster skeletons to be swapped */ - if ((ND_ranktype(v) == CLUSTER) - && (ND_node_type(v) == VIRTUAL)) - return FALSE; - if ((ND_ranktype(w) == CLUSTER) - && (ND_node_type(w) == VIRTUAL)) - return FALSE; - return TRUE; - /*return ((ND_ranktype(v) != CLUSTER) && (ND_ranktype(w) != CLUSTER)); */ - } - } else { - if ((ND_clust(v)) != (ND_clust(w))) - return TRUE; - } - M = GD_rank(g)[ND_rank(v)].flat; - if (M == NULL) - rv = FALSE; - else { - if (GD_flip(g)) { - node_t *t = v; - v = w; - w = t; - } - rv = ELT(M, flatindex(v), flatindex(w)); - } - return rv; -} - void allocate_ranks(graph_t * g) { int r, low, high, *cn; @@ -775,7 +1031,61 @@ static void flat_reorder(graph_t * g) free(temprank); } -static void mincross_step(graph_t * g, int pass) +static void +reorder(graph_t * g, int r, int reverse, int hasfixed) +{ + int changed = 0, nelt; + boolean muststay, sawclust; + node_t **vlist = GD_rank(g)[r].v; + node_t **lp, **rp, **ep = vlist + GD_rank(g)[r].n; + + for (nelt = GD_rank(g)[r].n - 1; nelt >= 0; nelt--) { + lp = vlist; + while (lp < ep) { + /* find leftmost node that can be compared */ + while ((lp < ep) && ((*lp)->u.mval < 0)) + lp++; + if (lp >= ep) + break; + /* find the node that can be compared */ + sawclust = muststay = FALSE; + for (rp = lp + 1; rp < ep; rp++) { + if (sawclust && (*rp)->u.clust) + continue; /* ### */ + if (left2right(g, *lp, *rp)) { + muststay = TRUE; + break; + } + if ((*rp)->u.mval >= 0) + break; + if ((*rp)->u.clust) + sawclust = TRUE; /* ### */ + } + if (rp >= ep) + break; + if (muststay == FALSE) { + register int p1 = ((*lp)->u.mval); + register int p2 = ((*rp)->u.mval); + if ((p1 > p2) || ((p1 == p2) && (reverse))) { + exchange(*lp, *rp); + changed++; + } + } + lp = rp; + } + if ((hasfixed == FALSE) && (reverse == FALSE)) + ep--; + } + + if (changed) { + GD_rank(Root)[r].valid = FALSE; + if (r > 0) + GD_rank(Root)[r - 1].valid = FALSE; + } +} + +static void +mincross_step(graph_t * g, int pass) { int r, other, first, last, dir; int hasfixed, reverse; @@ -815,30 +1125,8 @@ static void mincross_step(graph_t * g, int pass) transpose(g, NOT(reverse)); } -void transpose(graph_t * g, int reverse) -{ - int r, delta; - - for (r = GD_minrank(g); r <= GD_maxrank(g); r++) - GD_rank(g)[r].candidate = TRUE; - do { - delta = 0; -#ifdef NOTDEF - /* don't run both the upward and downward passes- they cancel. - i tried making it depend on whether an odd or even pass, - but that didn't help. */ - for (r = GD_maxrank(g); r >= GD_minrank(g); r--) - if (GD_rank(g)[r].candidate) - delta += transpose_step(g, r, reverse); -#endif - for (r = GD_minrank(g); r <= GD_maxrank(g); r++) - if (GD_rank(g)[r].candidate) - delta += transpose_step(g, r, reverse); - /*} while (delta > ncross(g)*(1.0 - Convergence)); */ - } while (delta >= 1); -} - -int local_cross(elist l, int dir) +static int +local_cross(elist l, int dir) { int i, j, is_out; int cross = 0; @@ -865,7 +1153,8 @@ int local_cross(elist l, int dir) return cross; } -int rcross(graph_t * g, int r) +static int +rcross(graph_t * g, int r) { static int *Count, C; int top, bot, cross, max, i, k; @@ -929,50 +1218,39 @@ int ncross(graph_t * g) return count; } -int ordercmpf(int *i0, int *i1) +static int +ordercmpf(int *i0, int *i1) { return (*i0) - (*i1); } -int out_cross(node_t * v, node_t * w) +static int +flat_mval(node_t * n) { - register edge_t **e1, **e2; - register int inv, cross = 0, t; - - for (e2 = ND_out(w).list; *e2; e2++) { - register int cnt = (*e2)->u.xpenalty; - inv = ((*e2)->head)->u.order; - - for (e1 = ND_out(v).list; *e1; e1++) { - t = ((*e1)->head)->u.order - inv; - if ((t > 0) - || ((t == 0) - && ((*e1)->u.head_port.p.x > (*e2)->u.head_port.p.x))) - cross += (*e1)->u.xpenalty * cnt; - } - } - return cross; - -} - -int in_cross(node_t * v, node_t * w) -{ - register edge_t **e1, **e2; - register int inv, cross = 0, t; - - for (e2 = ND_in(w).list; *e2; e2++) { - register int cnt = (*e2)->u.xpenalty; - inv = ((*e2)->tail)->u.order; + int i; + edge_t *e, **fl; + node_t *nn; - for (e1 = ND_in(v).list; *e1; e1++) { - t = ((*e1)->tail)->u.order - inv; - if ((t > 0) - || ((t == 0) - && ((*e1)->u.tail_port.p.x > (*e2)->u.tail_port.p.x))) - cross += (*e1)->u.xpenalty * cnt; + if ((ND_in(n).size == 0) && (ND_out(n).size == 0)) { + if (ND_flat_in(n).size > 0) { + fl = ND_flat_in(n).list; + nn = fl[0]->tail; + for (i = 1; (e = fl[i]); i++) + if (ND_order(e->tail) > ND_order(nn)) + nn = e->tail; + ND_mval(n) = ND_mval(nn) + 1; + return FALSE; + } else if (ND_flat_out(n).size > 0) { + fl = ND_flat_out(n).list; + nn = fl[0]->head; + for (i = 1; (e = fl[i]); i++) + if (ND_order(e->head) < ND_order(nn)) + nn = e->head; + ND_mval(n) = ND_mval(nn) - 1; + return FALSE; } } - return cross; + return TRUE; } #define VAL(node,port) (MC_SCALE * (node)->u.order + (port).order) @@ -1035,112 +1313,6 @@ static boolean medians(graph_t * g, int r0, int r1) return hasfixed; } -int transpose_step(graph_t * g, int r, int reverse) -{ - int i, c0, c1, rv; - node_t *v, *w; - - rv = 0; - GD_rank(g)[r].candidate = FALSE; - for (i = 0; i < GD_rank(g)[r].n - 1; i++) { - v = GD_rank(g)[r].v[i]; - w = GD_rank(g)[r].v[i + 1]; - assert(ND_order(v) < ND_order(w)); - if (left2right(g, v, w)) - continue; - c0 = c1 = 0; - if (r > 0) { - c0 += in_cross(v, w); - c1 += in_cross(w, v); - } - if (GD_rank(g)[r + 1].n > 0) { - c0 += out_cross(v, w); - c1 += out_cross(w, v); - } - if ((c1 < c0) || ((c0 > 0) && reverse && (c1 == c0))) { - exchange(v, w); - rv += (c0 - c1); - GD_rank(Root)[r].valid = FALSE; - GD_rank(g)[r].candidate = TRUE; - - if (r > GD_minrank(g)) { - GD_rank(Root)[r - 1].valid = FALSE; - GD_rank(g)[r - 1].candidate = TRUE; - } - if (r < GD_maxrank(g)) { - GD_rank(Root)[r + 1].valid = FALSE; - GD_rank(g)[r + 1].candidate = TRUE; - } - } - } - return rv; -} - -void exchange(node_t * v, node_t * w) -{ - int vi, wi, r; - - r = ND_rank(v); - vi = ND_order(v); - wi = ND_order(w); - ND_order(v) = wi; - GD_rank(Root)[r].v[wi] = v; - ND_order(w) = vi; - GD_rank(Root)[r].v[vi] = w; -} - -void reorder(graph_t * g, int r, int reverse, int hasfixed) -{ - int changed = 0, nelt; - boolean muststay, sawclust; - node_t **vlist = GD_rank(g)[r].v; - node_t **lp, **rp, **ep = vlist + GD_rank(g)[r].n; - - for (nelt = GD_rank(g)[r].n - 1; nelt >= 0; nelt--) { - lp = vlist; - while (lp < ep) { - /* find leftmost node that can be compared */ - while ((lp < ep) && ((*lp)->u.mval < 0)) - lp++; - if (lp >= ep) - break; - /* find the node that can be compared */ - sawclust = muststay = FALSE; - for (rp = lp + 1; rp < ep; rp++) { - if (sawclust && (*rp)->u.clust) - continue; /* ### */ - if (left2right(g, *lp, *rp)) { - muststay = TRUE; - break; - } - if ((*rp)->u.mval >= 0) - break; - if ((*rp)->u.clust) - sawclust = TRUE; /* ### */ - } - if (rp >= ep) - break; - if (muststay == FALSE) { - register int p1 = ((*lp)->u.mval); - register int p2 = ((*rp)->u.mval); - if ((p1 > p2) || ((p1 == p2) && (reverse))) { - exchange(*lp, *rp); - changed++; - } - } - lp = rp; - } - if ((hasfixed == FALSE) && (reverse == FALSE)) - ep--; - } - - if (changed) { - GD_rank(Root)[r].valid = FALSE; - if (r > 0) - GD_rank(Root)[r - 1].valid = FALSE; - } -} - static int nodeposcmpf(node_t ** n0, node_t ** n1) { return ((*n0)->u.order - (*n1)->u.order); @@ -1184,112 +1356,6 @@ void virtual_weight(edge_t * e) ED_weight(e) *= t; } -void ordered_edges(graph_t * g) -{ - char *ordering; - - if ((ordering = agget(g, "ordering"))) { - if (streq(ordering, "out")) - do_ordering(g, TRUE); - else if (streq(ordering, "in")) - do_ordering(g, FALSE); - else if (ordering[0]) - agerr(AGERR, "ordering '%s' not recognized.\n", ordering); - } - - else { - /* search meta-graph to find subgraphs that may be ordered */ - graph_t *mg, *subg; - node_t *mm, *mn; - edge_t *me; - - mm = g->meta_node; - mg = mm->graph; - for (me = agfstout(mg, mm); me; me = agnxtout(mg, me)) { - mn = me->head; - subg = agusergraph(mn); - /* clusters are processed by seperate calls to ordered_edges */ - if (!is_cluster(subg)) - ordered_edges(subg); - } - } -} - -static int betweenclust(edge_t * e) -{ - while (ED_to_orig(e)) - e = ED_to_orig(e); - return (ND_clust(e->tail) != ND_clust(e->head)); -} - -void do_ordering(graph_t * g, int outflag) -{ - int i, ne; - node_t *n, *u, *v; - edge_t *e, *f, *fe; - edge_t **sortlist = TE_list; - - for (n = agfstnode(g); n; n = agnxtnode(g, n)) { - if (ND_clust(n)) - continue; - if (outflag) { - for (i = ne = 0; (e = ND_out(n).list[i]); i++) - if (!betweenclust(e)) - sortlist[ne++] = e; - } else { - for (i = ne = 0; (e = ND_in(n).list[i]); i++) - if (!betweenclust(e)) - sortlist[ne++] = e; - } - if (ne <= 1) - continue; - /* write null terminator at end of list. - requires +1 in TE_list alloccation */ - sortlist[ne] = 0; - qsort(sortlist, ne, sizeof(sortlist[0]), (qsort_cmpf) edgeidcmpf); - for (ne = 1; (f = sortlist[ne]); ne++) { - e = sortlist[ne - 1]; - if (outflag) { - u = e->head; - v = f->head; - } else { - u = e->tail; - v = f->tail; - } - if (find_flat_edge(u, v)) - continue; - fe = new_virtual_edge(u, v, NULL); - ED_edge_type(fe) = FLATORDER; - flat_edge(g, fe); - } - } -} - -/* merges the connected components of g */ -void merge_components(graph_t * g) -{ - int c; - node_t *u, *v; - - if (GD_comp(g).size <= 1) - return; - u = NULL; - for (c = 0; c < GD_comp(g).size; c++) { - v = GD_comp(g).list[c]; - if (u) - ND_next(u) = v; - ND_prev(v) = u; - while (ND_next(v)) { - v = ND_next(v); - } - u = v; - } - GD_comp(g).size = 1; - GD_nlist(g) = GD_comp(g).list[0]; - GD_minrank(g) = GlobalMinRank; - GD_maxrank(g) = GlobalMaxRank; -} - #ifdef DEBUG void check_rs(graph_t * g, int null_ok) { @@ -1348,34 +1414,6 @@ static void mincross_options(graph_t * g) } } -int flat_mval(node_t * n) -{ - int i; - edge_t *e, **fl; - node_t *nn; - - if ((ND_in(n).size == 0) && (ND_out(n).size == 0)) { - if (ND_flat_in(n).size > 0) { - fl = ND_flat_in(n).list; - nn = fl[0]->tail; - for (i = 1; (e = fl[i]); i++) - if (ND_order(e->tail) > ND_order(nn)) - nn = e->tail; - ND_mval(n) = ND_mval(nn) + 1; - return FALSE; - } else if (ND_flat_out(n).size > 0) { - fl = ND_flat_out(n).list; - nn = fl[0]->head; - for (i = 1; (e = fl[i]); i++) - if (ND_order(e->head) < ND_order(nn)) - nn = e->head; - ND_mval(n) = ND_mval(nn) - 1; - return FALSE; - } - } - return TRUE; -} - #ifdef DEBUG void check_exchange(node_t * v, node_t * w) { diff --git a/lib/dotgen/position.c b/lib/dotgen/position.c index 61a75249c..bf2806bb3 100644 --- a/lib/dotgen/position.c +++ b/lib/dotgen/position.c @@ -36,6 +36,25 @@ static void make_lrvn(graph_t * g); static void contain_nodes(graph_t * g); static int idealsize(graph_t * g, double); +static void +dumpNS (graph_t * g) +{ + node_t* n = GD_nlist(g); + elist el; + edge_t* e; + int i; + + while (n) { + el = ND_out(n); + for (i = 0; i < el.size; i++) { + e = el.list[i]; + fprintf (stderr, "%s(%x) -> %s(%x) : %d\n", e->tail->name,e->tail, e->head->name, e->head, + ED_minlen(e)); + } + n = ND_next(n); + } +} + void dot_position(graph_t * g) { if (GD_nlist(g) == NULL) @@ -48,6 +67,7 @@ void dot_position(graph_t * g) if (flat_edges(g)) set_ycoords(g); create_aux_edges(g); +/* dumpNS (g); */ rank(g, 2, nsiter2(g)); /* LR balance == 2 */ set_xcoords(g); set_aspect(g); @@ -136,17 +156,18 @@ static void make_LR_constraints(graph_t * g) u = rank[i].v[j]; ND_mval(u) = ND_rw_i(u); /* keep it somewhere safe */ if (ND_other(u).size > 0) { /* compute self size */ + /* FIX: dot assumes all self-edges go to the right. This + * is no longer true, though makeSelfEdge still attempts to + * put as many as reasonable on the right. The dot code + * should be modified to allow a box reflecting the placement + * of all self-edges, and use that to reposition the nodes. + * Note that this would not only affect left and right + * positioning but may also affect interrank spacing. + */ sw = 0; for (k = 0; (e = ND_other(u).list[k]); k++) { if (e->tail == e->head) { - sw += SELF_EDGE_SIZE; - if (ED_label(e)) { - double label_width; - label_width = - GD_flip(g) ? ED_label(e)->dimen. - y : ED_label(e)->dimen.x; - sw += label_width; - } + sw += selfRightSpace (e); } } ND_rw_i(u) += sw; /* increment to include self edges */ diff --git a/lib/dotgen/rank.c b/lib/dotgen/rank.c index a645f6da8..d3f428dc4 100644 --- a/lib/dotgen/rank.c +++ b/lib/dotgen/rank.c @@ -31,26 +31,62 @@ #include "dot.h" +static void +renewlist(elist * L) +{ + int i; + for (i = L->size; i >= 0; i--) + L->list[i] = NULL; + L->size = 0; +} -void dot_rank(graph_t * g) +static void +cleanup1(graph_t * g) { - edgelabel_ranks(g); - collapse_sets(g); - /*collapse_leaves(g); */ - class1(g); - minmax_edges(g); - decompose(g, 0); - acyclic(g); - rank1(g); - expand_ranksets(g); - cleanup1(g); + node_t *n; + edge_t *e, *f; + int c; + + for (c = 0; c < GD_comp(g).size; c++) { + GD_nlist(g) = GD_comp(g).list[c]; + for (n = GD_nlist(g); n; n = ND_next(n)) { + renewlist(&ND_in(n)); + renewlist(&ND_out(n)); + ND_mark(n) = FALSE; + } + } + for (n = agfstnode(g); n; n = agnxtnode(g, n)) { + for (e = agfstout(g, n); e; e = agnxtout(g, e)) { + f = ED_to_virt(e); + if (f && (e == ED_to_orig(f))) { + /* Null out any other references to f to make sure we don't handle it + * a second time. For example, parallel multiedges share a virtual edge. + */ + edge_t *e1, *f1; + for (e1 = agfstout(g, n); e1; e1 = agnxtout(g, e1)) { + if (e != e1) { + f1 = ED_to_virt(e1); + if (f1 && (f == f1)) { + ED_to_virt(e1) = NULL; + } + } + } + free(f); + } + ED_to_virt(e) = NULL; + } + } + free(GD_comp(g).list); + GD_comp(g).list = NULL; + GD_comp(g).size = 0; } /* When there are edge labels, extra ranks are reserved here for the virtual * nodes of the labels. This is done by doubling the input edge lengths. * The input rank separation is adjusted to compensate. */ -void edgelabel_ranks(graph_t * g) +static void +edgelabel_ranks(graph_t * g) { node_t *n; edge_t *e; @@ -63,70 +99,9 @@ void edgelabel_ranks(graph_t * g) } } -/* Run the network simplex algorithm on each component. */ -void rank1(graph_t * g) -{ - int maxiter = MAXINT; - int c; - char *s; - - if ((s = agget(g, "nslimit1"))) - maxiter = atof(s) * agnnodes(g); - for (c = 0; c < GD_comp(g).size; c++) { - GD_nlist(g) = GD_comp(g).list[c]; - rank(g, (GD_n_cluster(g) == 0 ? 1 : 0), maxiter); /* TB balance */ - } -} - -int is_cluster(graph_t * g) -{ - return (strncmp(g->name, "cluster", 7) == 0); -} - -int rank_set_class(graph_t * g) -{ - static char *name[] = { "same", "min", "source", "max", "sink", NULL }; - static int class[] = - { SAMERANK, MINRANK, SOURCERANK, MAXRANK, SINKRANK, 0 }; - int val; - - if (is_cluster(g)) - return CLUSTER; - val = maptoken(agget(g, "rank"), name, class); - GD_set_type(g) = val; - return val; -} - -/* Execute union commands for "same rank" subgraphs and clusters. */ -void collapse_sets(graph_t * g) -{ - int c; - graph_t *mg, *subg; - node_t *mn, *n; - edge_t *me; - - mg = g->meta_node->graph; - for (me = agfstout(mg, g->meta_node); me; me = agnxtout(mg, me)) { - mn = me->head; - subg = agusergraph(mn); - - c = rank_set_class(subg); - if (c) { - if ((c == CLUSTER) && CL_type == LOCAL) - collapse_cluster(g, subg); - else - collapse_rankset(g, subg, c); - } - - /* mark nodes with ordered edges so their leaves are not collapsed */ - if (agget(subg, "ordering")) - for (n = agfstnode(subg); n; n = agnxtnode(subg, n)) - ND_order(n) = 1; - } -} - /* Merge the nodes of a min, max, or same rank set. */ -void collapse_rankset(graph_t * g, graph_t * subg, int kind) +static void +collapse_rankset(graph_t * g, graph_t * subg, int kind) { node_t *u, *v; @@ -164,101 +139,104 @@ void collapse_rankset(graph_t * g, graph_t * subg, int kind) } } -node_t *merge_leaves(graph_t * g, node_t * cur, node_t * new) +static int +rank_set_class(graph_t * g) { - node_t *rv; + static char *name[] = { "same", "min", "source", "max", "sink", NULL }; + static int class[] = + { SAMERANK, MINRANK, SOURCERANK, MAXRANK, SINKRANK, 0 }; + int val; - if (cur == NULL) - rv = new; - else { - rv = UF_union(cur, new); - ND_ht_i(rv) = MAX(ND_ht_i(cur), ND_ht_i(new)); - ND_lw_i(rv) = ND_lw_i(cur) + ND_lw_i(new) + GD_nodesep(g) / 2; - ND_rw_i(rv) = ND_rw_i(cur) + ND_rw_i(new) + GD_nodesep(g) / 2; - } - return rv; + if (is_cluster(g)) + return CLUSTER; + val = maptoken(agget(g, "rank"), name, class); + GD_set_type(g) = val; + return val; } -void potential_leaf(graph_t * g, edge_t * e, node_t * leaf) +static int +make_new_cluster(graph_t * g, graph_t * subg) { - node_t *par; - - if ((ED_tail_port(e).p.x) || (ED_head_port(e).p.x)) - return; - if ((ED_minlen(e) != 1) || (ND_order(e->tail) > 0)) - return; - par = ((leaf != e->head) ? e->head : e->tail); - ND_ranktype(leaf) = LEAFSET; - if (par == e->tail) - GD_outleaf(par) = merge_leaves(g, GD_outleaf(par), leaf); - else - GD_inleaf(par) = merge_leaves(g, GD_inleaf(par), leaf); + int cno; + cno = ++(GD_n_cluster(g)); + GD_clust(g) = ZALLOC(cno + 1, GD_clust(g), graph_t *, GD_n_cluster(g)); + GD_clust(g)[cno] = subg; + do_graph_label(subg); + return cno; } -void collapse_leaves(graph_t * g) +static void +node_induce(graph_t * par, graph_t * g) { - node_t *n; + node_t *n, *nn; edge_t *e; + int i; - for (n = agfstnode(g); n; n = agnxtnode(g, n)) { - - /* consider n as a potential leaf of some other node. */ - if ((ND_ranktype(n) != NOCMD) || (ND_order(n))) + /* enforce that a node is in at most one cluster at this level */ + for (n = agfstnode(g); n; n = nn) { + nn = agnxtnode(g, n); + if (ND_ranktype(n)) { + agdelete(g, n); continue; - if (agfstout(g, n) == NULL) { - if ((e = agfstin(g, n)) && (agnxtin(g, e) == NULL)) { - potential_leaf(g, e, n); - continue; - } } - if (agfstin(g, n) == NULL) { - if ((e = agfstout(g, n)) && (agnxtout(g, e) == NULL)) { - potential_leaf(g, e, n); - continue; - } + for (i = 1; i < GD_n_cluster(par); i++) + if (agcontains(GD_clust(par)[i], n)) + break; + if (i < GD_n_cluster(par)) + agdelete(g, n); + ND_clust(n) = NULL; + } + + for (n = agfstnode(g); n; n = agnxtnode(g, n)) { + for (e = agfstout(g->root, n); e; e = agnxtout(g->root, e)) { + if (agcontains(g, e->head)) + aginsert(g, e); } } } -/* To ensure that min and max rank nodes always have the intended rank - * assignment, reverse any incompatible edges. - */ -void minmax_edges(graph_t * g) +static void +scan_ranks(graph_t * g) { - node_t *n; - edge_t *e; - int srclen, sinklen; - - srclen = sinklen = 0; - if ((GD_maxset(g) == NULL) && (GD_minset(g) == NULL)) - return; - if (GD_minset(g) != NULL) - GD_minset(g) = UF_find(GD_minset(g)); - if (GD_maxset(g) != NULL) - GD_maxset(g) = UF_find(GD_maxset(g)); - - if ((n = GD_maxset(g))) { - sinklen = (GD_maxset(g)->u.ranktype == SINKRANK); - while ((e = ND_out(n).list[0])) { - assert(e->head == UF_find(e->head)); - reverse_edge(e); + node_t *n, *leader = NULL; + GD_minrank(g) = MAXSHORT; + GD_maxrank(g) = -1; + for (n = agfstnode(g); n; n = agnxtnode(g, n)) { + if (GD_maxrank(g) < ND_rank(n)) + GD_maxrank(g) = ND_rank(n); + if (GD_minrank(g) > ND_rank(n)) + GD_minrank(g) = ND_rank(n); + if (leader == NULL) + leader = n; + else { + if (ND_rank(n) < ND_rank(leader)) + leader = n; } } - if ((n = GD_minset(g))) { - srclen = (GD_minset(g)->u.ranktype == SOURCERANK); - while ((e = ND_in(n).list[0])) { - assert(e->tail == UF_find(e->tail)); - reverse_edge(e); - } + GD_leader(g) = leader; +} + +static void +cluster_leader(graph_t * clust) +{ + node_t *leader, *n; + int maxrank = 0; + + /* find number of ranks and select a leader */ + leader = NULL; + for (n = GD_nlist(clust); n; n = ND_next(n)) { + if ((ND_rank(n) == 0) && (ND_node_type(n) == NORMAL)) + leader = n; + if (maxrank < ND_rank(n)) + maxrank = ND_rank(n); } + assert(leader != NULL); + GD_leader(clust) = leader; - for (n = agfstnode(g); n; n = agnxtnode(g, n)) { - if (n != UF_find(n)) - continue; - if ((ND_out(n).size == 0) && GD_maxset(g) && (n != GD_maxset(g))) - virtual_edge(n, GD_maxset(g), NULL)->u.minlen = sinklen; - if ((ND_in(n).size == 0) && GD_minset(g) && (n != GD_minset(g))) - virtual_edge(GD_minset(g), n, NULL)->u.minlen = srclen; + for (n = agfstnode(clust); n; n = agnxtnode(clust, n)) { + assert((ND_UF_size(n) <= 1) || (n == leader)); + UF_union(n, leader); + ND_ranktype(n) = CLUSTER; } } @@ -269,7 +247,8 @@ void minmax_edges(graph_t * g) * 3) In class1(), any inter-cluster edges are converted using * the "virtual node + 2 edges" trick. */ -void collapse_cluster(graph_t * g, graph_t * subg) +static void +collapse_cluster(graph_t * g, graph_t * subg) { if (GD_cluster_was_collapsed(subg)) return; @@ -285,65 +264,119 @@ void collapse_cluster(graph_t * g, graph_t * subg) scan_ranks(subg); } -int make_new_cluster(graph_t * g, graph_t * subg) +/* Execute union commands for "same rank" subgraphs and clusters. */ +static void +collapse_sets(graph_t * g) { - int cno; - cno = ++(GD_n_cluster(g)); - GD_clust(g) = ZALLOC(cno + 1, GD_clust(g), graph_t *, GD_n_cluster(g)); - GD_clust(g)[cno] = subg; - do_graph_label(subg); - return cno; + int c; + graph_t *mg, *subg; + node_t *mn, *n; + edge_t *me; + + mg = g->meta_node->graph; + for (me = agfstout(mg, g->meta_node); me; me = agnxtout(mg, me)) { + mn = me->head; + subg = agusergraph(mn); + + c = rank_set_class(subg); + if (c) { + if ((c == CLUSTER) && CL_type == LOCAL) + collapse_cluster(g, subg); + else + collapse_rankset(g, subg, c); + } + + /* mark nodes with ordered edges so their leaves are not collapsed */ + if (agget(subg, "ordering")) + for (n = agfstnode(subg); n; n = agnxtnode(subg, n)) + ND_order(n) = 1; + } } -void node_induce(graph_t * par, graph_t * g) +static void +find_clusters(graph_t * g) { - node_t *n, *nn; + graph_t *mg, *subg; + node_t *mn; + edge_t *me; + + mg = g->meta_node->graph; + for (me = agfstout(mg, g->meta_node); me; me = agnxtout(mg, me)) { + mn = me->head; + subg = agusergraph(mn); + + if (GD_set_type(subg) == CLUSTER) + collapse_cluster(g, subg); + } +} + +static void +set_minmax(graph_t * g) +{ + int c; + + GD_minrank(g) += GD_leader(g)->u.rank; + GD_maxrank(g) += GD_leader(g)->u.rank; + for (c = 1; c <= GD_n_cluster(g); c++) + set_minmax(GD_clust(g)[c]); +} + +/* To ensure that min and max rank nodes always have the intended rank + * assignment, reverse any incompatible edges. + */ +static void +minmax_edges(graph_t * g) +{ + node_t *n; edge_t *e; - int i; + int srclen, sinklen; - /* enforce that a node is in at most one cluster at this level */ - for (n = agfstnode(g); n; n = nn) { - nn = agnxtnode(g, n); - if (ND_ranktype(n)) { - agdelete(g, n); - continue; + srclen = sinklen = 0; + if ((GD_maxset(g) == NULL) && (GD_minset(g) == NULL)) + return; + if (GD_minset(g) != NULL) + GD_minset(g) = UF_find(GD_minset(g)); + if (GD_maxset(g) != NULL) + GD_maxset(g) = UF_find(GD_maxset(g)); + + if ((n = GD_maxset(g))) { + sinklen = (GD_maxset(g)->u.ranktype == SINKRANK); + while ((e = ND_out(n).list[0])) { + assert(e->head == UF_find(e->head)); + reverse_edge(e); + } + } + if ((n = GD_minset(g))) { + srclen = (GD_minset(g)->u.ranktype == SOURCERANK); + while ((e = ND_in(n).list[0])) { + assert(e->tail == UF_find(e->tail)); + reverse_edge(e); } - for (i = 1; i < GD_n_cluster(par); i++) - if (agcontains(GD_clust(par)[i], n)) - break; - if (i < GD_n_cluster(par)) - agdelete(g, n); - ND_clust(n) = NULL; } for (n = agfstnode(g); n; n = agnxtnode(g, n)) { - for (e = agfstout(g->root, n); e; e = agnxtout(g->root, e)) { - if (agcontains(g, e->head)) - aginsert(g, e); - } + if (n != UF_find(n)) + continue; + if ((ND_out(n).size == 0) && GD_maxset(g) && (n != GD_maxset(g))) + virtual_edge(n, GD_maxset(g), NULL)->u.minlen = sinklen; + if ((ND_in(n).size == 0) && GD_minset(g) && (n != GD_minset(g))) + virtual_edge(GD_minset(g), n, NULL)->u.minlen = srclen; } } -void cluster_leader(graph_t * clust) +/* Run the network simplex algorithm on each component. */ +static void +rank1(graph_t * g) { - node_t *leader, *n; - int maxrank = 0; - - /* find number of ranks and select a leader */ - leader = NULL; - for (n = GD_nlist(clust); n; n = ND_next(n)) { - if ((ND_rank(n) == 0) && (ND_node_type(n) == NORMAL)) - leader = n; - if (maxrank < ND_rank(n)) - maxrank = ND_rank(n); - } - assert(leader != NULL); - GD_leader(clust) = leader; + int maxiter = MAXINT; + int c; + char *s; - for (n = agfstnode(clust); n; n = agnxtnode(clust, n)) { - assert((ND_UF_size(n) <= 1) || (n == leader)); - UF_union(n, leader); - ND_ranktype(n) = CLUSTER; + if ((s = agget(g, "nslimit1"))) + maxiter = atof(s) * agnnodes(g); + for (c = 0; c < GD_comp(g).size; c++) { + GD_nlist(g) = GD_comp(g).list[c]; + rank(g, (GD_n_cluster(g) == 0 ? 1 : 0), maxiter); /* TB balance */ } } @@ -391,96 +424,83 @@ void expand_ranksets(graph_t * g) } } -void renewlist(elist * L) +void dot_rank(graph_t * g) { - int i; - for (i = L->size; i >= 0; i--) - L->list[i] = NULL; - L->size = 0; + edgelabel_ranks(g); + collapse_sets(g); + /*collapse_leaves(g); */ + class1(g); + minmax_edges(g); + decompose(g, 0); + acyclic(g); + rank1(g); + expand_ranksets(g); + cleanup1(g); } -void cleanup1(graph_t * g) +int is_cluster(graph_t * g) { - node_t *n; - edge_t *e, *f; - int c; - - for (c = 0; c < GD_comp(g).size; c++) { - GD_nlist(g) = GD_comp(g).list[c]; - for (n = GD_nlist(g); n; n = ND_next(n)) { - renewlist(&ND_in(n)); - renewlist(&ND_out(n)); - ND_mark(n) = FALSE; - } - } - for (n = agfstnode(g); n; n = agnxtnode(g, n)) { - for (e = agfstout(g, n); e; e = agnxtout(g, e)) { - f = ED_to_virt(e); - if (f && (e == ED_to_orig(f))) { - /* Null out any other references to f to make sure we don't handle it - * a second time. For example, parallel multiedges share a virtual edge. - */ - edge_t *e1, *f1; - for (e1 = agfstout(g, n); e1; e1 = agnxtout(g, e1)) { - if (e != e1) { - f1 = ED_to_virt(e1); - if (f1 && (f == f1)) { - ED_to_virt(e1) = NULL; - } - } - } - free(f); - } - ED_to_virt(e) = NULL; - } - } - free(GD_comp(g).list); - GD_comp(g).list = NULL; - GD_comp(g).size = 0; + return (strncmp(g->name, "cluster", 7) == 0); } -void set_minmax(graph_t * g) +static node_t* +merge_leaves(graph_t * g, node_t * cur, node_t * new) { - int c; + node_t *rv; - GD_minrank(g) += GD_leader(g)->u.rank; - GD_maxrank(g) += GD_leader(g)->u.rank; - for (c = 1; c <= GD_n_cluster(g); c++) - set_minmax(GD_clust(g)[c]); + if (cur == NULL) + rv = new; + else { + rv = UF_union(cur, new); + ND_ht_i(rv) = MAX(ND_ht_i(cur), ND_ht_i(new)); + ND_lw_i(rv) = ND_lw_i(cur) + ND_lw_i(new) + GD_nodesep(g) / 2; + ND_rw_i(rv) = ND_rw_i(cur) + ND_rw_i(new) + GD_nodesep(g) / 2; + } + return rv; } -void scan_ranks(graph_t * g) +static void +potential_leaf(graph_t * g, edge_t * e, node_t * leaf) { - node_t *n, *leader = NULL; - GD_minrank(g) = MAXSHORT; - GD_maxrank(g) = -1; - for (n = agfstnode(g); n; n = agnxtnode(g, n)) { - if (GD_maxrank(g) < ND_rank(n)) - GD_maxrank(g) = ND_rank(n); - if (GD_minrank(g) > ND_rank(n)) - GD_minrank(g) = ND_rank(n); - if (leader == NULL) - leader = n; - else { - if (ND_rank(n) < ND_rank(leader)) - leader = n; - } - } - GD_leader(g) = leader; + node_t *par; + + if ((ED_tail_port(e).p.x) || (ED_head_port(e).p.x)) + return; + if ((ED_minlen(e) != 1) || (ND_order(e->tail) > 0)) + return; + par = ((leaf != e->head) ? e->head : e->tail); + ND_ranktype(leaf) = LEAFSET; + if (par == e->tail) + GD_outleaf(par) = merge_leaves(g, GD_outleaf(par), leaf); + else + GD_inleaf(par) = merge_leaves(g, GD_inleaf(par), leaf); } -void find_clusters(graph_t * g) +#ifdef OBSOLETE +static void +collapse_leaves(graph_t * g) { - graph_t *mg, *subg; - node_t *mn; - edge_t *me; + node_t *n; + edge_t *e; - mg = g->meta_node->graph; - for (me = agfstout(mg, g->meta_node); me; me = agnxtout(mg, me)) { - mn = me->head; - subg = agusergraph(mn); + for (n = agfstnode(g); n; n = agnxtnode(g, n)) { - if (GD_set_type(subg) == CLUSTER) - collapse_cluster(g, subg); + /* consider n as a potential leaf of some other node. */ + if ((ND_ranktype(n) != NOCMD) || (ND_order(n))) + continue; + if (agfstout(g, n) == NULL) { + if ((e = agfstin(g, n)) && (agnxtin(g, e) == NULL)) { + potential_leaf(g, e, n); + continue; + } + } + if (agfstin(g, n) == NULL) { + if ((e = agfstout(g, n)) && (agnxtout(g, e) == NULL)) { + potential_leaf(g, e, n); + continue; + } + } } } +#endif + diff --git a/lib/dotgen/sameport.c b/lib/dotgen/sameport.c index 79132bb17..86c55de28 100644 --- a/lib/dotgen/sameport.c +++ b/lib/dotgen/sameport.c @@ -152,7 +152,7 @@ static void sameport(node_t * u, elist * l, double arr_len) curve[3].x = ROUND(x2); curve[3].y = ROUND(y2); - shape_clip(u, curve, 0); + shape_clip(u, curve); x1 = curve[0].x - ND_coord_i(u).x; y1 = curve[0].y - ND_coord_i(u).y; } @@ -169,7 +169,7 @@ static void sameport(node_t * u, elist * l, double arr_len) prt.theta = 0; prt.side = 0; -#ifdef OLD +#ifdef OBSOLETE This code appears obsolete and wrong. First, we don't use arr_prt anymore, as we have previously ifdef'ed out the code below where it is used. In addition, it resets the rank height. But we've already @@ -208,7 +208,7 @@ nodes and maintaining equal separation when specified for (i = 0; i < l->size; i++) { e = l->list[i]; arrow_flags(e, &sflag, &eflag); -#ifndef OLD +#ifndef OBSOLETE for (; e; e = ED_to_virt(e)) { /* assign to all virt edges of e */ for (f = e; f; f = ED_edge_type(f) == VIRTUAL &&