twopiinit.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. /*************************************************************************
  2. * Copyright (c) 2011 AT&T Intellectual Property
  3. * All rights reserved. This program and the accompanying materials
  4. * are made available under the terms of the Eclipse Public License v1.0
  5. * which accompanies this distribution, and is available at
  6. * https://www.eclipse.org/legal/epl-v10.html
  7. *
  8. * Contributors: Details at https://graphviz.org
  9. *************************************************************************/
  10. /*
  11. * Written by Emden R. Gansner
  12. * Derived from Graham Wills' algorithm described in GD'97.
  13. */
  14. #include <cgraph/cgraph.h>
  15. #include <twopigen/circle.h>
  16. #include <neatogen/adjust.h>
  17. #include <pack/pack.h>
  18. #include <neatogen/neatoprocs.h>
  19. #include <stdbool.h>
  20. #include <stddef.h>
  21. #include <util/alloc.h>
  22. static void twopi_init_edge(edge_t * e)
  23. {
  24. agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true); //edge custom data
  25. common_init_edge(e);
  26. ED_factor(e) = late_double(e, E_weight, 1.0, 0.0);
  27. }
  28. static void twopi_init_node_edge(graph_t * g)
  29. {
  30. node_t *n;
  31. edge_t *e;
  32. int i = 0;
  33. int n_nodes = agnnodes(g);
  34. rdata* alg = gv_calloc(n_nodes, sizeof(rdata));
  35. GD_neato_nlist(g) = gv_calloc(n_nodes + 1, sizeof(node_t*));
  36. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  37. neato_init_node(n);
  38. ND_alg(n) = alg + i;
  39. GD_neato_nlist(g)[i++] = n;
  40. }
  41. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  42. for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
  43. twopi_init_edge(e);
  44. }
  45. }
  46. }
  47. void twopi_init_graph(graph_t * g)
  48. {
  49. setEdgeType (g, EDGETYPE_LINE);
  50. /* GD_ndim(g) = late_int(g,agfindgraphattr(g,"dim"),2,2); */
  51. Ndim = GD_ndim(agroot(g)) = 2; /* The algorithm only makes sense in 2D */
  52. twopi_init_node_edge(g);
  53. }
  54. static Agnode_t* findRootNode (Agraph_t* sg, Agsym_t* rootattr)
  55. {
  56. Agnode_t* n;
  57. for (n = agfstnode(sg); n; n = agnxtnode(sg,n)) {
  58. if (mapbool(agxget(n,rootattr))) return n;
  59. }
  60. return NULL;
  61. }
  62. /* twopi_layout:
  63. */
  64. void twopi_layout(Agraph_t * g)
  65. {
  66. Agnode_t *ctr = 0;
  67. char *s;
  68. int setRoot = 0;
  69. int setLocalRoot = 0;
  70. pointf sc;
  71. int r;
  72. Agsym_t* rootattr;
  73. if (agnnodes(g) == 0) return;
  74. twopi_init_graph(g);
  75. if ((s = agget(g, "root"))) {
  76. if (*s) {
  77. ctr = agfindnode(g, s);
  78. if (!ctr) {
  79. agwarningf("specified root node \"%s\" was not found.", s);
  80. agerr(AGPREV, "Using default calculation for root node\n");
  81. setRoot = 1;
  82. }
  83. }
  84. else {
  85. setRoot = 1;
  86. }
  87. }
  88. if ((rootattr = agattr(g, AGNODE, "root", 0))) {
  89. setLocalRoot = 1;
  90. }
  91. if ((s = agget(g, "scale")) && *s) {
  92. if ((r = sscanf (s, "%lf,%lf",&sc.x,&sc.y))) {
  93. if (r == 1) sc.y = sc.x;
  94. }
  95. }
  96. if (agnnodes(g)) {
  97. Agraph_t **ccs;
  98. Agraph_t *sg;
  99. Agnode_t *c = NULL;
  100. Agnode_t *n;
  101. Agnode_t* lctr;
  102. size_t ncc;
  103. ccs = ccomps(g, &ncc, 0);
  104. if (ncc == 1) {
  105. if (ctr)
  106. lctr = ctr;
  107. else if (!rootattr || !(lctr = findRootNode(g, rootattr)))
  108. lctr = 0;
  109. c = circleLayout(g, lctr);
  110. if (setRoot && !ctr)
  111. ctr = c;
  112. if (setLocalRoot && !lctr)
  113. agxset (c, rootattr, "1");
  114. n = agfstnode(g);
  115. free(ND_alg(n));
  116. ND_alg(n) = NULL;
  117. adjustNodes(g);
  118. spline_edges(g);
  119. } else {
  120. pack_info pinfo;
  121. getPackInfo (g, l_node, CL_OFFSET, &pinfo);
  122. pinfo.doSplines = false;
  123. for (size_t i = 0; i < ncc; i++) {
  124. sg = ccs[i];
  125. if (ctr && agcontains(sg, ctr))
  126. lctr = ctr;
  127. else if (!rootattr || !(lctr = findRootNode(sg, rootattr)))
  128. lctr = 0;
  129. (void)graphviz_node_induce(sg, NULL);
  130. c = circleLayout(sg, lctr);
  131. if (setRoot && !ctr)
  132. ctr = c;
  133. if (setLocalRoot && (!lctr || (lctr == ctr)))
  134. agxset (c, rootattr, "1");
  135. adjustNodes(sg);
  136. }
  137. n = agfstnode(g);
  138. free(ND_alg(n));
  139. ND_alg(n) = NULL;
  140. packSubgraphs(ncc, ccs, g, &pinfo);
  141. spline_edges(g);
  142. }
  143. for (size_t i = 0; i < ncc; i++) {
  144. agdelete(g, ccs[i]);
  145. }
  146. free(ccs);
  147. }
  148. if (setRoot)
  149. agset (g, "root", agnameof (ctr));
  150. dotneato_postprocess(g);
  151. }
  152. static void twopi_cleanup_graph(graph_t * g)
  153. {
  154. free(GD_neato_nlist(g));
  155. }
  156. /* twopi_cleanup:
  157. * The ND_alg data used by twopi is freed in twopi_layout
  158. * before edge routing as edge routing may use this field.
  159. */
  160. void twopi_cleanup(graph_t * g)
  161. {
  162. node_t *n;
  163. edge_t *e;
  164. n = agfstnode (g);
  165. if (!n) return; /* empty graph */
  166. /* free (ND_alg(n)); */
  167. for (; n; n = agnxtnode(g, n)) {
  168. for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
  169. gv_cleanup_edge(e);
  170. }
  171. gv_cleanup_node(n);
  172. }
  173. twopi_cleanup_graph(g);
  174. }
  175. /**
  176. * @dir lib/twopigen
  177. * @brief radial layout engine, API twopigen/circle.h
  178. * @ingroup engines
  179. *
  180. * Twopi means 2π.
  181. *
  182. * [Twopi layout user manual](https://graphviz.org/docs/layouts/twopi/)
  183. *
  184. * Other @ref engines
  185. */