static void cleanup1(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); /* 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. */ if (f && (e == ED_to_orig(f))) { edge_t *e1, *f1; #ifndef WITH_CGRAPH 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; } } } #else node_t *n1; for (n1 = agfstnode(g); n1; n1 = agnxtnode(g, n1)) { for (e1 = agfstout(g, n1); e1; e1 = agnxtout(g, e1)) { if (e != e1) { f1 = ED_to_virt(e1); if (f1 && (f == f1)) { ED_to_virt(e1) = NULL; } } } } free(f->base.data); #endif free(f); } ED_to_virt(e) = NULL; } } free(GD_comp(g).list); GD_comp(g).list = NULL; GD_comp(g).size = 0; }
void fast_node(graph_t *g, Agnode_t *n) { #ifdef DEBUG assert (find_fast_node(g,n) == NULL); #endif ND_next(n) = GD_nlist(g); if (ND_next(n)) ND_next(n)->u.prev = n; GD_nlist(g) = n; ND_prev(n) = NULL; assert (n != ND_next(n)); }
void fastgr(graph_t * g) { int i, j; node_t *n, *w; edge_t *e, *f; for (n = GD_nlist(g); n; n = ND_next(n)) { fprintf(stderr, "%s %d: (", NAME(n), ND_rank(n)); for (i = 0; e = ND_out(n).list[i]; i++) { fprintf(stderr, " %s:%d", NAME(e->head), ED_count(e)); w = e->head; if (g == g->root) { for (j = 0; f = ND_in(w).list[j]; j++) if (e == f) break; assert(f != NULL); } } fprintf(stderr, " ) ("); for (i = 0; e = ND_in(n).list[i]; i++) { fprintf(stderr, " %s:%d", NAME(e->tail), ED_count(e)); w = e->tail; if (g == g->root) { for (j = 0; f = ND_out(w).list[j]; j++) if (e == f) break; assert(f != NULL); } } fprintf(stderr, " )\n"); } }
node_t* find_fast_node(graph_t *g, node_t *n) { node_t *v; for (v = GD_nlist(g); v; v = ND_next(v)) if (v == n) break; return v; }
void delete_fast_node(graph_t *g, node_t *n) { assert(find_fast_node(g,n)); if (ND_next(n)) ND_next(n)->u.prev = ND_prev(n); if (ND_prev(n)) ND_prev(n)->u.next = ND_next(n); else GD_nlist(g) = ND_next(n); }
int fastn (graph_t * g) { node_t* u; int cnt = 0; for (u = GD_nlist(g); u; u = ND_next(u)) cnt++; return cnt; }
int is_fast_node(graph_t *g, node_t *v) { node_t *n; for (n = GD_nlist(g); n; n = ND_next(n)) if (v == n) return TRUE; return FALSE; }
/* closeGraphs: * Clean up graphs made for setting column and row widths. */ static void closeGraphs(graph_t * rowg, graph_t * colg) { node_t *n; for (n = GD_nlist(colg); n; n = ND_next(n)) { free_list(ND_in(n)); free_list(ND_out(n)); } agclose(rowg); agclose(colg); }
/* setSizes: * Use rankings to determine cell dimensions. The rank values * give the coordinate, so to get the width/height, we have * to subtract the previous value. */ void setSizes(htmltbl_t * tbl, graph_t * rowg, graph_t * colg) { int i; node_t *n; int prev; prev = 0; n = GD_nlist(rowg); for (i = 0, n = ND_next(n); n; i++, n = ND_next(n)) { tbl->heights[i] = ND_rank(n) - prev; prev = ND_rank(n); } prev = 0; n = GD_nlist(colg); for (i = 0, n = ND_next(n); n; i++, n = ND_next(n)) { tbl->widths[i] = ND_rank(n) - prev; prev = ND_rank(n); } }
/* 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 */ } }
/* delete virtual nodes of a cluster, and install real nodes or sub-clusters */ void expand_cluster(graph_t * subg) { /* build internal structure of the cluster */ class2(subg); GD_comp(subg).size = 1; GD_comp(subg).list[0] = GD_nlist(subg); allocate_ranks(subg); build_ranks(subg, 0); merge_ranks(subg); /* build external structure of the cluster */ interclexp(subg); remove_rankleaders(subg); }
/* delete the layout (but retain the underlying graph) */ void dot_cleanup(graph_t * g) { node_t *n; edge_t *e; free_virtual_node_list(GD_nlist(g)); for (n = agfstnode(g); n; n = agnxtnode(g, n)) { for (e = agfstout(g, n); e; e = agnxtout(g, e)) { gv_cleanup_edge(e); } dot_cleanup_node(n); } dot_cleanup_graph(g); }
static void checkChain(graph_t * g) { node_t *t; node_t *h; edge_t *e; t = GD_nlist(g); for (h = ND_next(t); h; h = ND_next(h)) { if (!agfindedge(g, t, h)) { e = agedge(g, t, h); ED_minlen(e) = 0; elist_append(e, ND_out(t)); elist_append(e, ND_in(h)); } t = h; } }
static void checkChain(graph_t * g) { node_t *t; node_t *h; edge_t *e; t = GD_nlist(g); for (h = ND_next(t); h; h = ND_next(h)) { if (!agfindedge(g, t, h)) { #ifdef WITH_CGRAPH e = agedge(g, t, h, NULL, 1); agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), TRUE); #else e = agedge(g, t, h); #endif ED_minlen(e) = 0; elist_append(e, ND_out(t)); elist_append(e, ND_in(h)); } t = h; } }
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(clust); n; n = agnxtnode(clust, n)) { assert((ND_UF_size(n) <= 1) || (n == leader)); UF_union(n, leader); ND_ranktype(n) = CLUSTER; } }
static void validate(graph_t * g) { node_t *n; edge_t *e; int i, cnt; cnt = 0; for (n = GD_nlist(g);n; n = ND_next(n)) { assert(outdegree(g,n) == ND_out(n).size); for (i = 0; (e = ND_out(n).list[i]); i++) { assert(agtail(e) == n); assert( e == agfindedge(g, n, aghead(e))); } assert(indegree(g,n) == ND_in(n).size); for (i = 0; (e = ND_in(n).list[i]); i++) { assert(aghead(e) == n); assert( e == agfindedge(g, agtail(e), n)); } cnt++; } assert (agnnodes(g) == cnt); }
/* mkNConstraintG: * Similar to mkConstraintG, except it doesn't enforce orthogonal * ordering. If there is overlap, as defined by intersect, the * nodes will kept/pushed apart in the current order. If not, no * constraint is enforced. If a constraint edge is added, and it * corresponds to a real edge, we increase the weight in an attempt * to keep the resulting shift short. */ static graph_t *mkNConstraintG(graph_t * g, Dt_t * list, intersectfn intersect, distfn dist) { nitem *p; nitem *nxp; node_t *n; edge_t *e; node_t *lastn = NULL; #ifndef WITH_CGRAPH graph_t *cg = agopen("cg", AGDIGRAPHSTRICT); #else graph_t *cg = agopen("cg", Agstrictdirected, NIL(Agdisc_t *)); agbindrec(cg, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE); // graph custom data #endif for (p = (nitem *) dtflatten(list); p; p = (nitem *) dtlink(list, (Dtlink_t *) p)) { #ifndef WITH_CGRAPH n = agnode(cg, agnameof(p->np)); /* FIX */ #else n = agnode(cg, agnameof(p->np), 1); /* FIX */ agbindrec(n, "Agnodeinfo_t", sizeof(Agnodeinfo_t), TRUE); //node custom data #endif ND_alg(n) = p; p->cnode = n; alloc_elist(0, ND_in(n)); alloc_elist(0, ND_out(n)); if (lastn) { ND_next(lastn) = n; lastn = n; } else { lastn = GD_nlist(cg) = n; } } for (p = (nitem *) dtflatten(list); p; p = (nitem *) dtlink(list, (Dtlink_t *) p)) { for (nxp = (nitem *) dtlink(link, (Dtlink_t *) p); nxp; nxp = (nitem *) dtlink(list, (Dtlink_t *) nxp)) { e = NULL; if (intersect(p, nxp)) { double delta = dist(&p->bb, &nxp->bb); #ifndef WITH_CGRAPH e = agedge(cg, p->cnode, nxp->cnode); #else e = agedge(cg, p->cnode, nxp->cnode, NULL, 1); agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), TRUE); // edge custom data #endif assert (delta <= 0xFFFF); ED_minlen(e) = delta; ED_weight(e) = 1; } if (e && agfindedge(g,p->np, nxp->np)) { ED_weight(e) = 100; } #if 0 if (agfindedge(g,p->np, nxp->np)) { if (e == NULL) e = agedge(cg, p->cnode, nxp->cnode); ED_weight(e) = 100; /* If minlen < SCALE, the nodes can't conflict or there's * an overlap but it will be removed in the orthogonal pass. * So we just keep the node's basically where they are. */ if (SCALE > ED_minlen(e)) ED_minlen(e) = SCALE; } #endif } } for (p = (nitem *) dtflatten(list); p; p = (nitem *) dtlink(list, (Dtlink_t *) p)) { n = p->cnode; for (e = agfstout(cg,n); e; e = agnxtout(cg,e)) { elist_append(e, ND_out(n)); elist_append(e, ND_in(aghead(e))); } } /* We could remove redundant constraints here. However, the cost of doing * this may be a good deal more than the time saved in network simplex. * Also, if the graph is changed, the ND_in and ND_out data has to be * updated. */ return cg; }
/* mkConstraintG: */ static graph_t *mkConstraintG(graph_t * g, Dt_t * list, intersectfn intersect, distfn dist) { nitem *p; nitem *nxt = NULL; nitem *nxp; graph_t *vg; node_t *prev = NULL; node_t *root = NULL; node_t *n = NULL; edge_t *e; int lcnt, cnt; int oldval = -INT_MAX; #ifdef OLD double root_val; #endif node_t *lastn = NULL; #ifndef WITH_CGRAPH graph_t *cg = agopen("cg", AGDIGRAPHSTRICT); #else graph_t *cg = agopen("cg", Agstrictdirected, NIL(Agdisc_t *)); agbindrec(cg, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE); // graph custom data #endif /* count distinct nodes */ cnt = 0; for (p = (nitem *) dtflatten(list); p; p = (nitem *) dtlink(list, (Dtlink_t *) p)) { if (oldval != p->val) { oldval = p->val; cnt++; } } /* construct basic chain to enforce left to right order */ oldval = -INT_MAX; lcnt = 0; for (p = (nitem *) dtflatten(list); p; p = (nitem *) dtlink(list, (Dtlink_t *) p)) { if (oldval != p->val) { oldval = p->val; /* n = newNode (cg); */ #ifndef WITH_CGRAPH n = agnode(cg, agnameof(p->np)); /* FIX */ #else n = agnode(cg, agnameof(p->np), 1); /* FIX */ agbindrec(n, "Agnodeinfo_t", sizeof(Agnodeinfo_t), TRUE); //node custom data #endif ND_alg(n) = p; if (root) { ND_next(lastn) = n; lastn = n; } else { root = n; #ifdef OLD root_val = p->val; #endif lastn = GD_nlist(cg) = n; } alloc_elist(lcnt, ND_in(n)); if (prev) { if (prev == root) alloc_elist(2 * (cnt - 1), ND_out(prev)); else alloc_elist(cnt - lcnt - 1, ND_out(prev)); #ifndef WITH_CGRAPH e = agedge(cg, prev, n); #else e = agedge(cg, prev, n, NULL, 1); agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), TRUE); // edge custom data #endif ED_minlen(e) = SCALE; ED_weight(e) = 1; elist_append(e, ND_out(prev)); elist_append(e, ND_in(n)); } lcnt++; prev = n; } p->cnode = n; } alloc_elist(0, ND_out(prev)); /* add immediate right neighbor constraints * Construct visibility graph, then perform transitive reduction. * Remaining outedges are immediate right neighbors. * FIX: Incremental algorithm to construct trans. reduction? */ #ifndef WITH_CGRAPH vg = agopen("vg", AGDIGRAPHSTRICT); #else vg = agopen("vg", Agstrictdirected, NIL(Agdisc_t *)); #endif for (p = (nitem *) dtflatten(list); p; p = (nitem *) dtlink(list, (Dtlink_t *) p)) { #ifndef WITH_CGRAPH n = agnode(vg, agnameof(p->np)); /* FIX */ #else n = agnode(vg, agnameof(p->np), 1); /* FIX */ agbindrec(n, "Agnodeinfo_t", sizeof(Agnodeinfo_t), TRUE); //node custom data #endif p->vnode = n; ND_alg(n) = p; } oldval = -INT_MAX; for (p = (nitem *) dtflatten(list); p; p = (nitem *) dtlink(list, (Dtlink_t *) p)) { if (oldval != p->val) { /* new pos: reset nxt */ oldval = p->val; for (nxt = (nitem *) dtlink(link, (Dtlink_t *) p); nxt; nxt = (nitem *) dtlink(list, (Dtlink_t *) nxt)) { if (nxt->val != oldval) break; } if (!nxt) break; } for (nxp = nxt; nxp; nxp = (nitem *) dtlink(list, (Dtlink_t *) nxp)) { if (intersect(p, nxp)) #ifndef WITH_CGRAPH agedge(vg, p->vnode, nxp->vnode); #else agedge(vg, p->vnode, nxp->vnode, NULL, 1); #endif } } /* Remove redundant constraints here. However, the cost of doing this * may be a good deal more than the time saved in network simplex. Also, * if the graph is changed, the ND_in and ND_out data has to be updated. */ mapGraphs(vg, cg, dist); agclose(vg); /* add dummy constraints for absolute values and initial positions */ #ifdef OLD for (n = agfstnode(cg); n; n = agnxtnode(cg, n)) { node_t *vn; /* slack node for absolute value */ node_t *an; /* node representing original position */ p = (nitem *) ND_alg(n); if ((n == root) || (!p)) continue; vn = newNode(cg); ND_next(lastn) = vn; lastn = vn; alloc_elist(0, ND_out(vn)); alloc_elist(2, ND_in(vn)); an = newNode(cg); ND_next(lastn) = an; lastn = an; alloc_elist(1, ND_in(an)); alloc_elist(1, ND_out(an)); #ifndef WITH_CGRAPH e = agedge(cg, root, an); #else e = agedge(cg, root, an, 1); #endif ED_minlen(e) = p->val - root_val; elist_append(e, ND_out(root)); elist_append(e, ND_in(an)); #ifndef WITH_CGRAPH e = agedge(cg, an, vn); #else e = agedge(cg, an, vn, 1); #endif elist_append(e, ND_out(an)); elist_append(e, ND_in(vn)); #ifndef WITH_CGRAPH e = agedge(cg, n, vn); #else e = agedge(cg, n, vn, 1); #endif elist_append(e, ND_out(n)); elist_append(e, ND_in(vn)); } #endif /* OLD */ return cg; }
/* makeGraphs: * Generate dags modeling the row and column constraints. * If the table has cc columns, we create the graph * 0 -> 1 -> 2 -> ... -> cc * and if a cell starts in column c with span cspan, with * width w, we add the edge c -> c+cspan [minlen = w]. * * We might simplify the graph by removing multiedges, * using the max minlen, but will affect the balancing? */ void makeGraphs(htmltbl_t * tbl, graph_t * rowg, graph_t * colg) { htmlcell_t *cp; htmlcell_t **cells; node_t *t; node_t *lastn; node_t *h; edge_t *e; int i; int* minc; int* minr; lastn = NULL; for (i = 0; i <= tbl->cc; i++) { t = agnode(colg, nToName(i)); alloc_elist(tbl->rc, ND_in(t)); alloc_elist(tbl->rc, ND_out(t)); if (lastn) { ND_next(lastn) = t; lastn = t; } else { lastn = GD_nlist(colg) = t; } } lastn = NULL; for (i = 0; i <= tbl->rc; i++) { t = agnode(rowg, nToName(i)); alloc_elist(tbl->cc, ND_in(t)); alloc_elist(tbl->cc, ND_out(t)); if (lastn) { ND_next(lastn) = t; lastn = t; } else { lastn = GD_nlist(rowg) = t; } } minr = N_NEW(tbl->rc, int); minc = N_NEW(tbl->cc, int); for (cells = tbl->u.n.cells; *cells; cells++) { int x, y, c, r; cp = *cells; x = (cp->data.box.UR.x + (cp->cspan-1))/cp->cspan; for (c = 0; c < cp->cspan; c++) minc[cp->col + c] = MAX(minc[cp->col + c],x); y = (cp->data.box.UR.y + (cp->rspan-1))/cp->rspan; for (r = 0; r < cp->rspan; r++) minr[cp->row + r] = MAX(minr[cp->row + r],y); } for (cells = tbl->u.n.cells; *cells; cells++) { int x, y, c, r; cp = *cells; t = agfindnode(colg, nToName(cp->col)); h = agfindnode(colg, nToName(cp->col + cp->cspan)); e = agedge(colg, t, h); x = 0; for (c = 0; c < cp->cspan; c++) x += minc[cp->col + c]; ED_minlen(e) = x; /* ED_minlen(e) = cp->data.box.UR.x; */ #ifdef DEBUG fprintf(stderr, "col edge %s -> %s %d\n", t->name, h->name, ED_minlen(e)); #endif elist_append(e, ND_out(t)); elist_append(e, ND_in(h)); t = agfindnode(rowg, nToName(cp->row)); h = agfindnode(rowg, nToName(cp->row + cp->rspan)); e = agedge(rowg, t, h); y = 0; for (r = 0; r < cp->rspan; r++) y += minr[cp->row + r]; ED_minlen(e) = y; /* ED_minlen(e) = cp->data.box.UR.y; */ #ifdef DEBUG fprintf(stderr, "row edge %s -> %s %d\n", t->name, h->name, ED_minlen(e)); #endif elist_append(e, ND_out(t)); elist_append(e, ND_in(h)); } /* Make sure that 0 <= 1 <= 2 ...k. This implies graph connected. */ checkChain(colg); checkChain(rowg); free (minc); free (minr); }