static void remove_rankleaders(graph_t * g) { int r; node_t *v; edge_t *e; for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { v = GD_rankleader(g)[r]; /* remove the entire chain */ while ((e = ND_out(v).list[0])) delete_fast_edge(e); while ((e = ND_in(v).list[0])) delete_fast_edge(e); delete_fast_node(agroot(g), v); GD_rankleader(g)[r] = NULL; } }
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; 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; }
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 int minmax_edges2(graph_t * g, point slen) { node_t *n; edge_t *e = 0; if ((GD_maxset(g)) || (GD_minset(g))) { 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))) { e = virtual_edge(n, GD_maxset(g), NULL); ED_minlen(e) = slen.y; } if ((ND_in(n).size == 0) && GD_minset(g) && (n != GD_minset(g))) { e = virtual_edge(GD_minset(g), n, NULL); ED_minlen(e) = slen.x; } } } return (e != 0); }
static bool upcandidate(node_t * v) { return ((ND_node_type(v) == VIRTUAL) && (ND_out(v).size == 1) && (ND_in(v).size == 1) && (ND_label(v) == NULL)); }
edge_t *find_fast_edge(node_t * u, node_t * v) { return ffe(u, ND_out(u), v, ND_in(v)); }
/* disconnects e from graph */ void delete_fast_edge(edge_t * e) { assert(e != NULL); zapinlist(&(ND_out(e->tail)), e); zapinlist(&(ND_in(e->head)), e); }
/* 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); }
/* 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; }
/* 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; }