tlayout.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  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. /* tlayout.c:
  11. * Written by Emden R. Gansner
  12. *
  13. * Module for initial layout, using point nodes and ports.
  14. *
  15. * Note: If interior nodes are not connected, they tend to fly apart,
  16. * despite being tied to port nodes. This occurs because, as initially
  17. * coded, as the nodes tend to straighten into a line, the radius
  18. * grows causing more expansion. Is the problem really here and not
  19. * with disconnected nodes in xlayout? If here, we can either forbid
  20. * expansion or eliminate repulsion between nodes only connected
  21. * via port nodes.
  22. */
  23. #include "config.h"
  24. /* uses PRIVATE interface */
  25. #define FDP_PRIVATE 1
  26. #include <sys/types.h>
  27. #include <math.h>
  28. #include <stdlib.h>
  29. #include <time.h>
  30. #ifndef _WIN32
  31. #include <unistd.h>
  32. #endif
  33. #include <ctype.h>
  34. #include <fdpgen/dbg.h>
  35. #include <fdpgen/grid.h>
  36. #include <neatogen/neato.h>
  37. #ifndef HAVE_SRAND48
  38. #define srand48 srand
  39. #endif
  40. #include <fdpgen/tlayout.h>
  41. #include <common/globals.h>
  42. #define D_useGrid (fdp_parms->useGrid)
  43. #define D_useNew (fdp_parms->useNew)
  44. #define D_numIters (fdp_parms->numIters)
  45. #define D_unscaled (fdp_parms->unscaled)
  46. #define D_C (fdp_parms->C)
  47. #define D_Tfact (fdp_parms->Tfact)
  48. #define D_K (fdp_parms->K)
  49. #define D_T0 (fdp_parms->T0)
  50. /* Actual parameters used; initialized using fdp_parms, then possibly
  51. * updated with graph-specific values.
  52. */
  53. typedef struct {
  54. int useGrid; /* use grid for speed up */
  55. int useNew; /* encode x-K into attractive force */
  56. long seed; /* seed for position RNG */
  57. int numIters; /* actual iterations in layout */
  58. int maxIters; /* max iterations in layout */
  59. int unscaled; /* % of iterations used in pass 1 */
  60. double C; /* Repulsion factor in xLayout */
  61. double Tfact; /* scale temp from default expression */
  62. double K; /* spring constant; ideal distance */
  63. double T0; /* initial temperature */
  64. int smode; /* seed mode */
  65. double Cell; /* grid cell size */
  66. double Wd; /* half-width of boundary */
  67. double Ht; /* half-height of boundary */
  68. int pass1; /* iterations used in pass 1 */
  69. int loopcnt; /* actual iterations in this pass */
  70. } parms_t;
  71. static parms_t parms;
  72. #define T_useGrid (parms.useGrid)
  73. #define T_useNew (parms.useNew)
  74. #define T_seed (parms.seed)
  75. #define T_numIters (parms.numIters)
  76. #define T_maxIters (parms.maxIters)
  77. #define T_unscaled (parms.unscaled)
  78. #define T_C (parms.C)
  79. #define T_Tfact (parms.Tfact)
  80. #define T_K (parms.K)
  81. #define T_T0 (parms.T0)
  82. #define T_smode (parms.smode)
  83. #define T_Cell (parms.Cell)
  84. #define T_Wd (parms.Wd)
  85. #define T_Ht (parms.Ht)
  86. #define T_pass1 (parms.pass1)
  87. #define T_loopcnt (parms.loopcnt)
  88. #define EXPFACTOR 1.2
  89. #define DFLT_maxIters 600
  90. #define DFLT_K 0.3
  91. #define DFLT_Cell 0.0
  92. #define DFLT_seed 1
  93. #define DFLT_smode INIT_RANDOM
  94. static double cool(int t)
  95. {
  96. return T_T0 * (T_maxIters - t) / T_maxIters;
  97. }
  98. /* reset_params:
  99. */
  100. static void reset_params(void)
  101. {
  102. T_T0 = -1.0;
  103. }
  104. /* init_params:
  105. * Set parameters for expansion phase based on initial
  106. * layout parameters. If T0 is not set, we set it here
  107. * based on the size of the graph. In this case, we
  108. * return 1, so that fdp_tLayout can unset T0, to be
  109. * reset by a recursive call to fdp_tLayout.
  110. */
  111. static int init_params(graph_t * g, xparams * xpms)
  112. {
  113. int ret = 0;
  114. if (T_T0 == -1.0) {
  115. int nnodes = agnnodes(g);
  116. T_T0 = T_Tfact * T_K * sqrt(nnodes) / 5;
  117. #ifdef DEBUG
  118. if (Verbose) {
  119. prIndent();
  120. fprintf(stderr, "tlayout %s", agnameof(g));
  121. fprintf(stderr, "(%s) : T0 %f\n", agnameof(GORIG(g->root)), T_T0);
  122. }
  123. #endif
  124. ret = 1;
  125. }
  126. xpms->T0 = cool(T_pass1);
  127. xpms->K = T_K;
  128. xpms->C = T_C;
  129. xpms->numIters = T_maxIters - T_pass1;
  130. if (T_numIters >= 0) {
  131. if (T_numIters <= T_pass1) {
  132. T_loopcnt = T_numIters;
  133. xpms->loopcnt = 0;
  134. } else if (T_numIters <= T_maxIters) {
  135. T_loopcnt = T_pass1;
  136. xpms->loopcnt = T_numIters - T_pass1;
  137. }
  138. } else {
  139. T_loopcnt = T_pass1;
  140. xpms->loopcnt = xpms->numIters;
  141. }
  142. return ret;
  143. }
  144. /* fdp_initParams:
  145. * Initialize parameters based on root graph attributes.
  146. */
  147. void fdp_initParams(graph_t * g)
  148. {
  149. T_useGrid = D_useGrid;
  150. T_useNew = D_useNew;
  151. T_numIters = D_numIters;
  152. T_unscaled = D_unscaled;
  153. T_Cell = DFLT_Cell;
  154. T_C = D_C;
  155. T_Tfact = D_Tfact;
  156. T_maxIters = late_int(g, agattr(g,AGRAPH, "maxiter", NULL), DFLT_maxIters, 0);
  157. D_K = T_K = late_double(g, agattr(g,AGRAPH, "K", NULL), DFLT_K, 0.0);
  158. if (D_T0 == -1.0) {
  159. T_T0 = late_double(g, agattr(g,AGRAPH, "T0", NULL), -1.0, 0.0);
  160. } else
  161. T_T0 = D_T0;
  162. T_seed = DFLT_seed;
  163. T_smode = setSeed (g, DFLT_smode, &T_seed);
  164. if (T_smode == INIT_SELF) {
  165. agwarningf("fdp does not support start=self - ignoring\n");
  166. T_seed = DFLT_smode;
  167. }
  168. T_pass1 = T_unscaled * T_maxIters / 100;
  169. if (T_useGrid) {
  170. if (T_Cell <= 0.0)
  171. T_Cell = 3 * T_K;
  172. }
  173. #ifdef DEBUG
  174. if (Verbose) {
  175. prIndent();
  176. fprintf(stderr,
  177. "Params %s : K %f T0 %f Tfact %f maxIters %d unscaled %d\n",
  178. agnameof(g),
  179. T_K, T_T0, T_Tfact, T_maxIters, T_unscaled);
  180. }
  181. #endif
  182. }
  183. static void
  184. doRep(node_t * p, node_t * q, double xdelta, double ydelta, double dist2)
  185. {
  186. double force;
  187. double dist;
  188. while (dist2 == 0.0) {
  189. xdelta = 5 - rand() % 10;
  190. ydelta = 5 - rand() % 10;
  191. dist2 = xdelta * xdelta + ydelta * ydelta;
  192. }
  193. if (T_useNew) {
  194. dist = sqrt(dist2);
  195. force = T_K * T_K / (dist * dist2);
  196. } else
  197. force = T_K * T_K / dist2;
  198. if (IS_PORT(p) && IS_PORT(q))
  199. force *= 10.0;
  200. DISP(q)[0] += xdelta * force;
  201. DISP(q)[1] += ydelta * force;
  202. DISP(p)[0] -= xdelta * force;
  203. DISP(p)[1] -= ydelta * force;
  204. }
  205. /* applyRep:
  206. * Repulsive force = (K*K)/d
  207. * or K*K/d*d
  208. */
  209. static void applyRep(Agnode_t * p, Agnode_t * q)
  210. {
  211. double xdelta, ydelta;
  212. xdelta = ND_pos(q)[0] - ND_pos(p)[0];
  213. ydelta = ND_pos(q)[1] - ND_pos(p)[1];
  214. doRep(p, q, xdelta, ydelta, xdelta * xdelta + ydelta * ydelta);
  215. }
  216. static void doNeighbor(Grid * grid, int i, int j, node_list * nodes)
  217. {
  218. cell *cellp = findGrid(grid, i, j);
  219. node_list *qs;
  220. Agnode_t *p;
  221. Agnode_t *q;
  222. double xdelta, ydelta;
  223. double dist2;
  224. if (cellp) {
  225. #ifdef DEBUG
  226. if (Verbose >= 3) {
  227. prIndent();
  228. fprintf(stderr, " doNeighbor (%d,%d) : %d\n", i, j,
  229. gLength(cellp));
  230. }
  231. #endif
  232. for (; nodes != 0; nodes = nodes->next) {
  233. p = nodes->node;
  234. for (qs = cellp->nodes; qs != 0; qs = qs->next) {
  235. q = qs->node;
  236. xdelta = (ND_pos(q))[0] - (ND_pos(p))[0];
  237. ydelta = (ND_pos(q))[1] - (ND_pos(p))[1];
  238. dist2 = xdelta * xdelta + ydelta * ydelta;
  239. if (dist2 < T_Cell * T_Cell)
  240. doRep(p, q, xdelta, ydelta, dist2);
  241. }
  242. }
  243. }
  244. }
  245. static int gridRepulse(cell *cellp, Grid *grid) {
  246. node_list *nodes = cellp->nodes;
  247. int i = cellp->p.i;
  248. int j = cellp->p.j;
  249. node_list *p;
  250. node_list *q;
  251. #ifdef DEBUG
  252. if (Verbose >= 3) {
  253. prIndent();
  254. fprintf(stderr, "gridRepulse (%d,%d) : %d\n", i, j,
  255. gLength(cellp));
  256. }
  257. #endif
  258. for (p = nodes; p != 0; p = p->next) {
  259. for (q = nodes; q != 0; q = q->next)
  260. if (p != q)
  261. applyRep(p->node, q->node);
  262. }
  263. doNeighbor(grid, i - 1, j - 1, nodes);
  264. doNeighbor(grid, i - 1, j, nodes);
  265. doNeighbor(grid, i - 1, j + 1, nodes);
  266. doNeighbor(grid, i, j - 1, nodes);
  267. doNeighbor(grid, i, j + 1, nodes);
  268. doNeighbor(grid, i + 1, j - 1, nodes);
  269. doNeighbor(grid, i + 1, j, nodes);
  270. doNeighbor(grid, i + 1, j + 1, nodes);
  271. return 0;
  272. }
  273. /* applyAttr:
  274. * Attractive force = weight*(d*d)/K
  275. * or force = (d - L(e))*weight(e)
  276. */
  277. static void applyAttr(Agnode_t * p, Agnode_t * q, Agedge_t * e)
  278. {
  279. double xdelta, ydelta;
  280. double force;
  281. double dist;
  282. double dist2;
  283. xdelta = ND_pos(q)[0] - ND_pos(p)[0];
  284. ydelta = ND_pos(q)[1] - ND_pos(p)[1];
  285. dist2 = xdelta * xdelta + ydelta * ydelta;
  286. while (dist2 == 0.0) {
  287. xdelta = 5 - rand() % 10;
  288. ydelta = 5 - rand() % 10;
  289. dist2 = xdelta * xdelta + ydelta * ydelta;
  290. }
  291. dist = sqrt(dist2);
  292. if (T_useNew)
  293. force = ED_factor(e) * (dist - ED_dist(e)) / dist;
  294. else
  295. force = ED_factor(e) * dist / ED_dist(e);
  296. DISP(q)[0] -= xdelta * force;
  297. DISP(q)[1] -= ydelta * force;
  298. DISP(p)[0] += xdelta * force;
  299. DISP(p)[1] += ydelta * force;
  300. }
  301. static void updatePos(Agraph_t * g, double temp, bport_t * pp)
  302. {
  303. Agnode_t *n;
  304. double temp2;
  305. double len2;
  306. double x, y, d;
  307. double dx, dy;
  308. temp2 = temp * temp;
  309. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  310. if (ND_pinned(n) & P_FIX)
  311. continue;
  312. dx = DISP(n)[0];
  313. dy = DISP(n)[1];
  314. len2 = dx * dx + dy * dy;
  315. /* limit by temperature */
  316. if (len2 < temp2) {
  317. x = ND_pos(n)[0] + dx;
  318. y = ND_pos(n)[1] + dy;
  319. } else {
  320. double fact = temp / sqrt(len2);
  321. x = ND_pos(n)[0] + dx * fact;
  322. y = ND_pos(n)[1] + dy * fact;
  323. }
  324. /* if ports, limit by boundary */
  325. if (pp) {
  326. d = sqrt(x * x / (T_Wd * T_Wd) + y * y / (T_Ht * T_Ht));
  327. if (IS_PORT(n)) {
  328. ND_pos(n)[0] = x / d;
  329. ND_pos(n)[1] = y / d;
  330. } else if (d >= 1.0) {
  331. ND_pos(n)[0] = 0.95 * x / d;
  332. ND_pos(n)[1] = 0.95 * y / d;
  333. } else {
  334. ND_pos(n)[0] = x;
  335. ND_pos(n)[1] = y;
  336. }
  337. } else {
  338. ND_pos(n)[0] = x;
  339. ND_pos(n)[1] = y;
  340. }
  341. }
  342. }
  343. #define FLOOR(d) ((int)floor(d))
  344. /* gAdjust:
  345. */
  346. static void gAdjust(Agraph_t * g, double temp, bport_t * pp, Grid * grid)
  347. {
  348. Agnode_t *n;
  349. Agedge_t *e;
  350. if (temp <= 0.0)
  351. return;
  352. clearGrid(grid);
  353. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  354. DISP(n)[0] = DISP(n)[1] = 0;
  355. addGrid(grid, FLOOR((ND_pos(n))[0] / T_Cell), FLOOR((ND_pos(n))[1] / T_Cell),
  356. n);
  357. }
  358. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  359. for (e = agfstout(g, n); e; e = agnxtout(g, e))
  360. if (n != aghead(e))
  361. applyAttr(n, aghead(e), e);
  362. }
  363. walkGrid(grid, gridRepulse);
  364. updatePos(g, temp, pp);
  365. }
  366. /* adjust:
  367. */
  368. static void adjust(Agraph_t * g, double temp, bport_t * pp)
  369. {
  370. Agnode_t *n;
  371. Agnode_t *n1;
  372. Agedge_t *e;
  373. if (temp <= 0.0)
  374. return;
  375. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  376. DISP(n)[0] = DISP(n)[1] = 0;
  377. }
  378. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  379. for (n1 = agnxtnode(g, n); n1; n1 = agnxtnode(g, n1)) {
  380. applyRep(n, n1);
  381. }
  382. for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
  383. if (n != aghead(e))
  384. applyAttr(n, aghead(e), e);
  385. }
  386. }
  387. updatePos(g, temp, pp);
  388. }
  389. /* initPositions:
  390. * Create initial layout of nodes
  391. * TODO :
  392. * Position nodes near neighbors with positions.
  393. * Use bbox to reset K.
  394. */
  395. static pointf initPositions(graph_t * g, bport_t * pp)
  396. {
  397. int nG = agnnodes(g) - NPORTS(g);
  398. double size;
  399. Agnode_t *np;
  400. int n_pos = 0; /* no. of nodes with position info */
  401. boxf bb = { {0, 0}, {0, 0} };
  402. pointf ctr; /* center of boundary ellipse */
  403. long local_seed;
  404. double PItimes2 = M_PI * 2.0;
  405. for (np = agfstnode(g); np; np = agnxtnode(g, np)) {
  406. if (ND_pinned(np)) {
  407. if (n_pos) {
  408. bb.LL.x = MIN(ND_pos(np)[0], bb.LL.x);
  409. bb.LL.y = MIN(ND_pos(np)[1], bb.LL.y);
  410. bb.UR.x = MAX(ND_pos(np)[0], bb.UR.x);
  411. bb.UR.y = MAX(ND_pos(np)[1], bb.UR.y);
  412. } else {
  413. bb.UR.x = bb.LL.x = ND_pos(np)[0];
  414. bb.UR.y = bb.LL.y = ND_pos(np)[1];
  415. }
  416. n_pos++;
  417. }
  418. }
  419. size = T_K * (sqrt((double) nG) + 1.0);
  420. T_Wd = T_Ht = EXPFACTOR * (size / 2.0);
  421. if (n_pos == 1) {
  422. ctr.x = bb.LL.x;
  423. ctr.y = bb.LL.y;
  424. } else if (n_pos > 1) {
  425. double alpha, area, width, height, quot;
  426. ctr.x = (bb.LL.x + bb.UR.x) / 2.0;
  427. ctr.y = (bb.LL.y + bb.UR.y) / 2.0;
  428. width = EXPFACTOR * (bb.UR.x - bb.LL.x);
  429. height = EXPFACTOR * (bb.UR.y - bb.LL.y);
  430. area = 4.0 * T_Wd * T_Ht;
  431. quot = width * height / area;
  432. if (quot >= 1.0) { /* If bbox has large enough area, use it */
  433. T_Wd = width / 2.0;
  434. T_Ht = height / 2.0;
  435. } else if (quot > 0.0) { /* else scale up to have enough area */
  436. quot = 2.0 * sqrt(quot);
  437. T_Wd = width / quot;
  438. T_Ht = height / quot;
  439. } else { /* either width or height is 0 */
  440. if (width > 0) {
  441. height = area / width;
  442. T_Wd = width / 2.0;
  443. T_Ht = height / 2.0;
  444. } else if (height > 0) {
  445. width = area / height;
  446. T_Wd = width / 2.0;
  447. T_Ht = height / 2.0;
  448. }
  449. /* If width = height = 0, use Wd and Ht as defined above for
  450. * the case the n_pos == 0.
  451. */
  452. }
  453. /* Construct enclosing ellipse */
  454. alpha = atan2(T_Ht, T_Wd);
  455. T_Wd = T_Wd / cos(alpha);
  456. T_Ht = T_Ht / sin(alpha);
  457. } else {
  458. ctr.x = ctr.y = 0;
  459. }
  460. /* Set seed value */
  461. if (T_smode == INIT_RANDOM)
  462. local_seed = T_seed;
  463. else {
  464. #if defined(_WIN32)
  465. local_seed = (long)time(NULL);
  466. #else
  467. local_seed = getpid() ^ time(NULL);
  468. #endif
  469. }
  470. srand48(local_seed);
  471. /* If ports, place ports on and nodes within an ellipse centered at origin
  472. * with halfwidth Wd and halfheight Ht.
  473. * If no ports, place nodes within a rectangle centered at origin
  474. * with halfwidth Wd and halfheight Ht. Nodes with a given position
  475. * are translated. Wd and Ht are set to contain all positioned points.
  476. * The reverse translation will be applied to all
  477. * nodes at the end of the layout.
  478. * TODO: place unfixed points using adjacent ports or fixed pts.
  479. */
  480. if (pp) {
  481. while (pp->e) { /* position ports on ellipse */
  482. np = pp->n;
  483. ND_pos(np)[0] = T_Wd * cos(pp->alpha) + ctr.x;
  484. ND_pos(np)[1] = T_Ht * sin(pp->alpha) + ctr.y;
  485. ND_pinned(np) = P_SET;
  486. pp++;
  487. }
  488. for (np = agfstnode(g); np; np = agnxtnode(g, np)) {
  489. if (IS_PORT(np))
  490. continue;
  491. if (ND_pinned(np)) {
  492. ND_pos(np)[0] -= ctr.x;
  493. ND_pos(np)[1] -= ctr.y;
  494. } else {
  495. pointf p = { 0.0, 0.0 };
  496. int cnt = 0;
  497. node_t *op;
  498. edge_t *ep;
  499. for (ep = agfstedge(g, np); ep; ep = agnxtedge(g, ep, np)) {
  500. if (aghead(ep) == agtail(ep))
  501. continue;
  502. op = aghead(ep) == np ? agtail(ep) : aghead(ep);
  503. if (!hasPos(op))
  504. continue;
  505. if (cnt) {
  506. p.x = (p.x * cnt + ND_pos(op)[0]) / (cnt + 1);
  507. p.y = (p.y * cnt + ND_pos(op)[1]) / (cnt + 1);
  508. } else {
  509. p.x = ND_pos(op)[0];
  510. p.y = ND_pos(op)[1];
  511. }
  512. cnt++;
  513. }
  514. if (cnt > 1) {
  515. ND_pos(np)[0] = p.x;
  516. ND_pos(np)[1] = p.y;
  517. } else if (cnt == 1) {
  518. ND_pos(np)[0] = 0.98 * p.x + 0.1 * ctr.x;
  519. ND_pos(np)[1] = 0.9 * p.y + 0.1 * ctr.y;
  520. } else {
  521. double angle = PItimes2 * drand48();
  522. double radius = 0.9 * drand48();
  523. ND_pos(np)[0] = radius * T_Wd * cos(angle);
  524. ND_pos(np)[1] = radius * T_Ht * sin(angle);
  525. }
  526. ND_pinned(np) = P_SET;
  527. }
  528. }
  529. } else {
  530. if (n_pos) { /* If positioned nodes */
  531. for (np = agfstnode(g); np; np = agnxtnode(g, np)) {
  532. if (ND_pinned(np)) {
  533. ND_pos(np)[0] -= ctr.x;
  534. ND_pos(np)[1] -= ctr.y;
  535. } else {
  536. ND_pos(np)[0] = T_Wd * (2.0 * drand48() - 1.0);
  537. ND_pos(np)[1] = T_Ht * (2.0 * drand48() - 1.0);
  538. }
  539. }
  540. } else { /* No ports or positions; place randomly */
  541. for (np = agfstnode(g); np; np = agnxtnode(g, np)) {
  542. ND_pos(np)[0] = T_Wd * (2.0 * drand48() - 1.0);
  543. ND_pos(np)[1] = T_Ht * (2.0 * drand48() - 1.0);
  544. }
  545. }
  546. }
  547. return ctr;
  548. }
  549. /* fdp_tLayout:
  550. * Given graph g with ports nodes, layout g respecting ports.
  551. * If some node have position information, it may be useful to
  552. * reset temperature and other parameters to reflect this.
  553. */
  554. void fdp_tLayout(graph_t * g, xparams * xpms)
  555. {
  556. int i;
  557. int reset;
  558. bport_t *pp = PORTS(g);
  559. double temp;
  560. Grid *grid;
  561. pointf ctr;
  562. Agnode_t *n;
  563. reset = init_params(g, xpms);
  564. temp = T_T0;
  565. ctr = initPositions(g, pp);
  566. if (T_useGrid) {
  567. grid = mkGrid(agnnodes(g));
  568. adjustGrid(grid, agnnodes(g));
  569. for (i = 0; i < T_loopcnt; i++) {
  570. temp = cool(i);
  571. gAdjust(g, temp, pp, grid);
  572. }
  573. delGrid(grid);
  574. } else {
  575. for (i = 0; i < T_loopcnt; i++) {
  576. temp = cool(i);
  577. adjust(g, temp, pp);
  578. }
  579. }
  580. if (ctr.x != 0.0 || ctr.y != 0.0) {
  581. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  582. ND_pos(n)[0] += ctr.x;
  583. ND_pos(n)[1] += ctr.y;
  584. }
  585. }
  586. if (reset)
  587. reset_params();
  588. }