123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666 |
- /*************************************************************************
- * 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
- *************************************************************************/
- #include <cgraph/list.h>
- #include <circogen/blockpath.h>
- #include <circogen/circular.h>
- #include <circogen/edgelist.h>
- #include <stddef.h>
- #include <stdbool.h>
- #include <util/agxbuf.h>
- #include <util/alloc.h>
- /* The code below lays out a single block on a circle.
- */
- /* We use the unused fields order and to_orig in cloned nodes and edges */
- #define ORIGE(e) (ED_to_orig(e))
- /* clone_graph:
- * Create two copies of the argument graph
- * One is a subgraph, the other is an actual copy since we will be
- * adding edges to it.
- *
- * @param state Context containing a counter to use for graph copy naming
- */
- static Agraph_t *clone_graph(Agraph_t *ing, Agraph_t **xg, circ_state *state) {
- Agraph_t *clone;
- Agraph_t *xclone;
- Agnode_t *n;
- Agnode_t *xn;
- Agnode_t *xh;
- Agedge_t *e;
- Agedge_t *xe;
- agxbuf gname = {0};
- agxbprint(&gname, "_clone_%d", state->graphCopyCount++);
- clone = agsubg(ing, agxbuse(&gname), 1);
- agbindrec(clone, "Agraphinfo_t", sizeof(Agraphinfo_t), true); //node custom data
- agxbprint(&gname, "_clone_%d", state->graphCopyCount++);
- xclone = agopen(agxbuse(&gname), ing->desc, NULL);
- agxbfree(&gname);
- for (n = agfstnode(ing); n; n = agnxtnode(ing, n)) {
- agsubnode(clone,n,1);
- xn = agnode(xclone, agnameof(n),1);
- agbindrec(xn, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true); //node custom data
- CLONE(n) = xn;
- }
- for (n = agfstnode(ing); n; n = agnxtnode(ing, n)) {
- xn = CLONE(n);
- for (e = agfstout(ing, n); e; e = agnxtout(ing, e)) {
- agsubedge(clone,e,1);
- xh = CLONE(aghead(e));
- xe = agedge(xclone, xn, xh, NULL, 1);
- agbindrec(xe, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true); //node custom data
- ORIGE(xe) = e;
- DEGREE(xn) += 1;
- DEGREE(xh) += 1;
- }
- }
- *xg = xclone;
- return clone;
- }
- DEFINE_LIST(deglist, Agnode_t*)
- /// comparison function for sorting nodes by degree, descending
- static int cmpDegree(const Agnode_t **a, const Agnode_t **b) {
- // Suppress Clang/GCC -Wcast-qual warnings. `DEGREE` does not modify the
- // underlying data, but uses casts that make it look like it does.
- #ifdef __GNUC__
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wcast-qual"
- #endif
- if (DEGREE(*a) < DEGREE(*b)) {
- return 1;
- }
- if (DEGREE(*a) > DEGREE(*b)) {
- return -1;
- }
- #ifdef __GNUC__
- #pragma GCC diagnostic pop
- #endif
- return 0;
- }
- /// Add nodes to deg_list, storing them by descending degree.
- static deglist_t getList(Agraph_t *g) {
- deglist_t dl = {0};
- Agnode_t *n;
- for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
- deglist_append(&dl, n);
- }
- deglist_sort(&dl, cmpDegree);
- return dl;
- }
- static void find_pair_edges(Agraph_t * g, Agnode_t * n, Agraph_t * outg)
- {
- Agedge_t *e;
- Agedge_t *ep;
- Agedge_t *ex;
- Agnode_t *n1;
- Agnode_t *n2;
- int has_pair_edge;
- int diff;
- int has_pair_count = 0;
- int no_pair_count = 0;
- int node_degree;
- int edge_cnt = 0;
- node_degree = DEGREE(n);
- Agnode_t **neighbors_with = gv_calloc(node_degree, sizeof(Agnode_t*));
- Agnode_t **neighbors_without = gv_calloc(node_degree, sizeof(Agnode_t*));
- for (e = agfstedge(g, n); e; e = agnxtedge(g, e, n)) {
- n1 = aghead(e);
- if (n1 == n)
- n1 = agtail(e);
- has_pair_edge = 0;
- for (ep = agfstedge(g, n); ep; ep = agnxtedge(g, ep, n)) {
- if (ep == e)
- continue;
- n2 = aghead(ep);
- if (n2 == n)
- n2 = agtail(ep);
- ex = agfindedge(g, n1, n2);
- if (ex) {
- has_pair_edge = 1;
- if (n1 < n2) { /* count edge only once */
- edge_cnt++;
- if (ORIGE(ex)) {
- agdelete(outg, ORIGE(ex));
- ORIGE(ex) = 0; /* delete only once */
- }
- }
- }
- }
- if (has_pair_edge) {
- neighbors_with[has_pair_count] = n1;
- has_pair_count++;
- } else {
- neighbors_without[no_pair_count] = n1;
- no_pair_count++;
- }
- }
- diff = node_degree - 1 - edge_cnt;
- if (diff > 0) {
- int mark;
- Agnode_t *hp;
- Agnode_t *tp;
- if (diff < no_pair_count) {
- for (mark = 0; mark < no_pair_count; mark += 2) {
- if ((mark + 1) >= no_pair_count)
- break;
- tp = neighbors_without[mark];
- hp = neighbors_without[mark + 1];
- agbindrec(agedge(g, tp, hp, NULL, 1), "Agedgeinfo_t", sizeof(Agedgeinfo_t), true); // edge custom data
- DEGREE(tp)++;
- DEGREE(hp)++;
- diff--;
- }
- mark = 2;
- while (diff > 0) {
- tp = neighbors_without[0];
- hp = neighbors_without[mark];
- agbindrec(agedge(g, tp, hp, NULL, 1), "Agedgeinfo_t", sizeof(Agedgeinfo_t), true); // edge custom data
- DEGREE(tp)++;
- DEGREE(hp)++;
- mark++;
- diff--;
- }
- }
- else if (diff == no_pair_count) {
- tp = neighbors_with[0];
- for (mark = 0; mark < no_pair_count; mark++) {
- hp = neighbors_without[mark];
- agbindrec(agedge(g, tp, hp, NULL, 1), "Agedgeinfo_t", sizeof(Agedgeinfo_t), true); //node custom data
- DEGREE(tp)++;
- DEGREE(hp)++;
- }
- }
- }
- free(neighbors_without);
- free(neighbors_with);
- }
- /// Create layout skeleton of ing. Why is returned graph connected?
- ///
- /// @param state Context containing a counter to use for graph copy naming
- static Agraph_t *remove_pair_edges(Agraph_t *ing, circ_state *state) {
- int counter = 0;
- int nodeCount;
- Agraph_t *outg;
- Agraph_t *g;
- Agnode_t *currnode, *adjNode;
- Agedge_t *e;
- outg = clone_graph(ing, &g, state);
- nodeCount = agnnodes(g);
- deglist_t dl = getList(g);
- while (counter < nodeCount - 3) {
- currnode = deglist_is_empty(&dl) ? NULL : deglist_pop_back(&dl);
- /* Remove all adjacent nodes since they have to be reinserted */
- for (e = agfstedge(g, currnode); e; e = agnxtedge(g, e, currnode)) {
- adjNode = aghead(e);
- if (currnode == adjNode)
- adjNode = agtail(e);
- deglist_remove(&dl, adjNode);
- }
- find_pair_edges(g, currnode, outg);
- for (e = agfstedge(g, currnode); e; e = agnxtedge(g, e, currnode)) {
- adjNode = aghead(e);
- if (currnode == adjNode)
- adjNode = agtail(e);
- DEGREE(adjNode)--;
- deglist_append(&dl, adjNode);
- }
- deglist_sort(&dl, cmpDegree);
- agdelete(g, currnode);
- counter++;
- }
- agclose(g);
- deglist_free(&dl);
- return outg;
- }
- static void
- measure_distance(Agnode_t * n, Agnode_t * ancestor, int dist,
- Agnode_t * change)
- {
- Agnode_t *parent;
- parent = TPARENT(ancestor);
- if (parent == NULL)
- return;
- dist++;
- /* check parent to see if it has other leaf paths at greater distance
- than the context node.
- set the path/distance of the leaf at this ancestor node */
- if (DISTONE(parent) == 0) {
- LEAFONE(parent) = n;
- DISTONE(parent) = dist;
- } else if (dist > DISTONE(parent)) {
- if (LEAFONE(parent) != change) {
- if (!DISTTWO(parent) || LEAFTWO(parent) != change)
- change = LEAFONE(parent);
- LEAFTWO(parent) = LEAFONE(parent);
- DISTTWO(parent) = DISTONE(parent);
- }
- LEAFONE(parent) = n;
- DISTONE(parent) = dist;
- } else if (dist > DISTTWO(parent)) {
- LEAFTWO(parent) = n;
- DISTTWO(parent) = dist;
- return;
- } else
- return;
- measure_distance(n, parent, dist, change);
- }
- /// Find and return longest path in tree.
- static nodelist_t find_longest_path(Agraph_t *tree) {
- Agnode_t *n;
- Agedge_t *e;
- Agnode_t *common = 0;
- int maxlength = 0;
- int length;
- if (agnnodes(tree) == 1) {
- nodelist_t beginPath = {0};
- n = agfstnode(tree);
- nodelist_append(&beginPath, n);
- SET_ONPATH(n);
- return beginPath;
- }
- for (n = agfstnode(tree); n; n = agnxtnode(tree, n)) {
- int count = 0;
- for (e = agfstedge(tree, n); e; e = agnxtedge(tree, e, n)) {
- count++;
- }
- if (count == 1)
- measure_distance(n, n, 0, NULL);
- }
- /* find the branch node rooted at the longest path */
- for (n = agfstnode(tree); n; n = agnxtnode(tree, n)) {
- length = DISTONE(n) + DISTTWO(n);
- if (length > maxlength) {
- common = n;
- maxlength = length;
- }
- }
- nodelist_t beginPath = {0};
- for (n = LEAFONE(common); n != common; n = TPARENT(n)) {
- nodelist_append(&beginPath, n);
- SET_ONPATH(n);
- }
- nodelist_append(&beginPath, common);
- SET_ONPATH(common);
- if (DISTTWO(common)) { /* 2nd path might be empty */
- nodelist_t endPath = {0};
- for (n = LEAFTWO(common); n != common; n = TPARENT(n)) {
- nodelist_append(&endPath, n);
- SET_ONPATH(n);
- }
- reverseAppend(&beginPath, &endPath);
- }
- return beginPath;
- }
- /// Simple depth first search, adding traversed edges to tree.
- static void dfs(Agraph_t * g, Agnode_t * n, Agraph_t * tree)
- {
- Agedge_t *e;
- Agnode_t *neighbor;
- SET_VISITED(n);
- for (e = agfstedge(g, n); e; e = agnxtedge(g, e, n)) {
- neighbor = aghead(e);
- if (neighbor == n)
- neighbor = agtail(e);
- if (!VISITED(neighbor)) {
- /* add the edge to the dfs tree */
- agsubedge(tree,e,1);
- TPARENT(neighbor) = n;
- dfs(g, neighbor, tree);
- }
- }
- }
- /// Construct spanning forest of g as subgraph
- ///
- /// @param state Context containing a counter to use for spanning tree naming
- static Agraph_t *spanning_tree(Agraph_t *g, circ_state *state) {
- Agnode_t *n;
- Agraph_t *tree;
- agxbuf gname = {0};
- agxbprint(&gname, "_span_%d", state->spanningTreeCount++);
- tree = agsubg(g, agxbuse(&gname), 1);
- agxbfree(&gname);
- agbindrec(tree, "Agraphinfo_t", sizeof(Agraphinfo_t), true); //node custom data
- for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
- agsubnode(tree,n,1);
- DISTONE(n) = 0;
- DISTTWO(n) = 0;
- UNSET_VISITED(n);
- }
- for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
- if (!VISITED(n)) {
- TPARENT(n) = NULL;
- dfs(g, n, tree);
- }
- }
- return tree;
- }
- /// Add induced edges.
- static void block_graph(Agraph_t * g, block_t * sn)
- {
- Agnode_t *n;
- Agedge_t *e;
- Agraph_t *subg = sn->sub_graph;
- for (n = agfstnode(subg); n; n = agnxtnode(subg, n)) {
- for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
- if (BLOCK(aghead(e)) == sn)
- agsubedge(subg,e,1);
- }
- }
- }
- static int count_all_crossings(nodelist_t * list, Agraph_t * subg)
- {
- edgelist *openEdgeList = init_edgelist();
- Agnode_t *n;
- Agedge_t *e;
- int crossings = 0;
- int order = 1;
- for (n = agfstnode(subg); n; n = agnxtnode(subg, n)) {
- for (e = agfstout(subg, n); e; e = agnxtout(subg, e)) {
- EDGEORDER(e) = 0;
- }
- }
- for (size_t item = 0; item < nodelist_size(list); ++item) {
- n = nodelist_get(list, item);
- for (e = agfstedge(subg, n); e; e = agnxtedge(subg, e, n)) {
- if (EDGEORDER(e) > 0) {
- edgelistitem *eitem;
- Agedge_t *ep;
- for (eitem = dtfirst(openEdgeList); eitem;
- eitem = dtnext(openEdgeList, eitem)) {
- ep = eitem->edge;
- if (EDGEORDER(ep) > EDGEORDER(e)) {
- if (aghead(ep) != n && agtail(ep) != n)
- crossings++;
- }
- }
- remove_edge(openEdgeList, e);
- }
- }
- for (e = agfstedge(subg, n); e; e = agnxtedge(subg, e, n)) {
- if (EDGEORDER(e) == 0) {
- EDGEORDER(e) = order;
- add_edge(openEdgeList, e);
- }
- }
- order++;
- }
- free_edgelist(openEdgeList);
- return crossings;
- }
- #define CROSS_ITER 10
- /* Attempt to reduce edge crossings by moving nodes.
- * Original crossing count is in cnt; final count is returned there.
- * list is the original list; return the best list found.
- */
- static nodelist_t reduce(nodelist_t list, Agraph_t *subg, int *cnt) {
- Agnode_t *curnode;
- Agedge_t *e;
- Agnode_t *neighbor;
- int crossings, j, newCrossings;
- crossings = *cnt;
- for (curnode = agfstnode(subg); curnode;
- curnode = agnxtnode(subg, curnode)) {
- /* move curnode next to its neighbors */
- for (e = agfstedge(subg, curnode); e;
- e = agnxtedge(subg, e, curnode)) {
- neighbor = agtail(e);
- if (neighbor == curnode)
- neighbor = aghead(e);
- for (j = 0; j < 2; j++) {
- nodelist_t listCopy = nodelist_copy(&list);
- insertNodelist(&list, curnode, neighbor, j);
- newCrossings = count_all_crossings(&list, subg);
- if (newCrossings < crossings) {
- crossings = newCrossings;
- nodelist_free(&listCopy);
- if (crossings == 0) {
- *cnt = 0;
- return list;
- }
- } else {
- nodelist_free(&list);
- list = listCopy;
- }
- }
- }
- }
- *cnt = crossings;
- return list;
- }
- static nodelist_t reduce_edge_crossings(nodelist_t list, Agraph_t *subg) {
- int i, crossings, origCrossings;
- crossings = count_all_crossings(&list, subg);
- if (crossings == 0)
- return list;
- for (i = 0; i < CROSS_ITER; i++) {
- origCrossings = crossings;
- list = reduce(list, subg, &crossings);
- /* return if no crossings or no improvement */
- if (origCrossings == crossings || crossings == 0)
- return list;
- }
- return list;
- }
- /// Return max dimension of nodes on list
- static double largest_nodesize(nodelist_t * list)
- {
- double size = 0;
- for (size_t item = 0; item < nodelist_size(list); ++item) {
- Agnode_t *n = ORIGN(nodelist_get(list, item));
- if (ND_width(n) > size)
- size = ND_width(n);
- if (ND_height(n) > size)
- size = ND_height(n);
- }
- return size;
- }
- /// Add n to list. By construction, n is not in list at start.
- static void place_node(Agraph_t * g, Agnode_t * n, nodelist_t * list)
- {
- Agedge_t *e;
- bool placed = false;
- nodelist_t neighbors = {0};
- for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
- nodelist_append(&neighbors, aghead(e));
- SET_NEIGHBOR(aghead(e));
- }
- for (e = agfstin(g, n); e; e = agnxtin(g, e)) {
- nodelist_append(&neighbors, agtail(e));
- SET_NEIGHBOR(agtail(e));
- }
- /* Look for 2 neighbors consecutive on list */
- if (nodelist_size(&neighbors) >= 2) {
- for (size_t one = 0; one < nodelist_size(list); ++one) {
- const size_t two = (one + 1) % nodelist_size(list);
- if (NEIGHBOR(nodelist_get(list, one)) &&
- NEIGHBOR(nodelist_get(list, two))) {
- appendNodelist(list, one + 1, n);
- placed = true;
- break;
- }
- }
- }
- /* Find any neighbor on list */
- if (!placed && !nodelist_is_empty(&neighbors)) {
- for (size_t one = 0; one < nodelist_size(list); ++one) {
- if (NEIGHBOR(nodelist_get(list, one))) {
- appendNodelist(list, one + 1, n);
- placed = true;
- break;
- }
- }
- }
- if (!placed)
- nodelist_append(list, n);
- for (size_t one = 0; one < nodelist_size(&neighbors); ++one)
- UNSET_NEIGHBOR(nodelist_get(&neighbors, one));
- nodelist_free(&neighbors);
- }
- /// Add nodes not in list to list.
- static void place_residual_nodes(Agraph_t * g, nodelist_t * list)
- {
- Agnode_t *n;
- for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
- if (!ONPATH(n))
- place_node(g, n, list);
- }
- }
- /// @param state Context containing a counter to use for graph copy naming
- nodelist_t layout_block(Agraph_t *g, block_t *sn, double min_dist,
- circ_state *state) {
- Agraph_t *copyG, *tree, *subg;
- int k;
- double theta, radius, largest_node;
- largest_node = 0;
- subg = sn->sub_graph;
- block_graph(g, sn); /* add induced edges */
- copyG = remove_pair_edges(subg, state);
- tree = spanning_tree(copyG, state);
- nodelist_t longest_path = find_longest_path(tree);
- place_residual_nodes(subg, &longest_path);
- /* at this point, longest_path is a list of all nodes in the block */
- /* apply crossing reduction algorithms here */
- longest_path = reduce_edge_crossings(longest_path, subg);
- size_t N = nodelist_size(&longest_path);
- largest_node = largest_nodesize(&longest_path);
- /* N*(min_dist+largest_node) is roughly circumference of required circle */
- if (N == 1)
- radius = 0;
- else
- radius = (double)N * (min_dist + largest_node) / (2 * M_PI);
- for (size_t item = 0; item < nodelist_size(&longest_path); ++item) {
- Agnode_t *n = nodelist_get(&longest_path, item);
- if (ISPARENT(n)) {
- /* QUESTION: Why is only one parent realigned? */
- realignNodelist(&longest_path, item);
- break;
- }
- }
- k = 0;
- for (size_t item = 0; item < nodelist_size(&longest_path); ++item) {
- Agnode_t *n = nodelist_get(&longest_path, item);
- POSITION(n) = k;
- PSI(n) = 0.0;
- theta = k * (2.0 * M_PI / (double)N);
- ND_pos(n)[0] = radius * cos(theta);
- ND_pos(n)[1] = radius * sin(theta);
- k++;
- }
- if (N == 1)
- sn->radius = largest_node / 2;
- else
- sn->radius = radius;
- sn->rad0 = sn->radius;
- /* initialize parent pos */
- sn->parent_pos = -1;
- agclose(copyG);
- return longest_path;
- }
- #ifdef DEBUG
- void prTree(Agraph_t * g)
- {
- Agnode_t *n;
- for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
- if (TPARENT(n)) {
- fprintf(stderr, "%s ", agnameof(n));
- fprintf(stderr, "-> %s\n", agnameof(TPARENT(n)));
- }
- }
- }
- #endif
|