123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- /*************************************************************************
- * Copyright (c) 2011 AT&T Intellectual Property
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors: Details at https://graphviz.org
- *************************************************************************/
- /* classify edges for mincross/nodepos/splines, using given ranks */
- #include <dotgen/dot.h>
- #include <stdbool.h>
- #include <stdlib.h>
- #include <util/alloc.h>
- static 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(v) = GD_nodesep(agroot(v));
- if (!ED_label_ontop(orig)) {
- if (GD_flip(agroot(g))) {
- ND_ht(v) = dimen.x;
- ND_rw(v) = dimen.y;
- } else {
- ND_ht(v) = dimen.y;
- ND_rw(v) = dimen.x;
- }
- }
- return v;
- }
- static void
- incr_width(graph_t * g, node_t * v)
- {
- int width = GD_nodesep(g) / 2;
- ND_lw(v) += width;
- ND_rw(v) += width;
- }
- static node_t *plain_vnode(graph_t *g) {
- node_t *v;
- v = virtual_node(g);
- incr_width(g, v);
- return v;
- }
- static node_t *leader_of(node_t * v) {
- graph_t *clust;
- node_t *rv;
- if (ND_ranktype(v) != CLUSTER) {
- rv = UF_find(v);
- } else {
- clust = ND_clust(v);
- rv = GD_rankleader(clust)[ND_rank(v)];
- }
- return rv;
- }
- /* make_chain:
- * Create chain of dummy nodes for edge orig.
- */
- static 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);
- ND_rank(v) = r;
- } else
- v = to;
- e = virtual_edge(u, v, orig);
- virtual_weight(e);
- u = v;
- }
- assert(ED_to_virt(orig) != NULL);
- }
- static void
- interclrep(graph_t * g, edge_t * e)
- {
- node_t *t, *h;
- edge_t *ve;
- t = leader_of(agtail(e));
- h = leader_of(aghead(e));
- 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(aghead(ve)) <= ND_rank(h);
- ve = ND_out(aghead(ve)).list[0])
- ED_edge_type(ve) = CLUSTER_EDGE;
- }
- /* else ignore intra-cluster edges at this point */
- }
- static bool is_cluster_edge(edge_t *e) {
- return ND_ranktype(agtail(e)) == CLUSTER || ND_ranktype(aghead(e)) == CLUSTER;
- }
- void merge_chain(graph_t *g, edge_t *e, edge_t *f, bool update_count) {
- edge_t *rep;
- int lastrank = MAX(ND_rank(agtail(e)), ND_rank(aghead(e)));
- assert(ED_to_virt(e) == NULL);
- ED_to_virt(e) = f;
- rep = f;
- do {
- /* interclust multi-edges are not counted now */
- if (update_count)
- ED_count(rep) += ED_count(e);
- ED_xpenalty(rep) += ED_xpenalty(e);
- ED_weight(rep) += ED_weight(e);
- if (ND_rank(aghead(rep)) == lastrank)
- break;
- incr_width(g, aghead(rep));
- rep = ND_out(aghead(rep)).list[0];
- } while (rep);
- }
- bool mergeable(edge_t *e, edge_t *f) {
- return e && f && agtail(e) == agtail(f) && aghead(e) == aghead(f) &&
- ED_label(e) == ED_label(f) && ports_eq(e, f);
- }
- void class2(graph_t * g)
- {
- int c;
- node_t *n, *t, *h;
- edge_t *e, *prev, *opp;
- GD_nlist(g) = NULL;
- 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(aghead(e)) <= 2)
- ND_weight_class(aghead(e))++;
- if (ND_weight_class(agtail(e)) <= 2)
- ND_weight_class(agtail(e))++;
- }
- for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
- if (ND_clust(n) == NULL && n == UF_find(n)) {
- fast_node(g, n);
- }
- prev = NULL;
- for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
- /* already processed */
- if (ED_to_virt(e)) {
- prev = 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(agtail(e)) == ND_rank(aghead(e))) {
- 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 && agtail(e) == agtail(prev) && aghead(e) == aghead(prev)) {
- if (ND_rank(agtail(e)) == ND_rank(aghead(e))) {
- 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 (agtail(e) == aghead(e)) {
- other_edge(e);
- prev = e;
- continue;
- }
- t = UF_find(agtail(e));
- h = UF_find(aghead(e));
- /* non-leader leaf nodes */
- if (agtail(e) != t || aghead(e) != h) {
- /* FIX need to merge stuff */
- continue;
- }
- /* flat edges */
- if (ND_rank(agtail(e)) == ND_rank(aghead(e))) {
- flat_edge(g, e);
- prev = e;
- continue;
- }
- /* forward edges */
- if (ND_rank(aghead(e)) > ND_rank(agtail(e))) {
- make_chain(g, agtail(e), aghead(e), e);
- prev = e;
- continue;
- }
- /* backward edges */
- else {
- /* avoid when opp==e in undirected graph */
- for (opp = agfstout(g, aghead(e)); opp != NULL; opp = agnxtout(g, opp)) {
- if (aghead(opp) != agtail(e) || aghead(opp) == aghead(e) ||
- ED_edge_type(opp) == IGNORED) {
- continue;
- }
- /* shadows a forward edge */
- if (ED_to_virt(opp) == NULL)
- make_chain(g, agtail(opp), aghead(opp), 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);
- }
- break;
- }
- }
- if (opp != NULL) {
- continue;
- }
- make_chain(g, aghead(e), agtail(e), e);
- prev = e;
- }
- }
- }
- /* since decompose() is not called on subgraphs */
- if (g != dot_root(g)) {
- free(GD_comp(g).list);
- GD_comp(g).list = gv_alloc(sizeof(node_t *));
- GD_comp(g).list[0] = GD_nlist(g);
- }
- }
|