xlayout.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  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. /* xlayout.c:
  11. * Written by Emden R. Gansner
  12. *
  13. * Layout routine to expand initial layout to accommodate node
  14. * sizes.
  15. */
  16. #ifdef FIX
  17. Allow sep to be absolute additive (margin of n points)
  18. Increase less between tries
  19. #endif
  20. /* uses PRIVATE interface */
  21. #define FDP_PRIVATE 1
  22. #include <cgraph/gv_ctype.h>
  23. #include <fdpgen/xlayout.h>
  24. #include <neatogen/adjust.h>
  25. #include <fdpgen/dbg.h>
  26. #include <math.h>
  27. #define DFLT_overlap "9:prism" /* default overlap value */
  28. static xparams xParams = {
  29. 60, /* numIters */
  30. 0.0, /* T0 */
  31. 0.3, /* K */
  32. 1.5, /* C */
  33. 0 /* loopcnt */
  34. };
  35. static expand_t X_marg;
  36. static double WD2(Agnode_t *n) {
  37. return X_marg.doAdd ? (ND_width(n) / 2.0 + X_marg.x) : (ND_width(n) * X_marg.x / 2.0);
  38. }
  39. static double HT2(Agnode_t *n) {
  40. return X_marg.doAdd ? (ND_height(n) / 2.0 + X_marg.y) : (ND_height(n) * X_marg.y / 2.0);
  41. }
  42. #ifdef DEBUG
  43. static void pr2graphs(Agraph_t *g0, Agraph_t *g1) {
  44. fprintf(stderr,"%s",agnameof(g0));
  45. fprintf(stderr,"(%s)",agnameof(g1));
  46. }
  47. #endif
  48. static double RAD(Agnode_t * n)
  49. {
  50. double w = WD2(n);
  51. double h = HT2(n);
  52. return hypot(w, h);
  53. }
  54. /* xinit_params:
  55. * Initialize local parameters
  56. */
  57. static double xinit_params(graph_t *g, int n, xparams *xpms) {
  58. (void)g;
  59. xParams.K = xpms->K;
  60. xParams.numIters = xpms->numIters;
  61. xParams.T0 = xpms->T0;
  62. xParams.loopcnt = xpms->loopcnt;
  63. if (xpms->C > 0.0)
  64. xParams.C = xpms->C;
  65. const double K2 = xParams.K * xParams.K;
  66. if (xParams.T0 == 0.0)
  67. xParams.T0 = xParams.K * sqrt(n) / 5;
  68. #ifdef DEBUG
  69. if (Verbose) {
  70. prIndent();
  71. fprintf(stderr, "xLayout ");
  72. pr2graphs(g,GORIG(agroot(g)));
  73. fprintf(stderr, " : n = %d K = %f T0 = %f loop %d C %f\n",
  74. xParams.numIters, xParams.K, xParams.T0, xParams.loopcnt,
  75. xParams.C);
  76. }
  77. #endif
  78. return K2;
  79. }
  80. #define X_T0 xParams.T0
  81. #define X_K xParams.K
  82. #define X_numIters xParams.numIters
  83. #define X_loopcnt xParams.loopcnt
  84. #define X_C xParams.C
  85. static double cool(int t)
  86. {
  87. return X_T0 * (X_numIters - t) / X_numIters;
  88. }
  89. /* overlap:
  90. * Return true if nodes overlap
  91. */
  92. static int overlap(node_t * p, node_t * q)
  93. {
  94. const double xdelta = fabs(ND_pos(q)[0] - ND_pos(p)[0]);
  95. const double ydelta = fabs(ND_pos(q)[1] - ND_pos(p)[1]);
  96. return xdelta <= WD2(p) + WD2(q) && ydelta <= HT2(p) + HT2(q);
  97. }
  98. /* cntOverlaps:
  99. * Return number of overlaps.
  100. */
  101. static int cntOverlaps(graph_t * g)
  102. {
  103. int cnt = 0;
  104. for (node_t *p = agfstnode(g); p; p = agnxtnode(g, p)) {
  105. for (node_t *q = agnxtnode(g, p); q; q = agnxtnode(g, q)) {
  106. cnt += overlap(p, q);
  107. }
  108. }
  109. return cnt;
  110. }
  111. /* doRep:
  112. * Return 1 if nodes overlap
  113. */
  114. static int doRep(node_t *p, node_t *q, double xdelta, double ydelta,
  115. double dist2, double X_ov, double X_nonov) {
  116. int ov;
  117. double force;
  118. while (dist2 == 0.0) {
  119. xdelta = 5 - rand() % 10;
  120. ydelta = 5 - rand() % 10;
  121. dist2 = xdelta * xdelta + ydelta * ydelta;
  122. }
  123. if ((ov = overlap(p, q)))
  124. force = X_ov / dist2;
  125. else
  126. force = X_nonov / dist2;
  127. #ifdef DEBUG
  128. if (Verbose == 4) {
  129. prIndent();
  130. const double dist = sqrt(dist2);
  131. fprintf(stderr, " ov Fr %f dist %f\n", force * dist, dist);
  132. }
  133. #endif
  134. DISP(q)[0] += xdelta * force;
  135. DISP(q)[1] += ydelta * force;
  136. DISP(p)[0] -= xdelta * force;
  137. DISP(p)[1] -= ydelta * force;
  138. return ov;
  139. }
  140. /* applyRep:
  141. * Repulsive force = (K*K)/d
  142. * Return 1 if nodes overlap
  143. */
  144. static int applyRep(Agnode_t *p, Agnode_t *q, double X_ov, double X_nonov) {
  145. const double xdelta = ND_pos(q)[0] - ND_pos(p)[0];
  146. const double ydelta = ND_pos(q)[1] - ND_pos(p)[1];
  147. return doRep(p, q, xdelta, ydelta, xdelta * xdelta + ydelta * ydelta, X_ov,
  148. X_nonov);
  149. }
  150. static void applyAttr(Agnode_t * p, Agnode_t * q)
  151. {
  152. if (overlap(p, q)) {
  153. #ifdef DEBUG
  154. if (Verbose == 4) {
  155. prIndent();
  156. fprintf(stderr, "ov 1 Fa 0 din %f\n", RAD(p) + RAD(q));
  157. }
  158. #endif
  159. return;
  160. }
  161. const double xdelta = ND_pos(q)[0] - ND_pos(p)[0];
  162. const double ydelta = ND_pos(q)[1] - ND_pos(p)[1];
  163. const double dist = hypot(xdelta, ydelta);
  164. const double din = RAD(p) + RAD(q);
  165. const double dout = dist - din;
  166. const double force = dout * dout / ((X_K + din) * dist);
  167. #ifdef DEBUG
  168. if (Verbose == 4) {
  169. prIndent();
  170. fprintf(stderr, " ov 0 Fa %f din %f \n", force * dist, din);
  171. }
  172. #endif
  173. DISP(q)[0] -= xdelta * force;
  174. DISP(q)[1] -= ydelta * force;
  175. DISP(p)[0] += xdelta * force;
  176. DISP(p)[1] += ydelta * force;
  177. }
  178. /* adjust:
  179. * Return 0 if definitely no overlaps.
  180. * Return non-zero if we had overlaps before most recent move.
  181. */
  182. static int adjust(Agraph_t *g, double temp, double X_ov, double X_nonov) {
  183. int overlaps = 0;
  184. #ifdef DEBUG
  185. if (Verbose == 4)
  186. fprintf(stderr, "=================\n");
  187. #endif
  188. for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
  189. DISP(n)[0] = DISP(n)[1] = 0;
  190. }
  191. for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
  192. int ov;
  193. for (Agnode_t *n1 = agnxtnode(g, n); n1; n1 = agnxtnode(g, n1)) {
  194. ov = applyRep(n, n1, X_ov, X_nonov);
  195. overlaps += ov;
  196. }
  197. for (Agedge_t *e = agfstout(g, n); e; e = agnxtout(g, e)) {
  198. applyAttr(n,aghead(e));
  199. }
  200. }
  201. if (overlaps == 0)
  202. return 0;
  203. const double temp2 = temp * temp;
  204. for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
  205. if (ND_pinned(n) == P_PIN)
  206. continue;
  207. const double disp[] = {DISP(n)[0], DISP(n)[1]}; // incremental displacement
  208. const double len2 = disp[0] * disp[0] + disp[1] * disp[1];
  209. if (len2 < temp2) {
  210. ND_pos(n)[0] += disp[0];
  211. ND_pos(n)[1] += disp[1];
  212. } else {
  213. /* to avoid sqrt, consider abs(x) + abs(y) */
  214. const double len = sqrt(len2);
  215. ND_pos(n)[0] += disp[0] * temp / len;
  216. ND_pos(n)[1] += disp[1] * temp / len;
  217. }
  218. }
  219. return overlaps;
  220. }
  221. /* x_layout:
  222. * Given graph g with initial layout, adjust g so that nodes
  223. * do not overlap.
  224. * Assume g is connected.
  225. * g may have ports. At present, we do not use ports in the layout
  226. * at this stage.
  227. * Returns non-zero if overlaps still exist.
  228. * TODO (possible):
  229. * Allow X_T0 independent of T_TO or percentage of, so the cooling would
  230. * be piecewise linear. This would allow longer, cooler expansion.
  231. * In tries > 1, increase X_T0 and/or lengthen cooling
  232. */
  233. static int x_layout(graph_t * g, xparams * pxpms, int tries)
  234. {
  235. int nnodes = agnnodes(g);
  236. int nedges = agnedges(g);
  237. X_marg = sepFactor (g);
  238. if (X_marg.doAdd) {
  239. X_marg.x = PS2INCH(X_marg.x); /* sepFactor is in points */
  240. X_marg.y = PS2INCH(X_marg.y);
  241. }
  242. int ov = cntOverlaps(g);
  243. if (ov == 0)
  244. return 0;
  245. xparams xpms = *pxpms;
  246. const double K = xpms.K;
  247. for (int try = 0; ov && try < tries; ++try) {
  248. const double K2 = xinit_params(g, nnodes, &xpms);
  249. const double X_ov = X_C * K2;
  250. const double X_nonov = nedges * X_ov * 2.0 / (nnodes * (nnodes - 1));
  251. #ifdef DEBUG
  252. if (Verbose) {
  253. prIndent();
  254. fprintf(stderr, "try %d (%d): %d overlaps on ", try, tries, ov);
  255. pr2graphs(g,GORIG(agroot(g)));
  256. fprintf(stderr," \n");
  257. }
  258. #endif
  259. for (int i = 0; i < X_loopcnt; i++) {
  260. const double temp = cool(i);
  261. if (temp <= 0.0)
  262. break;
  263. ov = adjust(g, temp, X_ov, X_nonov);
  264. if (ov == 0)
  265. break;
  266. }
  267. xpms.K += K; /* increase distance */
  268. }
  269. #ifdef DEBUG
  270. if (Verbose && ov)
  271. fprintf(stderr, "Warning: %d overlaps remain on ", ov);
  272. pr2graphs(g,GORIG(agroot(g)));
  273. fprintf(stderr,"\n");
  274. #endif
  275. return ov;
  276. }
  277. /* fdp_xLayout:
  278. * Use overlap parameter to determine if and how to remove overlaps.
  279. * In addition to the usual values accepted by removeOverlap, overlap
  280. * can begin with "n:" to indicate the given number of tries using
  281. * x_layout to remove overlaps.
  282. * Thus,
  283. * NULL or "" => dflt overlap
  284. * "mode" => "0:mode", i.e. removeOverlap with mode only
  285. * "true" => "0:true", i.e., no overlap removal
  286. * "n:" => n tries only
  287. * "n:mode" => n tries, then removeOverlap with mode
  288. * "0:" => no overlap removal
  289. */
  290. void fdp_xLayout(graph_t * g, xparams * xpms)
  291. {
  292. int tries;
  293. char* ovlp = agget (g, "overlap");
  294. char* cp;
  295. char* rest;
  296. if (Verbose) {
  297. #ifdef DEBUG
  298. prIndent();
  299. #endif
  300. fprintf (stderr, "xLayout ");
  301. }
  302. if (!ovlp || *ovlp == '\0') {
  303. ovlp = DFLT_overlap;
  304. }
  305. /* look for optional ":" or "number:" */
  306. if ((cp = strchr(ovlp, ':')) && (cp == ovlp || gv_isdigit(*ovlp))) {
  307. cp++;
  308. rest = cp;
  309. tries = atoi (ovlp);
  310. if (tries < 0) tries = 0;
  311. }
  312. else {
  313. tries = 0;
  314. rest = ovlp;
  315. }
  316. if (Verbose) {
  317. #ifdef DEBUG
  318. prIndent();
  319. #endif
  320. fprintf (stderr, "tries = %d, mode = %s\n", tries, rest);
  321. }
  322. if (tries && !x_layout(g, xpms, tries))
  323. return;
  324. removeOverlapAs(g, rest);
  325. }