circularinit.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. /**
  2. * @file
  3. * @brief API circogen/circo.h: @ref circo_init_graph, @ref circoLayout, @ref circo_layout, @ref circo_cleanup
  4. */
  5. /*************************************************************************
  6. * Copyright (c) 2011 AT&T Intellectual Property
  7. * All rights reserved. This program and the accompanying materials
  8. * are made available under the terms of the Eclipse Public License v1.0
  9. * which accompanies this distribution, and is available at
  10. * https://www.eclipse.org/legal/epl-v10.html
  11. *
  12. * Contributors: Details at https://graphviz.org
  13. *************************************************************************/
  14. #include <circogen/circular.h>
  15. #include <neatogen/adjust.h>
  16. #include <pack/pack.h>
  17. #include <neatogen/neatoprocs.h>
  18. #include <stddef.h>
  19. #include <stdbool.h>
  20. #include <string.h>
  21. #include <util/alloc.h>
  22. static void circular_init_edge(edge_t * e)
  23. {
  24. agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true); //node custom data
  25. common_init_edge(e);
  26. ED_factor(e) = late_double(e, E_weight, 1.0, 0.0);
  27. }
  28. static void circular_init_node_edge(graph_t * g)
  29. {
  30. node_t *n;
  31. edge_t *e;
  32. int i = 0;
  33. ndata* alg = gv_calloc(agnnodes(g), sizeof(ndata));
  34. GD_neato_nlist(g) = gv_calloc(agnnodes(g) + 1, sizeof(node_t*));
  35. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  36. neato_init_node(n);
  37. ND_alg(n) = alg + i;
  38. GD_neato_nlist(g)[i++] = n;
  39. }
  40. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  41. for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
  42. circular_init_edge(e);
  43. }
  44. }
  45. }
  46. void circo_init_graph(graph_t * g)
  47. {
  48. setEdgeType (g, EDGETYPE_LINE);
  49. /* GD_ndim(g) = late_int(g,agfindattr(g,"dim"),2,2); */
  50. Ndim = GD_ndim(agroot(g)) = 2; /* The algorithm only makes sense in 2D */
  51. circular_init_node_edge(g);
  52. }
  53. /* Make a node in the derived graph, with the given name.
  54. * orig points to what it represents, either a real node or
  55. * a cluster. Copy size info from original node; needed for
  56. * adjustNodes and packSubgraphs.
  57. */
  58. static node_t *makeDerivedNode(graph_t * dg, char *name, int isNode,
  59. void *orig)
  60. {
  61. node_t *n = agnode(dg, name,1);
  62. agbindrec(n, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true); //node custom data
  63. ND_alg(n) = gv_alloc(sizeof(cdata));
  64. if (isNode) {
  65. ND_pos(n) = gv_calloc(Ndim, sizeof(double));
  66. ND_lw(n) = ND_lw(orig);
  67. ND_rw(n) = ND_rw(orig);
  68. ND_ht(n) = ND_ht(orig);
  69. ORIGN(n) = orig;
  70. } else
  71. ORIGG(n) = orig;
  72. return n;
  73. }
  74. /* Construct a strict, undirected graph with no loops from g.
  75. * Construct the connected components with the provision that all
  76. * nodes in a block subgraph are considered connected.
  77. * Return array of components with number of components in cnt.
  78. * Each component has its blocks as subgraphs.
  79. * FIX: Check that blocks are disjoint.
  80. */
  81. static Agraph_t **circomps(Agraph_t *g, size_t *cnt) {
  82. Agraph_t **ccs;
  83. Agraph_t *dg;
  84. Agnode_t *n, *v, *dt, *dh;
  85. Agedge_t *e;
  86. Agraph_t *sg;
  87. Agedge_t *ep;
  88. Agnode_t *p;
  89. dg = agopen("derived", Agstrictundirected,NULL);
  90. agbindrec (dg, "info", sizeof(Agraphinfo_t), true);
  91. GD_alg(g) = dg; /* store derived graph for closing later */
  92. for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
  93. if (DNODE(v))
  94. continue;
  95. n = makeDerivedNode(dg, agnameof(v), 1, v);
  96. DNODE(v) = n;
  97. }
  98. for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
  99. for (e = agfstout(g, v); e; e = agnxtout(g, e)) {
  100. dt = DNODE(agtail(e));
  101. dh = DNODE(aghead(e));
  102. if (dt != dh) {
  103. agbindrec(agedge(dg, dt, dh, NULL, 1), "Agedgeinfo_t", sizeof(Agedgeinfo_t), true); //node custom data
  104. }
  105. }
  106. }
  107. size_t c_cnt;
  108. ccs = ccomps(dg, &c_cnt, 0);
  109. /* replace block nodes with block contents */
  110. for (size_t i = 0; i < c_cnt; i++) {
  111. sg = ccs[i];
  112. /* add edges: since sg is a union of components, all edges
  113. * of any node should be added, except loops.
  114. */
  115. for (n = agfstnode(sg); n; n = agnxtnode(sg, n)) {
  116. p = ORIGN(n);
  117. for (e = agfstout(g, p); e; e = agnxtout(g, e)) {
  118. /* n = DNODE(agtail(e)); by construction since agtail(e) == p */
  119. dh = DNODE(aghead(e));
  120. if (n != dh) {
  121. ep = agedge(dg, n, dh, NULL, 1);
  122. agbindrec(ep, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true); //node custom data
  123. agsubedge(sg,ep,1);
  124. }
  125. }
  126. }
  127. }
  128. /* Finally, add edge data to edges */
  129. for (n = agfstnode(dg); n; n = agnxtnode(dg, n)) {
  130. for (e = agfstout(dg, n); e; e = agnxtout(dg, e)) {
  131. ED_alg(e) = gv_alloc(sizeof(edata));
  132. }
  133. }
  134. *cnt = c_cnt;
  135. return ccs;
  136. }
  137. static void closeDerivedGraph(graph_t * g)
  138. {
  139. node_t *n;
  140. edge_t *e;
  141. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  142. for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
  143. free(ED_alg(e));
  144. }
  145. free(ND_alg(n));
  146. free(ND_pos(n));
  147. }
  148. agclose(g);
  149. }
  150. /* Copy position of nodes in given subgraph of derived graph
  151. * to corresponding node in original graph.
  152. * FIX: consider assigning from n directly to ORIG(n).
  153. */
  154. static void copyPosns(graph_t * g)
  155. {
  156. node_t *n;
  157. node_t *v;
  158. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  159. v = ORIGN(n);
  160. ND_pos(v)[0] = ND_pos(n)[0];
  161. ND_pos(v)[1] = ND_pos(n)[1];
  162. }
  163. }
  164. void circoLayout(Agraph_t * g)
  165. {
  166. Agraph_t **ccs;
  167. Agraph_t *sg;
  168. if (agnnodes(g)) {
  169. size_t ncc;
  170. ccs = circomps(g, &ncc);
  171. int blockCount = 0;
  172. if (ncc == 1) {
  173. circularLayout(ccs[0], g, &blockCount);
  174. copyPosns(ccs[0]);
  175. adjustNodes(g);
  176. } else {
  177. Agraph_t *dg = ccs[0]->root;
  178. pack_info pinfo;
  179. getPackInfo(g, l_node, CL_OFFSET, &pinfo);
  180. for (size_t i = 0; i < ncc; i++) {
  181. sg = ccs[i];
  182. circularLayout(sg, g, &blockCount);
  183. adjustNodes(sg);
  184. }
  185. /* FIX: splines have not been calculated for dg
  186. * To use, either do splines in dg and copy to g, or
  187. * construct components of g from ccs and use that in packing.
  188. */
  189. packSubgraphs(ncc, ccs, dg, &pinfo);
  190. for (size_t i = 0; i < ncc; i++)
  191. copyPosns(ccs[i]);
  192. }
  193. free(ccs);
  194. }
  195. }
  196. void circo_layout(Agraph_t * g)
  197. {
  198. if (agnnodes(g) == 0) return;
  199. circo_init_graph(g);
  200. circoLayout(g);
  201. /* Release ND_alg as we need to reuse it during edge routing */
  202. free(ND_alg(agfstnode(g)));
  203. spline_edges(g);
  204. dotneato_postprocess(g);
  205. }
  206. /// ND_alg is freed in circo_layout
  207. void circo_cleanup(graph_t * g)
  208. {
  209. node_t *n;
  210. edge_t *e;
  211. n = agfstnode(g);
  212. if (n == NULL)
  213. return; /* g is empty */
  214. closeDerivedGraph(GD_alg(g)); // delete derived graph
  215. /* free (ND_alg(n)); */
  216. for (; n; n = agnxtnode(g, n)) {
  217. for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
  218. gv_cleanup_edge(e);
  219. }
  220. gv_cleanup_node(n);
  221. }
  222. free(GD_neato_nlist(g));
  223. }
  224. /**
  225. * @dir lib/circogen
  226. * @brief [circular layout](https://en.wikipedia.org/wiki/Circular_layout) engine, API circogen/circo.h
  227. * @ingroup engines
  228. *
  229. * Biconnected components are put on circles.
  230. * block-cutnode tree is done recursively, with children placed
  231. * about parent block.
  232. *
  233. * [Circo layout user manual](https://graphviz.org/docs/layouts/circo/)
  234. *
  235. * Based on:
  236. * - Six and Tollis, "A Framework for Circular Drawings of
  237. * Networks", GD '99, LNCS 1731, pp. 107-116;
  238. * - Six and Tollis, "Circular Drawings of Biconnected Graphs",
  239. * Proc. ALENEX '99, pp 57-73.
  240. * - Kaufmann and Wiese, "Maintaining the Mental Map for Circular
  241. * Drawings", GD '02, LNCS 2528, pp. 12-22.
  242. *
  243. * Other @ref engines
  244. */