/*
This software may only be used by you under license from AT&T Corp.
("AT&T"). A copy of AT&T's Source Code Agreement is available at
AT&T's Internet website having the URL:
<http://www.research.att.com/sw/tools/graphviz/license/source.html>
If you received this software without first entering into a license
with AT&T, you have an infringing copy of this software and cannot use
it without violating AT&T's intellectual property rights.
*/
#pragma prototyped
/* classify edges for mincross/nodepos/splines, using given ranks */
#include "dot.h"
void class2(graph_t* g)
{
int c;
node_t *n,*t,*h;
edge_t *e,*prev,*opp;
GD_nlist(g) = NULL;
GD_n_nodes(g) = 0; /* new */
mark_clusters(g);
for (c = 1; c <= GD_n_cluster(g); c++)
build_skeleton(g,GD_clust(g)[c]);
for (n = agfstnode(g); n; n = agnxtnode(g,n))
for (e = agfstout(g,n); e; e = agnxtout(g,e)) {
if (ND_weight_class(e->head) <= 2) ND_weight_class(e->head)++;
if (ND_weight_class(e->tail) <= 2) ND_weight_class(e->tail)++;
}
for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
if ((ND_clust(n) == NULL) && (n == UF_find(n))) {fast_node(g,n); GD_n_nodes(g)++;}
prev = NULL;
for (e = agfstout(g,n); e; e = agnxtout(g,e)) {
/* already processed */
if (ED_to_virt(e)) continue;
/* edges involving sub-clusters of g */
if (is_cluster_edge(e)) {
/* following is new cluster multi-edge code */
if (mergeable(prev,e)) {
if (ED_to_virt(prev)) {
merge_chain(g,e,ED_to_virt(prev),FALSE);
other_edge(e);
}
else if (ND_rank(e->tail) == ND_rank(e->head)) {
merge_oneway(e,prev);
other_edge(e);
}
/* else is an intra-cluster edge */
continue;
}
interclrep(g,e);
prev = e;
continue;
}
/* merge multi-edges */
if (prev && (e->tail == prev->tail) && (e->head == prev->head)) {
if (ND_rank(e->tail) == ND_rank(e->head)) {
merge_oneway(e,prev);
other_edge(e);
continue;
}
if ((ED_label(e) == NULL) && (ED_label(prev) == NULL) && ports_eq(e,prev)) {
if (Concentrate)
ED_edge_type(e) = IGNORED;
else{
merge_chain(g,e,ED_to_virt(prev),TRUE);
other_edge(e);
}
continue;
}
/* parallel edges with different labels fall through here */
}
/* self edges */
if (e->tail == e->head) {
other_edge(e);
prev = e;
continue;
}
t = UF_find(e->tail);
h = UF_find(e->head);
/* non-leader leaf nodes */
if ((e->tail != t) || (e->head != h)) {
/* ### need to merge stuff */
continue;
}
/* flat edges */
if (ND_rank(e->tail) == ND_rank(e->head)) {
flat_edge(g,e);
prev = e;
continue;
}
/* forward edges */
if (ND_rank(e->head) > ND_rank(e->tail)) {
make_chain(g,e->tail,e->head,e);
prev = e;
continue;
}
/* backward edges */
else {
/*other_edge(e);*/
/* avoid when opp==e in undirected graph */
if ((opp = agfindedge(g,e->head,e->tail))&&(opp!=e)) {
/* shadows a forward edge */
if (ED_to_virt(opp) == NULL)
make_chain(g,opp->tail,opp->head,opp);
if ((ED_label(e) == NULL) && (ED_label(opp) == NULL) && ports_eq(e,opp)) {
if (Concentrate) {
ED_edge_type(e) = IGNORED;
ED_conc_opp_flag(opp) = TRUE;
}
else{ /* see above. this is getting out of hand */
other_edge(e);
merge_chain(g,e,ED_to_virt(opp),TRUE);
}
continue;
}
}
make_chain(g,e->head,e->tail,e);
prev = e;
}
}
}
/* since decompose() is not called on subgraphs */
if (g != g->root) {
GD_comp(g).list = ALLOC(1,GD_comp(g).list,node_t*);
GD_comp(g).list[0] = GD_nlist(g);
}
}
node_t *
label_vnode(graph_t* g, edge_t* orig)
{
node_t *v;
pointf dimen;
dimen = ED_label(orig)->dimen;
v = virtual_node(g);
ND_label(v) = ED_label(orig);
ND_lw_i(v) = GD_nodesep(v->graph);
if (!ED_label_ontop(orig)) {
if (GD_left_to_right(g)) {
ND_ht_i(v) = POINTS(dimen.x); ND_rw_i(v) = POINTS(dimen.y);
}
else {
ND_ht_i(v) = POINTS(dimen.y); ND_rw_i(v) = POINTS(dimen.x);
}
}
return v;
}
node_t *
plain_vnode(graph_t* g, edge_t* orig)
{
node_t *v;
orig = orig;
v = virtual_node(g);
incr_width(g,v);
return v;
}
void incr_width(graph_t* g, node_t* v)
{
int width = GD_nodesep(g) / 2;
ND_lw_i(v) += width;
ND_rw_i(v) += width;
}
void make_chain(graph_t *g, node_t *from, node_t *to, edge_t *orig)
{
int r,label_rank;
node_t *u,*v;
edge_t *e;
u = from;
if (ED_label(orig)) label_rank = (ND_rank(from) + ND_rank(to)) / 2;
else label_rank = -1;
assert(ED_to_virt(orig) == NULL);
for (r = ND_rank(from) + 1; r <= ND_rank(to); r++) {
if (r < ND_rank(to)) {
if (r == label_rank) v = label_vnode(g,orig);
else v = plain_vnode(g,orig);
ND_rank(v) = r;
}
else v = to;
e = virtual_edge(u,v,orig);
virtual_weight(e);
u = v;
}
assert(ED_to_virt(orig) != NULL);
}
void merge_chain(graph_t *g, edge_t *e, edge_t *f, int flag)
{
edge_t *rep;
int lastrank = MAX(ND_rank(e->tail),ND_rank(e->head));
assert(ED_to_virt(e) == NULL);
ED_to_virt(e) = f;
rep = f;
do {
/* interclust multi-edges are not counted now */
if (flag) ED_count(rep) += ED_count(e);
ED_xpenalty(rep) += ED_xpenalty(e);
ED_weight(rep) += ED_weight(e);
if (ND_rank(rep->head) == lastrank) break;
incr_width(g,rep->head);
rep = ND_out(rep->head).list[0];
} while (rep);
}
node_t *
leader_of(graph_t* g, node_t* v)
{
graph_t *clust;
node_t *rv;
if (ND_ranktype(v) != CLUSTER) {
/*assert(v == UF_find(v)); could be leaf, so comment out */
rv = UF_find(v);
}
else {
clust = ND_clust(v);
rv = GD_rankleader(clust)[ND_rank(v)];
}
return rv;
}
void interclrep(graph_t* g, edge_t* e)
{
node_t *t,*h;
edge_t *ve;
t = leader_of(g,e->tail);
h = leader_of(g,e->head);
if (ND_rank(t) > ND_rank(h)) {node_t *t0 = t; t = h; h = t0;}
if (ND_clust(t) != ND_clust(h)) {
if ((ve = find_fast_edge(t,h))) {
merge_chain(g,e,ve,TRUE);
return;
}
if (ND_rank(t) == ND_rank(h)) return;
make_chain(g,t,h,e);
/* mark as cluster edge */
for (ve = ED_to_virt(e); ve && (ND_rank(ve->head) <= ND_rank(h));
ve = ND_out(ve->head).list[0]) ED_edge_type(ve) = CLUSTER_EDGE;
}
/* else ignore intra-cluster edges at this point */
}
int is_cluster_edge(edge_t* e)
{
return ((ND_ranktype(e->tail)==CLUSTER) || (ND_ranktype(e->head)==CLUSTER));
}
int mergeable(edge_t *e, edge_t *f)
{
if (e && f && (e->tail == f->tail) && (e->head == f->head) &&
(ED_label(e) == ED_label(f)) && ports_eq(e,f))
return TRUE;
return FALSE;
}
|