circle.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  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. #include <assert.h>
  11. #include <cgraph/gv_ctype.h>
  12. #include <cgraph/gv_math.h>
  13. #include <cgraph/list.h>
  14. #include <twopigen/circle.h>
  15. #include <inttypes.h>
  16. #include <limits.h>
  17. #include <math.h>
  18. #include <stdbool.h>
  19. #include <stdint.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <util/alloc.h>
  23. #include <util/streq.h>
  24. #define DEF_RANKSEP 1.00
  25. #define UNSET 10.00
  26. /* dfs to set distance from a particular leaf.
  27. * Note that termination is implicit in the test
  28. * for reduced number of steps. Proof?
  29. */
  30. static void setNStepsToLeaf(Agraph_t * g, Agnode_t * n, Agnode_t * prev)
  31. {
  32. Agnode_t *next;
  33. uint64_t nsteps = SLEAF(n) + 1;
  34. for (Agedge_t *ep = agfstedge(g, n); ep; ep = agnxtedge(g, ep, n)) {
  35. if ((next = agtail(ep)) == n)
  36. next = aghead(ep);
  37. if (prev == next)
  38. continue;
  39. if (nsteps < SLEAF(next)) { /* handles loops and multiedges */
  40. SLEAF(next) = nsteps;
  41. setNStepsToLeaf(g, next, n);
  42. }
  43. }
  44. }
  45. /* isLeaf:
  46. * Return true if n is a leaf node.
  47. */
  48. static bool isLeaf(Agraph_t * g, Agnode_t * n)
  49. {
  50. Agnode_t *neighp = 0;
  51. Agnode_t *np;
  52. for (Agedge_t *ep = agfstedge(g, n); ep; ep = agnxtedge(g, ep, n)) {
  53. if ((np = agtail(ep)) == n)
  54. np = aghead(ep);
  55. if (n == np)
  56. continue; /* loop */
  57. if (neighp) {
  58. if (neighp != np)
  59. return false; /* two different neighbors */
  60. } else
  61. neighp = np;
  62. }
  63. return true;
  64. }
  65. static void initLayout(Agraph_t * g)
  66. {
  67. int nnodes = agnnodes(g);
  68. assert(nnodes >= 0);
  69. uint64_t INF = (uint64_t)nnodes * (uint64_t)nnodes;
  70. for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
  71. SCENTER(n) = INF;
  72. THETA(n) = UNSET; /* marks theta as unset, since 0 <= theta <= 2PI */
  73. if (isLeaf(g, n))
  74. SLEAF(n) = 0;
  75. else
  76. SLEAF(n) = INF;
  77. }
  78. }
  79. /*
  80. * Working recursively in from each leaf node (ie, each node
  81. * with nStepsToLeaf == 0; see initLayout), set the
  82. * minimum value of nStepsToLeaf for each node. Using
  83. * that information, assign some node to be the centerNode.
  84. */
  85. static Agnode_t *findCenterNode(Agraph_t * g)
  86. {
  87. Agnode_t *center = NULL;
  88. uint64_t maxNStepsToLeaf = 0;
  89. /* dfs from each leaf node */
  90. for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
  91. if (SLEAF(n) == 0)
  92. setNStepsToLeaf(g, n, 0);
  93. }
  94. for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
  95. if (center == NULL || SLEAF(n) > maxNStepsToLeaf) {
  96. maxNStepsToLeaf = SLEAF(n);
  97. center = n;
  98. }
  99. }
  100. return center;
  101. }
  102. DEFINE_LIST(node_queue, Agnode_t *)
  103. /* bfs to create tree structure */
  104. static void setNStepsToCenter(Agraph_t * g, Agnode_t * n)
  105. {
  106. Agnode_t *next;
  107. Agsym_t* wt = agfindedgeattr(g,"weight");
  108. node_queue_t q = {0};
  109. node_queue_push_back(&q, n);
  110. while (!node_queue_is_empty(&q)) {
  111. n = node_queue_pop_front(&q);
  112. uint64_t nsteps = SCENTER(n) + 1;
  113. for (Agedge_t *ep = agfstedge(g, n); ep; ep = agnxtedge(g, ep, n)) {
  114. if (wt && streq(ag_xget(ep,wt),"0")) continue;
  115. if ((next = agtail(ep)) == n)
  116. next = aghead(ep);
  117. if (nsteps < SCENTER(next)) {
  118. SCENTER(next) = nsteps;
  119. SPARENT(next) = n;
  120. NCHILD(n)++;
  121. node_queue_push_back(&q, next);
  122. }
  123. }
  124. }
  125. node_queue_free(&q);
  126. }
  127. /*
  128. * Work out from the center and determine the value of
  129. * nStepsToCenter and parent node for each node.
  130. * Return UINT64_MAX if some node was not reached.
  131. */
  132. static uint64_t setParentNodes(Agraph_t * sg, Agnode_t * center)
  133. {
  134. uint64_t maxn = 0;
  135. uint64_t unset = SCENTER(center);
  136. SCENTER(center) = 0;
  137. SPARENT(center) = 0;
  138. setNStepsToCenter(sg, center);
  139. /* find the maximum number of steps from the center */
  140. for (Agnode_t *n = agfstnode(sg); n; n = agnxtnode(sg, n)) {
  141. if (SCENTER(n) == unset) {
  142. return UINT64_MAX;
  143. }
  144. else if (SCENTER(n) > maxn) {
  145. maxn = SCENTER(n);
  146. }
  147. }
  148. return maxn;
  149. }
  150. /* Sets each node's subtreeSize, which counts the number of
  151. * leaves in subtree rooted at the node.
  152. * At present, this is done bottom-up.
  153. */
  154. static void setSubtreeSize(Agraph_t * g)
  155. {
  156. for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
  157. if (NCHILD(n) > 0)
  158. continue;
  159. STSIZE(n)++;
  160. for (Agnode_t *parent = SPARENT(n); parent; parent = SPARENT(parent)) {
  161. STSIZE(parent)++;
  162. }
  163. }
  164. }
  165. static void setChildSubtreeSpans(Agraph_t * g, Agnode_t * n)
  166. {
  167. Agnode_t *next;
  168. double ratio = SPAN(n) / STSIZE(n);
  169. for (Agedge_t *ep = agfstedge(g, n); ep; ep = agnxtedge(g, ep, n)) {
  170. if ((next = agtail(ep)) == n)
  171. next = aghead(ep);
  172. if (SPARENT(next) != n)
  173. continue; /* handles loops */
  174. if (SPAN(next) != 0.0)
  175. continue; /* multiedges */
  176. SPAN(next) = ratio * STSIZE(next);
  177. if (NCHILD(next) > 0) {
  178. setChildSubtreeSpans(g, next);
  179. }
  180. }
  181. }
  182. static void setSubtreeSpans(Agraph_t * sg, Agnode_t * center)
  183. {
  184. SPAN(center) = 2 * M_PI;
  185. setChildSubtreeSpans(sg, center);
  186. }
  187. /// has the given value been assigned?
  188. static bool is_set(double a) { return !is_exactly_equal(a, UNSET); }
  189. /* Set the node positions for the 2nd and later rings. */
  190. static void setChildPositions(Agraph_t * sg, Agnode_t * n)
  191. {
  192. Agnode_t *next;
  193. double theta; /* theta is the lower boundary radius of the fan */
  194. if (SPARENT(n) == 0) /* center */
  195. theta = 0;
  196. else
  197. theta = THETA(n) - SPAN(n) / 2;
  198. for (Agedge_t *ep = agfstedge(sg, n); ep; ep = agnxtedge(sg, ep, n)) {
  199. if ((next = agtail(ep)) == n)
  200. next = aghead(ep);
  201. if (SPARENT(next) != n)
  202. continue; /* handles loops */
  203. if (is_set(THETA(next)))
  204. continue; /* handles multiedges */
  205. THETA(next) = theta + SPAN(next) / 2.0;
  206. theta += SPAN(next);
  207. if (NCHILD(next) > 0)
  208. setChildPositions(sg, next);
  209. }
  210. }
  211. static void setPositions(Agraph_t * sg, Agnode_t * center)
  212. {
  213. THETA(center) = 0;
  214. setChildPositions(sg, center);
  215. }
  216. /* getRankseps:
  217. * Return array of doubles of size maxrank+1 containing the radius of each
  218. * rank. Position 0 always contains 0. Use the colon-separated list of
  219. * doubles provided by ranksep to get the deltas for each additional rank.
  220. * If not enough values are provided, the last value is repeated.
  221. * If the ranksep attribute is not provided, use DEF_RANKSEP for all values.
  222. */
  223. static double*
  224. getRankseps (Agraph_t* g, uint64_t maxrank)
  225. {
  226. char *p;
  227. char *endp;
  228. char c;
  229. uint64_t rk = 1;
  230. double* ranks = gv_calloc(maxrank + 1, sizeof(double));
  231. double xf = 0.0, delx = 0.0, d;
  232. if ((p = late_string(g, agfindgraphattr(g->root, "ranksep"), NULL))) {
  233. while (rk <= maxrank && (d = strtod (p, &endp)) > 0) {
  234. delx = fmax(d, MIN_RANKSEP);
  235. xf += delx;
  236. ranks[rk++] = xf;
  237. p = endp;
  238. while ((c = *p) && (gv_isspace(c) || c == ':'))
  239. p++;
  240. }
  241. }
  242. else {
  243. delx = DEF_RANKSEP;
  244. }
  245. for (uint64_t i = rk; i <= maxrank; i++) {
  246. xf += delx;
  247. ranks[i] = xf;
  248. }
  249. return ranks;
  250. }
  251. static void setAbsolutePos(Agraph_t * g, uint64_t maxrank)
  252. {
  253. double* ranksep = getRankseps (g, maxrank);
  254. if (Verbose) {
  255. fputs ("Rank separation = ", stderr);
  256. for (uint64_t i = 0; i <= maxrank; i++)
  257. fprintf (stderr, "%.03lf ", ranksep[i]);
  258. fputs ("\n", stderr);
  259. }
  260. /* Convert circular to cartesian coordinates */
  261. for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
  262. double hyp = ranksep[SCENTER(n)];
  263. ND_pos(n)[0] = hyp * cos(THETA(n));
  264. ND_pos(n)[1] = hyp * sin(THETA(n));
  265. }
  266. free (ranksep);
  267. }
  268. /* circleLayout:
  269. * We assume sg is is connected and non-empty.
  270. * Also, if center != 0, we are guaranteed that center is
  271. * in the graph.
  272. */
  273. Agnode_t* circleLayout(Agraph_t * sg, Agnode_t * center)
  274. {
  275. if (agnnodes(sg) == 1) {
  276. Agnode_t *n = agfstnode(sg);
  277. ND_pos(n)[0] = 0;
  278. ND_pos(n)[1] = 0;
  279. return center;
  280. }
  281. initLayout(sg);
  282. if (!center)
  283. center = findCenterNode(sg);
  284. uint64_t maxNStepsToCenter = setParentNodes(sg,center);
  285. if (Verbose)
  286. fprintf(stderr, "root = %s max steps to root = %" PRIu64 "\n",
  287. agnameof(center), maxNStepsToCenter);
  288. if (maxNStepsToCenter == UINT64_MAX) {
  289. agerrorf("twopi: use of weight=0 creates disconnected component.\n");
  290. return center;
  291. }
  292. setSubtreeSize(sg);
  293. setSubtreeSpans(sg, center);
  294. setPositions(sg, center);
  295. setAbsolutePos(sg, maxNStepsToCenter);
  296. return center;
  297. }