sfdpinit.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  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 "config.h"
  11. #include <float.h>
  12. #include <limits.h>
  13. #include <sfdpgen/sfdp.h>
  14. #include <neatogen/neato.h>
  15. #include <neatogen/adjust.h>
  16. #include <pack/pack.h>
  17. #include <assert.h>
  18. #include <sfdpgen/spring_electrical.h>
  19. #include <neatogen/overlap.h>
  20. #include <sfdpgen/stress_model.h>
  21. #include <cgraph/cgraph.h>
  22. #include <cgraph/gv_ctype.h>
  23. #include <stdbool.h>
  24. #include <stddef.h>
  25. #include <util/alloc.h>
  26. #include <util/strcasecmp.h>
  27. static void sfdp_init_edge(edge_t * e)
  28. {
  29. agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true); //node custom data
  30. common_init_edge(e);
  31. }
  32. static void sfdp_init_node_edge(graph_t * g)
  33. {
  34. node_t *n;
  35. edge_t *e;
  36. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  37. neato_init_node(n);
  38. }
  39. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  40. for (e = agfstout(g, n); e; e = agnxtout(g, e))
  41. sfdp_init_edge(e);
  42. }
  43. }
  44. static void sfdp_init_graph(Agraph_t * g)
  45. {
  46. int outdim;
  47. setEdgeType(g, EDGETYPE_LINE);
  48. outdim = late_int(g, agfindgraphattr(g, "dimen"), 2, 2);
  49. GD_ndim(agroot(g)) = late_int(g, agfindgraphattr(g, "dim"), outdim, 2);
  50. Ndim = GD_ndim(agroot(g)) = MIN(GD_ndim(agroot(g)), MAXDIM);
  51. GD_odim(agroot(g)) = MIN(outdim, Ndim);
  52. sfdp_init_node_edge(g);
  53. }
  54. /* getPos:
  55. */
  56. static double *getPos(Agraph_t * g)
  57. {
  58. Agnode_t *n;
  59. double *pos = gv_calloc(Ndim * agnnodes(g), sizeof(double));
  60. int ix, i;
  61. if (agfindnodeattr(g, "pos") == NULL)
  62. return pos;
  63. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  64. i = ND_id(n);
  65. if (hasPos(n)) {
  66. for (ix = 0; ix < Ndim; ix++) {
  67. pos[i * Ndim + ix] = ND_pos(n)[ix];
  68. }
  69. }
  70. }
  71. return pos;
  72. }
  73. static void sfdpLayout(graph_t * g, spring_electrical_control ctrl,
  74. pointf pad) {
  75. double *sizes;
  76. double *pos;
  77. Agnode_t *n;
  78. int flag, i;
  79. int n_edge_label_nodes = 0, *edge_label_nodes = NULL;
  80. SparseMatrix A = makeMatrix(g);
  81. if (ctrl->overlap >= 0) {
  82. if (ctrl->edge_labeling_scheme > 0)
  83. sizes = getSizes(g, pad, &n_edge_label_nodes, &edge_label_nodes);
  84. else
  85. sizes = getSizes(g, pad, NULL, NULL);
  86. }
  87. else
  88. sizes = NULL;
  89. pos = getPos(g);
  90. multilevel_spring_electrical_embedding(Ndim, A, ctrl, sizes, pos, n_edge_label_nodes, edge_label_nodes, &flag);
  91. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  92. double *npos = pos + (Ndim * ND_id(n));
  93. for (i = 0; i < Ndim; i++) {
  94. ND_pos(n)[i] = npos[i];
  95. }
  96. }
  97. free(sizes);
  98. free(pos);
  99. SparseMatrix_delete (A);
  100. free(edge_label_nodes);
  101. }
  102. static int
  103. late_smooth (graph_t* g, Agsym_t* sym, int dflt)
  104. {
  105. char* s;
  106. int v;
  107. int rv;
  108. if (!sym) return dflt;
  109. s = agxget (g, sym);
  110. if (gv_isdigit(*s)) {
  111. #if (defined(HAVE_GTS) || defined(HAVE_TRIANGLE))
  112. if ((v = atoi (s)) <= SMOOTHING_RNG)
  113. #else
  114. if ((v = atoi (s)) <= SMOOTHING_SPRING)
  115. #endif
  116. rv = v;
  117. else
  118. rv = dflt;
  119. }
  120. else if (gv_isalpha(*s)) {
  121. if (!strcasecmp(s, "avg_dist"))
  122. rv = SMOOTHING_STRESS_MAJORIZATION_AVG_DIST;
  123. else if (!strcasecmp(s, "graph_dist"))
  124. rv = SMOOTHING_STRESS_MAJORIZATION_GRAPH_DIST;
  125. else if (!strcasecmp(s, "none"))
  126. rv = SMOOTHING_NONE;
  127. else if (!strcasecmp(s, "power_dist"))
  128. rv = SMOOTHING_STRESS_MAJORIZATION_POWER_DIST;
  129. #if (defined(HAVE_GTS) || defined(HAVE_TRIANGLE))
  130. else if (!strcasecmp(s, "rng"))
  131. rv = SMOOTHING_RNG;
  132. #endif
  133. else if (!strcasecmp(s, "spring"))
  134. rv = SMOOTHING_SPRING;
  135. #if (defined(HAVE_GTS) || defined(HAVE_TRIANGLE))
  136. else if (!strcasecmp(s, "triangle"))
  137. rv = SMOOTHING_TRIANGLE;
  138. #endif
  139. else
  140. rv = dflt;
  141. }
  142. else
  143. rv = dflt;
  144. return rv;
  145. }
  146. static int
  147. late_quadtree_scheme (graph_t* g, Agsym_t* sym, int dflt)
  148. {
  149. char* s;
  150. int v;
  151. int rv;
  152. if (!sym) return dflt;
  153. s = agxget (g, sym);
  154. if (gv_isdigit(*s)) {
  155. if ((v = atoi (s)) <= QUAD_TREE_FAST && v >= QUAD_TREE_NONE){
  156. rv = v;
  157. } else {
  158. rv = dflt;
  159. }
  160. } else if (gv_isalpha(*s)) {
  161. if (!strcasecmp(s, "none") || !strcasecmp(s, "false") ){
  162. rv = QUAD_TREE_NONE;
  163. } else if (!strcasecmp(s, "normal") || !strcasecmp(s, "true") || !strcasecmp(s, "yes")){
  164. rv = QUAD_TREE_NORMAL;
  165. } else if (!strcasecmp(s, "fast")){
  166. rv = QUAD_TREE_FAST;
  167. } else {
  168. rv = dflt;
  169. }
  170. } else {
  171. rv = dflt;
  172. }
  173. return rv;
  174. }
  175. /* tuneControl:
  176. * Use user values to reset control
  177. */
  178. static void
  179. tuneControl (graph_t* g, spring_electrical_control ctrl)
  180. {
  181. long seed;
  182. int init;
  183. seed = ctrl->random_seed;
  184. init = setSeed (g, INIT_RANDOM, &seed);
  185. if (init != INIT_RANDOM) {
  186. agwarningf("sfdp only supports start=random\n");
  187. }
  188. ctrl->random_seed = seed;
  189. ctrl->K = late_double(g, agfindgraphattr(g, "K"), -1.0, 0.0);
  190. ctrl->p = -1.0*late_double(g, agfindgraphattr(g, "repulsiveforce"), -AUTOP, 0.0);
  191. ctrl->multilevels = late_int(g, agfindgraphattr(g, "levels"), INT_MAX, 0);
  192. ctrl->smoothing = late_smooth(g, agfindgraphattr(g, "smoothing"), SMOOTHING_NONE);
  193. ctrl->tscheme = late_quadtree_scheme(g, agfindgraphattr(g, "quadtree"), QUAD_TREE_NORMAL);
  194. ctrl->beautify_leaves = mapbool(agget(g, "beautify"));
  195. ctrl->do_shrinking = mapBool(agget(g, "overlap_shrink"), true);
  196. ctrl->rotation = late_double(g, agfindgraphattr(g, "rotation"), 0.0, -DBL_MAX);
  197. ctrl->edge_labeling_scheme = late_int(g, agfindgraphattr(g, "label_scheme"), 0, 0);
  198. if (ctrl->edge_labeling_scheme > 4) {
  199. agwarningf("label_scheme = %d > 4 : ignoring\n", ctrl->edge_labeling_scheme);
  200. ctrl->edge_labeling_scheme = 0;
  201. }
  202. }
  203. void sfdp_layout(graph_t * g)
  204. {
  205. int doAdjust;
  206. adjust_data am;
  207. sfdp_init_graph(g);
  208. doAdjust = (Ndim == 2);
  209. if (agnnodes(g)) {
  210. Agraph_t **ccs;
  211. Agraph_t *sg;
  212. expand_t sep;
  213. pointf pad;
  214. spring_electrical_control ctrl = spring_electrical_control_new();
  215. tuneControl (g, ctrl);
  216. #if (defined(HAVE_GTS) || defined(HAVE_TRIANGLE))
  217. graphAdjustMode(g, &am, "prism0");
  218. #else
  219. graphAdjustMode(g, &am, 0);
  220. #endif
  221. pad.x = PS2INCH(DFLT_MARGIN);
  222. pad.y = PS2INCH(DFLT_MARGIN);
  223. if ((am.mode == AM_PRISM) && doAdjust) {
  224. doAdjust = 0; /* overlap removal done in sfdp */
  225. ctrl->overlap = am.value;
  226. ctrl->initial_scaling = am.scaling;
  227. sep = sepFactor(g);
  228. if (sep.doAdd) {
  229. pad.x = PS2INCH(sep.x);
  230. pad.y = PS2INCH(sep.y);
  231. }
  232. }
  233. else {
  234. /* Turn off overlap removal in sfdp if prism not used */
  235. ctrl->overlap = -1;
  236. }
  237. if (Verbose)
  238. spring_electrical_control_print(ctrl);
  239. size_t ncc;
  240. ccs = ccomps(g, &ncc, 0);
  241. if (ncc == 1) {
  242. sfdpLayout(g, ctrl, pad);
  243. if (doAdjust) removeOverlapWith(g, &am);
  244. spline_edges(g);
  245. } else {
  246. pack_info pinfo;
  247. getPackInfo(g, l_node, CL_OFFSET, &pinfo);
  248. pinfo.doSplines = true;
  249. for (size_t i = 0; i < ncc; i++) {
  250. sg = ccs[i];
  251. (void)graphviz_node_induce(sg, NULL);
  252. sfdpLayout(sg, ctrl, pad);
  253. if (doAdjust) removeOverlapWith(sg, &am);
  254. setEdgeType(sg, EDGETYPE_LINE);
  255. spline_edges(sg);
  256. }
  257. packSubgraphs(ncc, ccs, g, &pinfo);
  258. }
  259. for (size_t i = 0; i < ncc; i++) {
  260. agdelete(g, ccs[i]);
  261. }
  262. free(ccs);
  263. spring_electrical_control_delete(ctrl);
  264. }
  265. dotneato_postprocess(g);
  266. }
  267. void sfdp_cleanup(graph_t * g)
  268. {
  269. node_t *n;
  270. edge_t *e;
  271. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  272. for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
  273. gv_cleanup_edge(e);
  274. }
  275. gv_cleanup_node(n);
  276. }
  277. }
  278. /**
  279. * @dir lib/sfdpgen
  280. * @brief scalable [Force-Directed Placement](https://en.wikipedia.org/wiki/Force-directed_graph_drawing) layout engine, API sfdpgen/sfdp.h
  281. * @ingroup engines
  282. *
  283. * [SFDP layout user manual](https://graphviz.org/docs/layouts/sfdp/)
  284. *
  285. * Other @ref engines
  286. */