stuff.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  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 <math.h>
  12. #include <neatogen/neato.h>
  13. #include <neatogen/stress.h>
  14. #include <stdlib.h>
  15. #include <time.h>
  16. #include <util/alloc.h>
  17. #ifndef _WIN32
  18. #include <unistd.h>
  19. #endif
  20. static double Epsilon2;
  21. static Agnode_t *choose_node(graph_t *, int);
  22. static void make_spring(graph_t *, Agnode_t *, Agnode_t *, double);
  23. static void move_node(graph_t *, int, Agnode_t *);
  24. static double fpow32(double x)
  25. {
  26. x = sqrt(x);
  27. return x * x * x;
  28. }
  29. static double distvec(double *p0, double *p1, double *vec)
  30. {
  31. int k;
  32. double dist = 0.0;
  33. for (k = 0; k < Ndim; k++) {
  34. vec[k] = p0[k] - p1[k];
  35. dist += vec[k] * vec[k];
  36. }
  37. dist = sqrt(dist);
  38. return dist;
  39. }
  40. double **new_array(int m, int n, double ival)
  41. {
  42. int i, j;
  43. double **rv = gv_calloc(m, sizeof(double*));
  44. double *mem = gv_calloc(m * n, sizeof(double));
  45. for (i = 0; i < m; i++) {
  46. rv[i] = mem;
  47. mem += n;
  48. for (j = 0; j < n; j++)
  49. rv[i][j] = ival;
  50. }
  51. return rv;
  52. }
  53. void free_array(double **rv)
  54. {
  55. if (rv) {
  56. free(rv[0]);
  57. free(rv);
  58. }
  59. }
  60. static double ***new_3array(int m, int n, int p, double ival)
  61. {
  62. int i, j, k;
  63. double ***rv = gv_calloc(m + 1, sizeof(double**));
  64. for (i = 0; i < m; i++) {
  65. rv[i] = gv_calloc(n + 1, sizeof(double*));
  66. for (j = 0; j < n; j++) {
  67. rv[i][j] = gv_calloc(p, sizeof(double));
  68. for (k = 0; k < p; k++)
  69. rv[i][j][k] = ival;
  70. }
  71. rv[i][j] = NULL; /* NULL terminate so we can clean up */
  72. }
  73. rv[i] = NULL;
  74. return rv;
  75. }
  76. static void free_3array(double ***rv)
  77. {
  78. int i, j;
  79. if (rv) {
  80. for (i = 0; rv[i]; i++) {
  81. for (j = 0; rv[i][j]; j++)
  82. free(rv[i][j]);
  83. free(rv[i]);
  84. }
  85. free(rv);
  86. }
  87. }
  88. /* lenattr:
  89. * Return 1 if attribute not defined
  90. * Return 2 if attribute string bad
  91. */
  92. static int lenattr(edge_t* e, Agsym_t* index, double* val)
  93. {
  94. char* s;
  95. if (index == NULL)
  96. return 1;
  97. s = agxget(e, index);
  98. if (*s == '\0') return 1;
  99. if (sscanf(s, "%lf", val) < 1 || *val < 0 || (*val == 0 && !Nop)) {
  100. agwarningf("bad edge len \"%s\"", s);
  101. return 2;
  102. }
  103. else
  104. return 0;
  105. }
  106. /* degreeKind;
  107. * Returns degree of n ignoring loops and multiedges.
  108. * Returns 0, 1 or many (2)
  109. * For case of 1, returns other endpoint of edge.
  110. */
  111. static int degreeKind(graph_t * g, node_t * n, node_t ** op)
  112. {
  113. edge_t *ep;
  114. int deg = 0;
  115. node_t *other = NULL;
  116. for (ep = agfstedge(g, n); ep; ep = agnxtedge(g, ep, n)) {
  117. if (aghead(ep) == agtail(ep))
  118. continue; /* ignore loops */
  119. if (deg == 1) {
  120. if ((agtail(ep) == n && aghead(ep) == other) || /* ignore multiedge */
  121. (agtail(ep) == other && aghead(ep) == n))
  122. continue;
  123. return 2;
  124. } else { /* deg == 0 */
  125. if (agtail(ep) == n)
  126. other = aghead(ep);
  127. else
  128. other = agtail(ep);
  129. *op = other;
  130. deg++;
  131. }
  132. }
  133. return deg;
  134. }
  135. /* prune:
  136. * np is at end of a chain. If its degree is 0, remove it.
  137. * If its degree is 1, remove it and recurse.
  138. * If its degree > 1, stop.
  139. * The node next is the next node to be visited during iteration.
  140. * If it is equal to a node being deleted, set it to next one.
  141. * Delete from root graph, since G may be a connected component subgraph.
  142. * Return next.
  143. */
  144. static node_t *prune(graph_t * G, node_t * np, node_t * next)
  145. {
  146. node_t *other;
  147. int deg;
  148. while (np) {
  149. deg = degreeKind(G, np, &other);
  150. if (deg == 0) {
  151. if (next == np)
  152. next = agnxtnode(G, np);
  153. agdelete(G->root, np);
  154. np = 0;
  155. } else if (deg == 1) {
  156. if (next == np)
  157. next = agnxtnode(G, np);
  158. agdelete(G->root, np);
  159. np = other;
  160. } else
  161. np = 0;
  162. }
  163. return next;
  164. }
  165. static double setEdgeLen(graph_t * G, node_t * np, Agsym_t* lenx, double dfltlen)
  166. {
  167. edge_t *ep;
  168. double total_len = 0.0;
  169. double len;
  170. int err;
  171. for (ep = agfstout(G, np); ep; ep = agnxtout(G, ep)) {
  172. if ((err = lenattr(ep, lenx, &len))) {
  173. if (err == 2) agerr(AGPREV, " in %s - setting to %.02f\n", agnameof(G), dfltlen);
  174. len = dfltlen;
  175. }
  176. ED_dist(ep) = len;
  177. total_len += len;
  178. }
  179. return total_len;
  180. }
  181. /* scan_graph_mode:
  182. * Prepare the graph and data structures depending on the layout mode.
  183. * If Reduce is true, eliminate singletons and trees. Since G may be a
  184. * subgraph, we remove the nodes from the root graph.
  185. * Return the number of nodes in the reduced graph.
  186. */
  187. int scan_graph_mode(graph_t * G, int mode)
  188. {
  189. int i, nV, nE, deg;
  190. char *str;
  191. node_t *np, *xp, *other;
  192. double total_len = 0.0;
  193. double dfltlen = 1.0;
  194. Agsym_t* lenx;
  195. if (Verbose)
  196. fprintf(stderr, "Scanning graph %s, %d nodes\n", agnameof(G),
  197. agnnodes(G));
  198. /* Eliminate singletons and trees */
  199. if (Reduce) {
  200. for (np = agfstnode(G); np; np = xp) {
  201. xp = agnxtnode(G, np);
  202. deg = degreeKind(G, np, &other);
  203. if (deg == 0) { /* singleton node */
  204. agdelete(G->root, np);
  205. } else if (deg == 1) {
  206. agdelete(G->root, np);
  207. xp = prune(G, other, xp);
  208. }
  209. }
  210. }
  211. nV = agnnodes(G);
  212. nE = agnedges(G);
  213. lenx = agattr(G, AGEDGE, "len", 0);
  214. if (mode == MODE_KK) {
  215. Epsilon = .0001 * nV;
  216. getdouble(G, "epsilon", &Epsilon);
  217. if ((str = agget(G->root, "Damping")))
  218. Damping = atof(str);
  219. else
  220. Damping = .99;
  221. GD_neato_nlist(G) = gv_calloc(nV + 1, sizeof(node_t*));
  222. for (i = 0, np = agfstnode(G); np; np = agnxtnode(G, np)) {
  223. GD_neato_nlist(G)[i] = np;
  224. ND_id(np) = i++;
  225. ND_heapindex(np) = -1;
  226. total_len += setEdgeLen(G, np, lenx, dfltlen);
  227. }
  228. } else if (mode == MODE_SGD) {
  229. Epsilon = .01;
  230. getdouble(G, "epsilon", &Epsilon);
  231. GD_neato_nlist(G) = gv_calloc(nV + 1, sizeof(node_t*)); // not sure why but sometimes needs the + 1
  232. for (i = 0, np = agfstnode(G); np; np = agnxtnode(G, np)) {
  233. GD_neato_nlist(G)[i] = np;
  234. ND_id(np) = i++;
  235. total_len += setEdgeLen(G, np, lenx, dfltlen);
  236. }
  237. } else {
  238. Epsilon = DFLT_TOLERANCE;
  239. getdouble(G, "epsilon", &Epsilon);
  240. for (i = 0, np = agfstnode(G); np; np = agnxtnode(G, np)) {
  241. ND_id(np) = i++;
  242. total_len += setEdgeLen(G, np, lenx, dfltlen);
  243. }
  244. }
  245. str = agget(G, "defaultdist");
  246. if (str && str[0])
  247. Initial_dist = fmax(Epsilon, atof(str));
  248. else
  249. Initial_dist = total_len / (nE > 0 ? nE : 1) * sqrt(nV) + 1;
  250. if (!Nop && mode == MODE_KK) {
  251. GD_dist(G) = new_array(nV, nV, Initial_dist);
  252. GD_spring(G) = new_array(nV, nV, 1.0);
  253. GD_sum_t(G) = new_array(nV, Ndim, 1.0);
  254. GD_t(G) = new_3array(nV, nV, Ndim, 0.0);
  255. }
  256. return nV;
  257. }
  258. int scan_graph(graph_t * g)
  259. {
  260. return scan_graph_mode(g, MODE_KK);
  261. }
  262. void free_scan_graph(graph_t * g)
  263. {
  264. free(GD_neato_nlist(g));
  265. if (!Nop) {
  266. free_array(GD_dist(g));
  267. free_array(GD_spring(g));
  268. free_array(GD_sum_t(g));
  269. free_3array(GD_t(g));
  270. GD_t(g) = NULL;
  271. }
  272. }
  273. void jitter_d(node_t * np, int nG, int n)
  274. {
  275. int k;
  276. for (k = n; k < Ndim; k++)
  277. ND_pos(np)[k] = nG * drand48();
  278. }
  279. void jitter3d(node_t * np, int nG)
  280. {
  281. jitter_d(np, nG, 2);
  282. }
  283. void randompos(node_t * np, int nG)
  284. {
  285. ND_pos(np)[0] = nG * drand48();
  286. ND_pos(np)[1] = nG * drand48();
  287. if (Ndim > 2)
  288. jitter3d(np, nG);
  289. }
  290. void initial_positions(graph_t * G, int nG)
  291. {
  292. int init, i;
  293. node_t *np;
  294. static int once = 0;
  295. if (Verbose)
  296. fprintf(stderr, "Setting initial positions\n");
  297. init = checkStart(G, nG, INIT_RANDOM);
  298. if (init == INIT_REGULAR)
  299. return;
  300. if (init == INIT_SELF && once == 0) {
  301. agwarningf("start=0 not supported with mode=self - ignored\n");
  302. once = 1;
  303. }
  304. for (i = 0; (np = GD_neato_nlist(G)[i]); i++) {
  305. if (hasPos(np))
  306. continue;
  307. randompos(np, 1);
  308. }
  309. }
  310. void diffeq_model(graph_t * G, int nG)
  311. {
  312. int i, j, k;
  313. double dist, **D, **K, del[MAXDIM], f;
  314. node_t *vi, *vj;
  315. edge_t *e;
  316. if (Verbose) {
  317. fprintf(stderr, "Setting up spring model: ");
  318. start_timer();
  319. }
  320. /* init springs */
  321. K = GD_spring(G);
  322. D = GD_dist(G);
  323. for (i = 0; i < nG; i++) {
  324. for (j = 0; j < i; j++) {
  325. f = Spring_coeff / (D[i][j] * D[i][j]);
  326. if ((e = agfindedge(G, GD_neato_nlist(G)[i], GD_neato_nlist(G)[j])))
  327. f = f * ED_factor(e);
  328. K[i][j] = K[j][i] = f;
  329. }
  330. }
  331. /* init differential equation solver */
  332. for (i = 0; i < nG; i++)
  333. for (k = 0; k < Ndim; k++)
  334. GD_sum_t(G)[i][k] = 0.0;
  335. for (i = 0; (vi = GD_neato_nlist(G)[i]); i++) {
  336. for (j = 0; j < nG; j++) {
  337. if (i == j)
  338. continue;
  339. vj = GD_neato_nlist(G)[j];
  340. dist = distvec(ND_pos(vi), ND_pos(vj), del);
  341. for (k = 0; k < Ndim; k++) {
  342. GD_t(G)[i][j][k] =
  343. GD_spring(G)[i][j] * (del[k] -
  344. GD_dist(G)[i][j] * del[k] /
  345. dist);
  346. GD_sum_t(G)[i][k] += GD_t(G)[i][j][k];
  347. }
  348. }
  349. }
  350. if (Verbose) {
  351. fprintf(stderr, "%.2f sec\n", elapsed_sec());
  352. }
  353. }
  354. /* total_e:
  355. * Return 2*energy of system.
  356. */
  357. static double total_e(graph_t * G, int nG)
  358. {
  359. int i, j, d;
  360. double e = 0.0; /* 2*energy */
  361. double t0; /* distance squared */
  362. double t1;
  363. node_t *ip, *jp;
  364. for (i = 0; i < nG - 1; i++) {
  365. ip = GD_neato_nlist(G)[i];
  366. for (j = i + 1; j < nG; j++) {
  367. jp = GD_neato_nlist(G)[j];
  368. for (t0 = 0.0, d = 0; d < Ndim; d++) {
  369. t1 = ND_pos(ip)[d] - ND_pos(jp)[d];
  370. t0 += t1 * t1;
  371. }
  372. e = e + GD_spring(G)[i][j] *
  373. (t0 + GD_dist(G)[i][j] * GD_dist(G)[i][j]
  374. - 2.0 * GD_dist(G)[i][j] * sqrt(t0));
  375. }
  376. }
  377. return e;
  378. }
  379. void solve_model(graph_t * G, int nG)
  380. {
  381. node_t *np;
  382. Epsilon2 = Epsilon * Epsilon;
  383. while ((np = choose_node(G, nG))) {
  384. move_node(G, nG, np);
  385. }
  386. if (Verbose) {
  387. fprintf(stderr, "\nfinal e = %f", total_e(G, nG));
  388. fprintf(stderr, " %d%s iterations %.2f sec\n",
  389. GD_move(G), GD_move(G) == MaxIter ? "!" : "",
  390. elapsed_sec());
  391. }
  392. if (GD_move(G) == MaxIter)
  393. agwarningf("Max. iterations (%d) reached on graph %s\n",
  394. MaxIter, agnameof(G));
  395. }
  396. static void update_arrays(graph_t * G, int nG, int i)
  397. {
  398. int j, k;
  399. double del[MAXDIM], dist, old;
  400. node_t *vi, *vj;
  401. vi = GD_neato_nlist(G)[i];
  402. for (k = 0; k < Ndim; k++)
  403. GD_sum_t(G)[i][k] = 0.0;
  404. for (j = 0; j < nG; j++) {
  405. if (i == j)
  406. continue;
  407. vj = GD_neato_nlist(G)[j];
  408. dist = distvec(ND_pos(vi), ND_pos(vj), del);
  409. for (k = 0; k < Ndim; k++) {
  410. old = GD_t(G)[i][j][k];
  411. GD_t(G)[i][j][k] =
  412. GD_spring(G)[i][j] * (del[k] -
  413. GD_dist(G)[i][j] * del[k] / dist);
  414. GD_sum_t(G)[i][k] += GD_t(G)[i][j][k];
  415. old = GD_t(G)[j][i][k];
  416. GD_t(G)[j][i][k] = -GD_t(G)[i][j][k];
  417. GD_sum_t(G)[j][k] += GD_t(G)[j][i][k] - old;
  418. }
  419. }
  420. }
  421. #define Msub(i,j) M[(i)*Ndim+(j)]
  422. static void D2E(graph_t * G, int nG, int n, double *M)
  423. {
  424. int i, l, k;
  425. node_t *vi, *vn;
  426. double scale, sq, t[MAXDIM];
  427. double **K = GD_spring(G);
  428. double **D = GD_dist(G);
  429. vn = GD_neato_nlist(G)[n];
  430. for (l = 0; l < Ndim; l++)
  431. for (k = 0; k < Ndim; k++)
  432. Msub(l, k) = 0.0;
  433. for (i = 0; i < nG; i++) {
  434. if (n == i)
  435. continue;
  436. vi = GD_neato_nlist(G)[i];
  437. sq = 0.0;
  438. for (k = 0; k < Ndim; k++) {
  439. t[k] = ND_pos(vn)[k] - ND_pos(vi)[k];
  440. sq += (t[k] * t[k]);
  441. }
  442. scale = 1 / fpow32(sq);
  443. for (k = 0; k < Ndim; k++) {
  444. for (l = 0; l < k; l++)
  445. Msub(l, k) += K[n][i] * D[n][i] * t[k] * t[l] * scale;
  446. Msub(k, k) +=
  447. K[n][i] * (1.0 - D[n][i] * (sq - t[k] * t[k]) * scale);
  448. }
  449. }
  450. for (k = 1; k < Ndim; k++)
  451. for (l = 0; l < k; l++)
  452. Msub(k, l) = Msub(l, k);
  453. }
  454. node_t *choose_node(graph_t * G, int nG)
  455. {
  456. int i, k;
  457. double m, max;
  458. node_t *choice, *np;
  459. static int cnt = 0;
  460. cnt++;
  461. if (GD_move(G) >= MaxIter)
  462. return NULL;
  463. max = 0.0;
  464. choice = NULL;
  465. for (i = 0; i < nG; i++) {
  466. np = GD_neato_nlist(G)[i];
  467. if (ND_pinned(np) > P_SET)
  468. continue;
  469. for (m = 0.0, k = 0; k < Ndim; k++)
  470. m += GD_sum_t(G)[i][k] * GD_sum_t(G)[i][k];
  471. /* could set the color=energy of the node here */
  472. if (m > max) {
  473. choice = np;
  474. max = m;
  475. }
  476. }
  477. if (max < Epsilon2)
  478. choice = NULL;
  479. else {
  480. if (Verbose && cnt % 100 == 0) {
  481. fprintf(stderr, "%.3f ", sqrt(max));
  482. if (cnt % 1000 == 0)
  483. fprintf(stderr, "\n");
  484. }
  485. }
  486. return choice;
  487. }
  488. void move_node(graph_t * G, int nG, node_t * n)
  489. {
  490. int i, m;
  491. double b[MAXDIM] = {0};
  492. double c[MAXDIM] = {0};
  493. m = ND_id(n);
  494. double *a = gv_calloc((size_t)Ndim * Ndim, sizeof(double));
  495. D2E(G, nG, m, a);
  496. for (i = 0; i < Ndim; i++)
  497. c[i] = -GD_sum_t(G)[m][i];
  498. solve(a, b, c, Ndim);
  499. for (i = 0; i < Ndim; i++) {
  500. b[i] = (Damping + 2 * (1 - Damping) * drand48()) * b[i];
  501. ND_pos(n)[i] += b[i];
  502. }
  503. GD_move(G)++;
  504. update_arrays(G, nG, m);
  505. if (test_toggle()) {
  506. double sum = 0;
  507. for (i = 0; i < Ndim; i++) {
  508. sum += fabs(b[i]);
  509. } /* Why not squared? */
  510. sum = sqrt(sum);
  511. fprintf(stderr, "%s %.3f\n", agnameof(n), sum);
  512. }
  513. free(a);
  514. }
  515. static node_t **Heap;
  516. static int Heapsize;
  517. static node_t *Src;
  518. static void heapup(node_t * v)
  519. {
  520. int i, par;
  521. node_t *u;
  522. for (i = ND_heapindex(v); i > 0; i = par) {
  523. par = (i - 1) / 2;
  524. u = Heap[par];
  525. if (ND_dist(u) <= ND_dist(v))
  526. break;
  527. Heap[par] = v;
  528. ND_heapindex(v) = par;
  529. Heap[i] = u;
  530. ND_heapindex(u) = i;
  531. }
  532. }
  533. static void heapdown(node_t * v)
  534. {
  535. int i, left, right, c;
  536. node_t *u;
  537. i = ND_heapindex(v);
  538. while ((left = 2 * i + 1) < Heapsize) {
  539. right = left + 1;
  540. if ((right < Heapsize)
  541. && (ND_dist(Heap[right]) < ND_dist(Heap[left])))
  542. c = right;
  543. else
  544. c = left;
  545. u = Heap[c];
  546. if (ND_dist(v) <= ND_dist(u))
  547. break;
  548. Heap[c] = v;
  549. ND_heapindex(v) = c;
  550. Heap[i] = u;
  551. ND_heapindex(u) = i;
  552. i = c;
  553. }
  554. }
  555. void neato_enqueue(node_t * v)
  556. {
  557. int i;
  558. assert(ND_heapindex(v) < 0);
  559. i = Heapsize++;
  560. ND_heapindex(v) = i;
  561. Heap[i] = v;
  562. if (i > 0)
  563. heapup(v);
  564. }
  565. node_t *neato_dequeue(void)
  566. {
  567. int i;
  568. node_t *rv, *v;
  569. if (Heapsize == 0)
  570. return NULL;
  571. rv = Heap[0];
  572. i = --Heapsize;
  573. v = Heap[i];
  574. Heap[0] = v;
  575. ND_heapindex(v) = 0;
  576. if (i > 1)
  577. heapdown(v);
  578. ND_heapindex(rv) = -1;
  579. return rv;
  580. }
  581. void shortest_path(graph_t * G, int nG)
  582. {
  583. node_t *v;
  584. Heap = gv_calloc(nG + 1, sizeof(node_t*));
  585. if (Verbose) {
  586. fprintf(stderr, "Calculating shortest paths: ");
  587. start_timer();
  588. }
  589. for (v = agfstnode(G); v; v = agnxtnode(G, v))
  590. s1(G, v);
  591. if (Verbose) {
  592. fprintf(stderr, "%.2f sec\n", elapsed_sec());
  593. }
  594. free(Heap);
  595. }
  596. void s1(graph_t * G, node_t * node)
  597. {
  598. node_t *v, *u;
  599. edge_t *e;
  600. int t;
  601. double f;
  602. for (t = 0; (v = GD_neato_nlist(G)[t]); t++)
  603. ND_dist(v) = Initial_dist;
  604. Src = node;
  605. ND_dist(Src) = 0;
  606. ND_hops(Src) = 0;
  607. neato_enqueue(Src);
  608. while ((v = neato_dequeue())) {
  609. if (v != Src)
  610. make_spring(G, Src, v, ND_dist(v));
  611. for (e = agfstedge(G, v); e; e = agnxtedge(G, e, v)) {
  612. if ((u = agtail(e)) == v)
  613. u = aghead(e);
  614. f = ND_dist(v) + ED_dist(e);
  615. if (ND_dist(u) > f) {
  616. ND_dist(u) = f;
  617. if (ND_heapindex(u) >= 0)
  618. heapup(u);
  619. else {
  620. ND_hops(u) = ND_hops(v) + 1;
  621. neato_enqueue(u);
  622. }
  623. }
  624. }
  625. }
  626. }
  627. void make_spring(graph_t * G, node_t * u, node_t * v, double f)
  628. {
  629. int i, j;
  630. i = ND_id(u);
  631. j = ND_id(v);
  632. GD_dist(G)[i][j] = GD_dist(G)[j][i] = f;
  633. }