/* sets ports that represent connections to subclusters */ static void subclustports(Agraph_t *ug) { Agraph_t *model, *clustmodel; Agnode_t *x; Agedge_t *e; vpath_t *p; Dict_t *d; double frac; /* walk all the paths */ model = GD_model(ug); d = repdict(model); for (p = dtfirst(d); p; p = dtnext(d,p)) { if ((ND_type(p->v[p->low])) == NODETYPE_CNODE) { x = p->v[p->low]; clustmodel = GD_model(ND_cluster(x)); frac = (ND_order(x) + 1) / GD_rank(clustmodel)[ND_rank(x)].n; e = p->e[p->low]; ED_tailport(e).p.x = 2 * PORTCLAMP * (frac - 0.5) + p->tailport; } if ((ND_type(p->v[p->high])) == NODETYPE_CNODE) { x = p->v[p->high]; clustmodel = GD_model(ND_cluster(x)); frac = (ND_order(x) + 1) / GD_rank(clustmodel)[ND_rank(x)].n; e = p->e[p->high-1]; ED_headport(e).p.x = 2 * PORTCLAMP * (frac - 0.5) + p->headport; } } }
/* determine canonical order of n0, n1 */ static void getlowhigh(Agnode_t **n0, Agnode_t **n1) { Agnode_t *temp; int d; d = ND_rank(*n0) - ND_rank(*n1); if ((d < 0) || ((d == 0) && ((*n0)->id > (*n1)->id))) {temp = *n0; *n0 = *n1; *n1 = temp;} }
static void infuse(graph_t * g, node_t * n) { node_t *lead; lead = GD_rankleader(g)[ND_rank(n)]; if ((lead == NULL) || (ND_order(lead) > ND_order(n))) GD_rankleader(g)[ND_rank(n)] = n; }
static void set_minmax(graph_t * g) { int c; GD_minrank(g) += ND_rank(GD_leader(g)); GD_maxrank(g) += ND_rank(GD_leader(g)); for (c = 1; c <= GD_n_cluster(g); c++) set_minmax(GD_clust(g)[c]); }
static void rebuild_vlists(graph_t * g) { int c, i, r, maxi; node_t *n, *lead; edge_t *e, *rep; for (r = GD_minrank(g); r <= GD_maxrank(g); r++) GD_rankleader(g)[r] = NULL; for (n = agfstnode(g); n; n = agnxtnode(g, n)) { infuse(g, n); for (e = agfstout(g, n); e; e = agnxtout(g, e)) { for (rep = e; ED_to_virt(rep); rep = ED_to_virt(rep)); while (ND_rank(rep->head) < ND_rank(e->head)) { infuse(g, rep->head); rep = ND_out(rep->head).list[0]; } } } for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { lead = GD_rankleader(g)[r]; if (ND_rank(g->root)[r].v[ND_order(lead)] != lead) abort(); GD_rank(g)[r].v = ND_rank(g->root)[r].v + GD_rankleader(g)[r]->u.order; maxi = -1; for (i = 0; i < GD_rank(g)[r].n; i++) { if ((n = GD_rank(g)[r].v[i]) == NULL) break; if (ND_node_type(n) == NORMAL) { if (agcontains(g, n)) maxi = i; else break; } else { edge_t *e; for (e = ND_in(n).list[0]; e && ED_to_orig(e); e = ED_to_orig(e)); if (e && (agcontains(g, e->tail)) && agcontains(g, e->head)) maxi = i; } } if (maxi == -1) agerr(AGWARN, "degenerate concentrated rank %s,%d\n", g->name, r); GD_rank(g)[r].n = maxi + 1; } for (c = 1; c <= GD_n_cluster(g); c++) rebuild_vlists(GD_clust(g)[c]); }
/* a given edge can have several other edges (forward or backward) between the same endpoints. here, we choose one of these to be the canonical representative of those edges. */ static Agedge_t* canonical_edge(Agedge_t* e) { Agraph_t *g; Agedge_t *canon; g = e->tail->graph; if (ND_rank(e->head) > ND_rank(e->tail)) canon = agfindedge(g,e->tail,e->head); else { if } }
static void merge_ranks(graph_t * subg) { int i, d, r, pos, ipos; node_t *v; graph_t *root; root = agroot(subg); if (GD_minrank(subg) > 0) #ifndef WITH_CGRAPH ND_rank(root)[GD_minrank(subg) - 1].valid = FALSE; #else /* WITH_CGRAPH */ GD_rank(root)[GD_minrank(subg) - 1].valid = FALSE; #endif /* WITH_CGRAPH */ for (r = GD_minrank(subg); r <= GD_maxrank(subg); r++) { d = GD_rank(subg)[r].n; #ifndef WITH_CGRAPH ipos = pos = GD_rankleader(subg)[r]->u.order; #else /* WITH_CGRAPH */ ipos = pos = ND_order(GD_rankleader(subg)[r]); #endif /* WITH_CGRAPH */ make_slots(root, r, pos, d); for (i = 0; i < GD_rank(subg)[r].n; i++) { #ifndef WITH_CGRAPH v = ND_rank(root)[r].v[pos] = GD_rank(subg)[r].v[i]; #else /* WITH_CGRAPH */ v = GD_rank(root)[r].v[pos] = GD_rank(subg)[r].v[i]; #endif /* WITH_CGRAPH */ ND_order(v) = pos++; #ifndef WITH_CGRAPH v->graph = subg->root; #else /* WITH_CGRAPH */ /* real nodes automatically have v->root = root graph */ if (ND_node_type(v) == VIRTUAL) v->root = root; #endif /* WITH_CGRAPH */ delete_fast_node(subg, v); fast_node(agroot(subg), v); GD_n_nodes(agroot(subg))++; } #ifndef WITH_CGRAPH GD_rank(subg)[r].v = ND_rank(root)[r].v + ipos; ND_rank(root)[r].valid = FALSE; #else /* WITH_CGRAPH */ GD_rank(subg)[r].v = GD_rank(root)[r].v + ipos; GD_rank(root)[r].valid = FALSE; #endif /* WITH_CGRAPH */ } if (r < GD_maxrank(root)) GD_rank(root)[r].valid = FALSE; GD_expanded(subg) = TRUE; }
static bool samedir(edge_t * e, edge_t * f) { edge_t *e0, *f0; for (e0 = e; ED_edge_type(e0) != NORMAL; e0 = ED_to_orig(e0)); for (f0 = f; ED_edge_type(f0) != NORMAL; f0 = ED_to_orig(f0)); if (ED_conc_opp_flag(e0)) return FALSE; if (ED_conc_opp_flag(f0)) return FALSE; return ((ND_rank(f0->tail) - ND_rank(f0->head)) * (ND_rank(e0->tail) - ND_rank(e0->head)) > 0); }
/* constrainY: * See constrainX. */ static void constrainY(graph_t* g, nitem* nlist, int nnodes, intersectfn ifn, int ortho) { Dt_t *list = dtopen(&constr, Dtobag); nitem *p = nlist; graph_t *cg; int i; for (i = 0; i < nnodes; i++) { p->val = p->pos.y; dtinsert(list, p); p++; } if (ortho) cg = mkConstraintG(g, list, ifn, distY); else cg = mkNConstraintG(g, list, ifn, distY); rank(cg, 2, INT_MAX); #ifdef DEBUG { Agsym_t *mlsym = agedgeattr(cg, "minlen", ""); Agsym_t *rksym = agnodeattr(cg, "rank", ""); char buf[100]; node_t *n; edge_t *e; for (n = agfstnode(cg); n; n = agnxtnode(cg, n)) { sprintf(buf, "%d", ND_rank(n)); agxset(n, rksym->index, buf); for (e = agfstedge(cg, n); e; e = agnxtedge(cg, e, n)) { sprintf(buf, "%d", ED_minlen(e)); agxset(e, mlsym->index, buf); } } } #endif p = nlist; for (i = 0; i < nnodes; i++) { int newpos, oldpos, delta; oldpos = p->pos.y; newpos = ND_rank(p->cnode); delta = newpos - oldpos; p->pos.y = newpos; p->bb.LL.y += delta; p->bb.UR.y += delta; p++; } closeGraph(cg); dtclose(list); }
/* bind/construct representative of an external endpoint to a model graph */ static rep_t model_extnode(Agraph_t *model, Agnode_t *orig) { Agnode_t *v; rep_t rep; rep = association(model,orig); if (rep.type) return rep; /* assume endpoint is represented by one node, even if orig is multi-rank */ rep.p = v = agnode(model,orig->name); rep.type = EXTNODE; ND_rank(v) = ND_rank(orig); /* should be ND_rank(orig)+ranksize(orig)? */ associate(model,orig,rep); return rep; }
static int presort_cmpf(const void *arg0, const void *arg1) { Agnode_t *n0, *n1; Agraph_t *c0, *c1; n0 = *(Agnode_t**)arg0; n1 = *(Agnode_t**)arg1; c0 = ND_cluster(n0); c1 = ND_cluster(n1); if (c0 == c1) return 0; assert(ND_rank(n0) == ND_rank(n1)); n0 = GD_skel(c0)->v[ND_rank(n0)]; n1 = GD_skel(c1)->v[ND_rank(n1)]; return ND_order(n0) - ND_order(n1); }
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"); } }
/* constrainX: * Create the X constrains and solve. We use a linear objective function * (absolute values rather than squares), so we can reuse network simplex. * The constraints are encoded as a dag with edges having a minimum length. */ static void constrainX(graph_t* g, nitem* nlist, int nnodes, intersectfn ifn, int ortho) { Dt_t *list = dtopen(&constr, Dtobag); nitem *p = nlist; graph_t *cg; int i; for (i = 0; i < nnodes; i++) { p->val = p->pos.x; dtinsert(list, p); p++; } if (ortho) cg = mkConstraintG(g, list, ifn, distX); else cg = mkNConstraintG(g, list, ifn, distX); rank(cg, 2, INT_MAX); p = nlist; for (i = 0; i < nnodes; i++) { int newpos, oldpos, delta; oldpos = p->pos.x; newpos = ND_rank(p->cnode); delta = newpos - oldpos; p->pos.x = newpos; p->bb.LL.x += delta; p->bb.UR.x += delta; p++; } closeGraph(cg); dtclose(list); }
int nonconstraint_edge(edge_t * e) { char *constr; #ifndef WITH_CGRAPH if (E_constr && (constr = agxget(e, E_constr->index))) { #else /* WITH_CGRAPH */ if (E_constr && (constr = agxget(e, E_constr))) { #endif /* WITH_CGRAPH */ if (constr[0] && mapbool(constr) == FALSE) return TRUE; } return FALSE; } static void interclust1(graph_t * g, node_t * t, node_t * h, edge_t * e) { node_t *v, *t0, *h0; int offset, t_len, h_len, t_rank, h_rank; edge_t *rt, *rh; if (ND_clust(agtail(e))) t_rank = ND_rank(agtail(e)) - ND_rank(GD_leader(ND_clust(agtail(e)))); else t_rank = 0; if (ND_clust(aghead(e))) h_rank = ND_rank(aghead(e)) - ND_rank(GD_leader(ND_clust(aghead(e)))); else h_rank = 0; offset = ED_minlen(e) + t_rank - h_rank; if (offset > 0) { t_len = 0; h_len = offset; } else { t_len = -offset; h_len = 0; } v = virtual_node(g); ND_node_type(v) = SLACKNODE; t0 = UF_find(t); h0 = UF_find(h); rt = make_aux_edge(v, t0, t_len, CL_BACK * ED_weight(e)); rh = make_aux_edge(v, h0, h_len, ED_weight(e)); ED_to_orig(rt) = ED_to_orig(rh) = e; }
static void model_edge(Agraph_t *model, Agedge_t *orig) { Agedge_t *e; Agnode_t *low, *high, *u, *v; port_t lowport, highport; vpath_t *path; rep_t rep; int i; rep = association(model,orig); if (rep.type == 0) { low = orig->tail; high = orig->head; getlowhigh(&low,&high); u = association(model,low).p; assert(u); v = association(model,high).p; assert(v); path = newpath(model,u,ND_rank(low),v,ND_rank(high)); rep.type = PATH; rep.p = path; associate(model,orig,rep); } else path = rep.p; /* merge the attributes of orig */ for (i = path->low; i < path->high; i++) { e = path->e[i]; ED_xpenalty(e) += ED_xpenalty(orig); ED_weight(e) += ED_weight(orig); } /* deal with ports. note that ends could be swapped! */ if (ND_rank(orig->tail) <= ND_rank(orig->head)) { lowport = ED_tailport(orig); highport = ED_headport(orig); } else { highport = ED_tailport(orig); lowport = ED_headport(orig); } if (lowport.defined) path->avgtailport = ((path->weight * path->avgtailport) + ED_weight(orig) * lowport.p.x) / (path->weight + ED_weight(orig)); if (highport.defined) path->avgheadport = ((path->weight * path->avgheadport) + ED_weight(orig) * highport.p.x) / (path->weight + ED_weight(orig)); path->weight += ED_weight(orig); }
/* swaps two nodes in the same level */ static void exchange(Agraph_t *g, Agnode_t *u, Agnode_t *v) { rank_t *r; int ui,vi,rank; assert(ND_rank(u) == ND_rank(v)); rank = ND_rank(u); r = &GD_rank(g)[rank]; ui = ND_order(u); vi = ND_order(v); ND_order(v) = ui; ND_order(u) = vi; r->v[ND_order(u)] = u; r->v[ND_order(v)] = v; r->crossing_cache.valid = FALSE; r->changed = TRUE; r->candidate = TRUE; /* old dot had this. i have qualms. sn */ invalidate(g,rank); }
static void install(Agraph_t *g, Agnode_t *n) { int rank; rank_t *r; rank = ND_rank(n); r = &GD_rank(g)[rank]; r->v[r->n] = n; ND_order(n) = r->n++; }
static void countup(Agraph_t *g, rank_t *globr) { Agnode_t *n; Agedge_t *e; int r0, r1, low, high, i; for (n = agfstnode(g); n; n = agnxtnode(g,n)) { for (i = 0; i < ND_ranksize(n); i++) globr[ND_rank(n)+i].n += 1; for (e = agfstout(g,n); e; e = agnxtout(g,e)) { r0 = ND_rank(e->tail); r1 = ND_rank(e->head); low = MIN(r0,r1); high = MAX(r0,r1); for (i = low + 1; i < high; i++) globr[i].n += 1; } } }
static node_t* map_interclust_node(node_t * n) { node_t *rv; #ifndef WITH_CGRAPH if ((ND_clust(n) == NULL) || (ND_clust(n)->u.expanded)) #else /* WITH_CGRAPH */ if ((ND_clust(n) == NULL) || ( GD_expanded(ND_clust(n))) ) #endif /* WITH_CGRAPH */ rv = n; else #ifndef WITH_CGRAPH rv = ND_clust(n)->u.rankleader[ND_rank(n)]; #else /* WITH_CGRAPH */ rv = GD_rankleader(ND_clust(n))[ND_rank(n)]; #endif /* WITH_CGRAPH */ return rv; }
/* 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); } }
/* interpolate_zcoord: * Given 2 points in 3D p = (fst.x,fst.y,fstz) and q = (snd.x, snd.y, sndz), * and a point p1 in the xy plane lying on the line segment connecting * the projections of the p and q, find the z coordinate of p1 when it * is projected up onto the segment (p,q) in 3-space. * * Why the special case for ranks? Is the arithmetic really correct? */ static double interpolate_zcoord(GVJ_t *job, pointf p1, pointf fst, double fstz, pointf snd, double sndz) { obj_state_t *obj = job->obj; edge_t *e = obj->u.e; double len, d, rv; if (fstz == sndz) return fstz; if (ND_rank(e->tail) != ND_rank(e->head)) { if (snd.y == fst.y) rv = (fstz + sndz) / 2.0; else rv = fstz + (sndz - fstz) * (p1.y - fst.y) / (snd.y - fst.y); } else { len = DIST(fst, snd); d = DIST(p1, fst)/len; rv = fstz + d*(sndz - fstz); } return rv; }
/* bind/construct representative of an internal node in a model graph */ static rep_t model_intnode(Agraph_t *model, Agnode_t *orig) { int nr; rep_t rep; Agnode_t *v; rep = association(model,orig); if (rep.type) return rep; nr = ND_ranksize(orig); if (nr <= 1) { /* simple case */ v = rep.p = agnode(model,orig->name); rep.type = NODE; ND_rank(v) = ND_rank(orig); } else { /* multi-rank node case */ rep.p = newpath(model,NILnode,ND_rank(orig),NILnode,ND_rank(orig)+nr-1); rep.type = TALLNODE; } associate(model,orig,rep); return rep; }
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; } }
void setRanks (graph_t* g, attrsym_t* lsym) { node_t* n; char* s; char* ep; long v; for (n = agfstnode(g); n; n = agnxtnode(g,n)) { s = agxget (n, lsym->index); v = strtol (s, &ep, 10); if (ep == s) agerr(AGWARN, "no level attribute for node \"%s\"\n", n->name); ND_rank(n) = v; } }
static int left2right(Agraph_t *g, node_t *v, node_t *w) { int rv; #ifdef NOTDEF adjmatrix_t *M; 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)); } #else rv = FALSE; #endif return rv; }
/* mkMCGraph: * Clone original graph. We only need the nodes, edges and clusters. * Copy */ Agraph_t* mkMCGraph (Agraph_t* g) { Agnode_t* t; Agnode_t* newt; Agnode_t* newh; Agedge_t* e; Agedge_t* newe; Agraph_t* sg; edgepair_t* data; edgepair_t* ep; Agraph_t* newg = agopen (agnameof(g), g->desc, 0); Dt_t* emap = dtopen (&edgepair, Dtoset);; data = N_NEW(agnedges(g), edgepair_t); ep = data; for (t = agfstnode(g); t; t = agnxtnode(g, t)) { newt = mkMCNode (newg, STDNODE, agnameof(t)); assert(newt); MND_orig(newt) = t; MND_rank(newt) = ND_rank(t); } for (t = agfstnode(g); t; t = agnxtnode(g, t)) { newt = agnode (newg, agnameof(t), 0); for (e = agfstout(g, t); e; e = agnxtout(g, e)) { newh = agnode (newg, agnameof(aghead(e)), 0); assert(newh); newe = mkMCEdge (newg, newt, newh, agnameof (e), NORMAL, e); assert(newe); ep->key = e; ep->val = newe; dtinsert (emap, ep++); } } for (sg = agfstsubg(g); sg; sg = agnxtsubg(sg)) { cloneSubg(newg, sg, emap); } dtclose (emap); free (data); return newg; }
static void remove_from_rank (Agraph_t * g, Agnode_t* n) { Agnode_t* v = NULL; int j, rk = ND_rank(n); for (j = 0; j < GD_rank(g)[rk].n; j++) { v = GD_rank(g)[rk].v[j]; if (v == n) { for (j++; j < GD_rank(g)[rk].n; j++) { GD_rank(g)[rk].v[j-1] = GD_rank(g)[rk].v[j]; } GD_rank(g)[rk].n--; break; } } assert (v == n); /* if found */ }
static void attach_phase_attrs (Agraph_t * g, int maxphase) { Agsym_t* rk = agnodeattr(g,"rank",""); Agsym_t* order = agnodeattr(g,"order",""); Agnode_t* n; char buf[BUFSIZ]; for (n = agfstnode(g); n; n = agnxtnode(g,n)) { if (maxphase >= 1) { sprintf(buf, "%d", ND_rank(n)); ag_xset(n,rk,buf); } if (maxphase >= 2) { sprintf(buf, "%d", ND_order(n)); ag_xset(n,order,buf); } } }
static void restorebest(graph_t *g) { Agnode_t *n; int i; for (i = GD_minrank(g); i <= GD_maxrank(g); i++) GD_rank(g)[i].changed = FALSE; for (n = agfstnode(g); n; n = agnxtnode(g,n)) { if (ND_order(n) != ND_saveorder(n)) { invalidate(g,ND_rank(n)); GD_rank(g)[i].changed = TRUE; ND_order(n) = ND_saveorder(n); } } for (i = GD_minrank(g); i <= GD_maxrank(g); i++) { if (GD_rank(g)[i].changed) qsort(GD_rank(g)[i].v,GD_rank(g)[i].n,sizeof(Agnode_t*),ND_order_cmpf); } }
/* * Assigns ranks of non-leader nodes. * Expands same, min, max rank sets. * Leaf sets and clusters remain merged. * Sets minrank and maxrank appropriately. */ static void expand_ranksets(graph_t * g, aspect_t* asp) { int c; node_t *n, *leader; if ((n = agfstnode(g))) { GD_minrank(g) = MAXSHORT; GD_maxrank(g) = -1; while (n) { leader = UF_find(n); /* The following works because ND_rank(n) == 0 if n is not in a * cluster, and ND_rank(n) = the local rank offset if n is in * a cluster. */ if ((leader != n) && (!asp || (ND_rank(n) == 0))) ND_rank(n) += ND_rank(leader); 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 (ND_ranktype(n) && (ND_ranktype(n) != LEAFSET)) UF_singleton(n); n = agnxtnode(g, n); } if (g == dot_root(g)) { if (CL_type == LOCAL) { for (c = 1; c <= GD_n_cluster(g); c++) set_minmax(GD_clust(g)[c]); } else { find_clusters(g); } } } else { GD_minrank(g) = GD_maxrank(g) = 0; } }