Graphviz  2.31.20130618.0446
lib/neatogen/stuff.c
Go to the documentation of this file.
00001 /* $Id$ $Revision$ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /*************************************************************************
00005  * Copyright (c) 2011 AT&T Intellectual Property 
00006  * All rights reserved. This program and the accompanying materials
00007  * are made available under the terms of the Eclipse Public License v1.0
00008  * which accompanies this distribution, and is available at
00009  * http://www.eclipse.org/legal/epl-v10.html
00010  *
00011  * Contributors: See CVS logs. Details at http://www.graphviz.org/
00012  *************************************************************************/
00013 
00014 
00015 #ifdef HAVE_CONFIG_H
00016 #include "config.h"
00017 #endif
00018 
00019 #include        "neato.h"
00020 #include        "stress.h"
00021 #include        <time.h>
00022 #ifndef WIN32
00023 #include        <unistd.h>
00024 #endif
00025 
00026 static double Epsilon2;
00027 
00028 
00029 double fpow32(double x)
00030 {
00031     x = sqrt(x);
00032     return x * x * x;
00033 }
00034 
00035 double distvec(double *p0, double *p1, double *vec)
00036 {
00037     int k;
00038     double dist = 0.0;
00039 
00040     for (k = 0; k < Ndim; k++) {
00041         vec[k] = p0[k] - p1[k];
00042         dist += (vec[k] * vec[k]);
00043     }
00044     dist = sqrt(dist);
00045     return dist;
00046 }
00047 
00048 double **new_array(int m, int n, double ival)
00049 {
00050     double **rv;
00051     double *mem;
00052     int i, j;
00053 
00054     rv = N_NEW(m, double *);
00055     mem = N_NEW(m * n, double);
00056     for (i = 0; i < m; i++) {
00057         rv[i] = mem;
00058         mem = mem + n;
00059         for (j = 0; j < n; j++)
00060             rv[i][j] = ival;
00061     }
00062     return rv;
00063 }
00064 
00065 void free_array(double **rv)
00066 {
00067     if (rv) {
00068         free(rv[0]);
00069         free(rv);
00070     }
00071 }
00072 
00073 
00074 static double ***new_3array(int m, int n, int p, double ival)
00075 {
00076     double ***rv;
00077     int i, j, k;
00078 
00079     rv = N_NEW(m + 1, double **);
00080     for (i = 0; i < m; i++) {
00081         rv[i] = N_NEW(n + 1, double *);
00082         for (j = 0; j < n; j++) {
00083             rv[i][j] = N_NEW(p, double);
00084             for (k = 0; k < p; k++)
00085                 rv[i][j][k] = ival;
00086         }
00087         rv[i][j] = NULL;        /* NULL terminate so we can clean up */
00088     }
00089     rv[i] = NULL;
00090     return rv;
00091 }
00092 
00093 static void free_3array(double ***rv)
00094 {
00095     int i, j;
00096 
00097     if (rv) {
00098         for (i = 0; rv[i]; i++) {
00099             for (j = 0; rv[i][j]; j++)
00100                 free(rv[i][j]);
00101             free(rv[i]);
00102         }
00103         free(rv);
00104     }
00105 }
00106 
00107 
00108 /* lenattr:
00109  * Return 1 if attribute not defined
00110  * Return 2 if attribute string bad
00111  */
00112 #ifdef WITH_CGRAPH
00113 static int lenattr(edge_t* e, Agsym_t* index, double* val)
00114 #else
00115 static int lenattr(edge_t* e, int index, double* val)
00116 #endif
00117 {
00118     char* s;
00119 
00120 #ifdef WITH_CGRAPH
00121     if (index == NULL)
00122 #else
00123     if (index < 0)
00124 #endif
00125         return 1;
00126 
00127     s = agxget(e, index);
00128     if (*s == '\0') return 1;
00129 
00130     if ((sscanf(s, "%lf", val) < 1) || (*val < 0) || ((*val == 0) && !Nop)) {
00131         agerr(AGWARN, "bad edge len \"%s\"", s);
00132         return 2;
00133     }
00134     else
00135         return 0;
00136 }
00137 
00138 
00139 /* degreeKind;
00140  * Returns degree of n ignoring loops and multiedges.
00141  * Returns 0, 1 or many (2)
00142  * For case of 1, returns other endpoint of edge.
00143  */
00144 static int degreeKind(graph_t * g, node_t * n, node_t ** op)
00145 {
00146     edge_t *ep;
00147     int deg = 0;
00148     node_t *other = NULL;
00149 
00150     for (ep = agfstedge(g, n); ep; ep = agnxtedge(g, ep, n)) {
00151         if (aghead(ep) == agtail(ep))
00152             continue;           /* ignore loops */
00153         if (deg == 1) {
00154             if (((agtail(ep) == n) && (aghead(ep) == other)) || /* ignore multiedge */
00155                 ((agtail(ep) == other) && (aghead(ep) == n)))
00156                 continue;
00157             return 2;
00158         } else {                /* deg == 0 */
00159             if (agtail(ep) == n)
00160                 other = aghead(ep);
00161             else
00162                 other = agtail(ep);
00163             *op = other;
00164             deg++;
00165         }
00166     }
00167     return deg;
00168 }
00169 
00170 
00171 /* prune:
00172  * np is at end of a chain. If its degree is 0, remove it.
00173  * If its degree is 1, remove it and recurse.
00174  * If its degree > 1, stop.
00175  * The node next is the next node to be visited during iteration.
00176  * If it is equal to a node being deleted, set it to next one.
00177  * Delete from root graph, since G may be a connected component subgraph.
00178  * Return next.
00179  */
00180 static node_t *prune(graph_t * G, node_t * np, node_t * next)
00181 {
00182     node_t *other;
00183     int deg;
00184 
00185     while (np) {
00186         deg = degreeKind(G, np, &other);
00187         if (deg == 0) {
00188             if (next == np)
00189                 next = agnxtnode(G, np);
00190             agdelete(G->root, np);
00191             np = 0;
00192         } else if (deg == 1) {
00193             if (next == np)
00194                 next = agnxtnode(G, np);
00195             agdelete(G->root, np);
00196             np = other;
00197         } else
00198             np = 0;
00199 
00200     }
00201     return next;
00202 }
00203 
00204 #ifdef WITH_CGRAPH
00205 static double setEdgeLen(graph_t * G, node_t * np, Agsym_t* lenx, double dfltlen)
00206 #else
00207 static double setEdgeLen(graph_t * G, node_t * np, int lenx, double dfltlen)
00208 #endif
00209 {
00210     edge_t *ep;
00211     double total_len = 0.0;
00212     double len;
00213     int err;
00214 
00215     for (ep = agfstout(G, np); ep; ep = agnxtout(G, ep)) {
00216         if ((err = lenattr(ep, lenx, &len))) {
00217             if (err == 2) agerr(AGPREV, " in %s - setting to %.02f\n", agnameof(G), dfltlen);
00218             len = dfltlen;
00219         }
00220         ED_dist(ep) = len;
00221         total_len += len;
00222     }
00223     return total_len;
00224 }
00225 
00226 /* scan_graph_mode:
00227  * Prepare the graph and data structures depending on the layout mode.
00228  * If Reduce is true, eliminate singletons and trees. Since G may be a
00229  * subgraph, we remove the nodes from the root graph.
00230  * Return the number of nodes in the reduced graph.
00231  */
00232 int scan_graph_mode(graph_t * G, int mode)
00233 {
00234     int i, nV, nE, deg;
00235     char *str;
00236     node_t *np, *xp, *other;
00237     double total_len = 0.0;
00238     double dfltlen = 1.0;
00239 #ifdef WITH_CGRAPH
00240     Agsym_t* lenx;
00241 #else
00242     int lenx;
00243 #endif /* WITH_CGRAPH */
00244 
00245     if (Verbose)
00246         fprintf(stderr, "Scanning graph %s, %d nodes\n", agnameof(G),
00247                 agnnodes(G));
00248 
00249 
00250     /* Eliminate singletons and trees */
00251     if (Reduce) {
00252         for (np = agfstnode(G); np; np = xp) {
00253             xp = agnxtnode(G, np);
00254             deg = degreeKind(G, np, &other);
00255             if (deg == 0) {     /* singleton node */
00256                 agdelete(G->root, np);
00257             } else if (deg == 1) {
00258                 agdelete(G->root, np);
00259                 xp = prune(G, other, xp);
00260             }
00261         }
00262     }
00263 
00264     nV = agnnodes(G);
00265     nE = agnedges(G);
00266 
00267 #ifdef WITH_CGRAPH
00268     lenx = agattr(G, AGEDGE, "len", 0);
00269 #else
00270     lenx = agindex(G->root->proto->e, "len");
00271 #endif
00272     if (mode == MODE_KK) {
00273         Epsilon = .0001 * nV;
00274         getdouble(G, "epsilon", &Epsilon);
00275         if ((str = agget(G->root, "Damping")))
00276             Damping = atof(str);
00277         else
00278             Damping = .99;
00279         GD_neato_nlist(G) = N_NEW(nV + 1, node_t *);
00280         for (i = 0, np = agfstnode(G); np; np = agnxtnode(G, np)) {
00281             GD_neato_nlist(G)[i] = np;
00282             ND_id(np) = i++;
00283             ND_heapindex(np) = -1;
00284             total_len += setEdgeLen(G, np, lenx, dfltlen);
00285         }
00286     } else {
00287         Epsilon = DFLT_TOLERANCE;
00288         getdouble(G, "epsilon", &Epsilon);
00289         for (i = 0, np = agfstnode(G); np; np = agnxtnode(G, np)) {
00290             ND_id(np) = i++;
00291             total_len += setEdgeLen(G, np, lenx, dfltlen);
00292         }
00293     }
00294 
00295     str = agget(G, "defaultdist");
00296     if (str && str[0])
00297         Initial_dist = MAX(Epsilon, atof(str));
00298     else
00299         Initial_dist = total_len / (nE > 0 ? nE : 1) * sqrt(nV) + 1;
00300 
00301     if (!Nop && (mode == MODE_KK)) {
00302         GD_dist(G) = new_array(nV, nV, Initial_dist);
00303         GD_spring(G) = new_array(nV, nV, 1.0);
00304         GD_sum_t(G) = new_array(nV, Ndim, 1.0);
00305         GD_t(G) = new_3array(nV, nV, Ndim, 0.0);
00306     }
00307 
00308     return nV;
00309 }
00310 
00311 int scan_graph(graph_t * g)
00312 {
00313     return scan_graph_mode(g, MODE_KK);
00314 }
00315 
00316 void free_scan_graph(graph_t * g)
00317 {
00318     free(GD_neato_nlist(g));
00319     if (!Nop) {
00320         free_array(GD_dist(g));
00321         free_array(GD_spring(g));
00322         free_array(GD_sum_t(g));
00323         free_3array(GD_t(g));
00324         GD_t(g) = NULL;
00325     }
00326 }
00327 
00328 void jitter_d(node_t * np, int nG, int n)
00329 {
00330     int k;
00331     for (k = n; k < Ndim; k++)
00332         ND_pos(np)[k] = nG * drand48();
00333 }
00334 
00335 void jitter3d(node_t * np, int nG)
00336 {
00337     jitter_d(np, nG, 2);
00338 }
00339 
00340 void randompos(node_t * np, int nG)
00341 {
00342     ND_pos(np)[0] = nG * drand48();
00343     ND_pos(np)[1] = nG * drand48();
00344     if (Ndim > 2)
00345         jitter3d(np, nG);
00346 }
00347 
00348 void initial_positions(graph_t * G, int nG)
00349 {
00350     int init, i;
00351     node_t *np;
00352     static int once = 0;
00353 
00354     if (Verbose)
00355         fprintf(stderr, "Setting initial positions\n");
00356 
00357     init = checkStart(G, nG, INIT_RANDOM);
00358     if (init == INIT_REGULAR)
00359         return;
00360     if ((init == INIT_SELF) && (once == 0)) {
00361         agerr(AGWARN, "start=%s not supported with mode=self - ignored\n");
00362         once = 1;
00363     }
00364 
00365     for (i = 0; (np = GD_neato_nlist(G)[i]); i++) {
00366         if (hasPos(np))
00367             continue;
00368         randompos(np, 1);
00369     }
00370 }
00371 
00372 void diffeq_model(graph_t * G, int nG)
00373 {
00374     int i, j, k;
00375     double dist, **D, **K, del[MAXDIM], f;
00376     node_t *vi, *vj;
00377     edge_t *e;
00378 
00379     if (Verbose) {
00380         fprintf(stderr, "Setting up spring model: ");
00381         start_timer();
00382     }
00383     /* init springs */
00384     K = GD_spring(G);
00385     D = GD_dist(G);
00386     for (i = 0; i < nG; i++) {
00387         for (j = 0; j < i; j++) {
00388             f = Spring_coeff / (D[i][j] * D[i][j]);
00389             if ((e = agfindedge(G, GD_neato_nlist(G)[i], GD_neato_nlist(G)[j])))
00390                 f = f * ED_factor(e);
00391             K[i][j] = K[j][i] = f;
00392         }
00393     }
00394 
00395     /* init differential equation solver */
00396     for (i = 0; i < nG; i++)
00397         for (k = 0; k < Ndim; k++)
00398             GD_sum_t(G)[i][k] = 0.0;
00399 
00400     for (i = 0; (vi = GD_neato_nlist(G)[i]); i++) {
00401         for (j = 0; j < nG; j++) {
00402             if (i == j)
00403                 continue;
00404             vj = GD_neato_nlist(G)[j];
00405             dist = distvec(ND_pos(vi), ND_pos(vj), del);
00406             for (k = 0; k < Ndim; k++) {
00407                 GD_t(G)[i][j][k] =
00408                     GD_spring(G)[i][j] * (del[k] -
00409                                           GD_dist(G)[i][j] * del[k] /
00410                                           dist);
00411                 GD_sum_t(G)[i][k] += GD_t(G)[i][j][k];
00412             }
00413         }
00414     }
00415     if (Verbose) {
00416         fprintf(stderr, "%.2f sec\n", elapsed_sec());
00417     }
00418 }
00419 
00420 /* total_e:
00421  * Return 2*energy of system.
00422  */
00423 static double total_e(graph_t * G, int nG)
00424 {
00425     int i, j, d;
00426     double e = 0.0;             /* 2*energy */
00427     double t0;                  /* distance squared */
00428     double t1;
00429     node_t *ip, *jp;
00430 
00431     for (i = 0; i < nG - 1; i++) {
00432         ip = GD_neato_nlist(G)[i];
00433         for (j = i + 1; j < nG; j++) {
00434             jp = GD_neato_nlist(G)[j];
00435             for (t0 = 0.0, d = 0; d < Ndim; d++) {
00436                 t1 = (ND_pos(ip)[d] - ND_pos(jp)[d]);
00437                 t0 += t1 * t1;
00438             }
00439             e = e + GD_spring(G)[i][j] *
00440                 (t0 + GD_dist(G)[i][j] * GD_dist(G)[i][j]
00441                  - 2.0 * GD_dist(G)[i][j] * sqrt(t0));
00442         }
00443     }
00444     return e;
00445 }
00446 
00447 void solve_model(graph_t * G, int nG)
00448 {
00449     node_t *np;
00450 
00451     Epsilon2 = Epsilon * Epsilon;
00452 
00453     while ((np = choose_node(G, nG))) {
00454         move_node(G, nG, np);
00455     }
00456     if (Verbose) {
00457         fprintf(stderr, "\nfinal e = %f", total_e(G, nG));
00458         fprintf(stderr, " %d%s iterations %.2f sec\n",
00459                 GD_move(G), (GD_move(G) == MaxIter ? "!" : ""),
00460                 elapsed_sec());
00461     }
00462     if (GD_move(G) == MaxIter)
00463         agerr(AGWARN, "Max. iterations (%d) reached on graph %s\n",
00464               MaxIter, agnameof(G));
00465 }
00466 
00467 void update_arrays(graph_t * G, int nG, int i)
00468 {
00469     int j, k;
00470     double del[MAXDIM], dist, old;
00471     node_t *vi, *vj;
00472 
00473     vi = GD_neato_nlist(G)[i];
00474     for (k = 0; k < Ndim; k++)
00475         GD_sum_t(G)[i][k] = 0.0;
00476     for (j = 0; j < nG; j++) {
00477         if (i == j)
00478             continue;
00479         vj = GD_neato_nlist(G)[j];
00480         dist = distvec(ND_pos(vi), ND_pos(vj), del);
00481         for (k = 0; k < Ndim; k++) {
00482             old = GD_t(G)[i][j][k];
00483             GD_t(G)[i][j][k] =
00484                 GD_spring(G)[i][j] * (del[k] -
00485                                       GD_dist(G)[i][j] * del[k] / dist);
00486             GD_sum_t(G)[i][k] += GD_t(G)[i][j][k];
00487             old = GD_t(G)[j][i][k];
00488             GD_t(G)[j][i][k] = -GD_t(G)[i][j][k];
00489             GD_sum_t(G)[j][k] += (GD_t(G)[j][i][k] - old);
00490         }
00491     }
00492 }
00493 
00494 #define Msub(i,j)  M[(i)*Ndim+(j)]
00495 void D2E(graph_t * G, int nG, int n, double *M)
00496 {
00497     int i, l, k;
00498     node_t *vi, *vn;
00499     double scale, sq, t[MAXDIM];
00500     double **K = GD_spring(G);
00501     double **D = GD_dist(G);
00502 
00503     vn = GD_neato_nlist(G)[n];
00504     for (l = 0; l < Ndim; l++)
00505         for (k = 0; k < Ndim; k++)
00506             Msub(l, k) = 0.0;
00507     for (i = 0; i < nG; i++) {
00508         if (n == i)
00509             continue;
00510         vi = GD_neato_nlist(G)[i];
00511         sq = 0.0;
00512         for (k = 0; k < Ndim; k++) {
00513             t[k] = ND_pos(vn)[k] - ND_pos(vi)[k];
00514             sq += (t[k] * t[k]);
00515         }
00516         scale = 1 / fpow32(sq);
00517         for (k = 0; k < Ndim; k++) {
00518             for (l = 0; l < k; l++)
00519                 Msub(l, k) += K[n][i] * D[n][i] * t[k] * t[l] * scale;
00520             Msub(k, k) +=
00521                 K[n][i] * (1.0 - D[n][i] * (sq - (t[k] * t[k])) * scale);
00522         }
00523     }
00524     for (k = 1; k < Ndim; k++)
00525         for (l = 0; l < k; l++)
00526             Msub(k, l) = Msub(l, k);
00527 }
00528 
00529 void final_energy(graph_t * G, int nG)
00530 {
00531     fprintf(stderr, "iterations = %d final e = %f\n", GD_move(G),
00532             total_e(G, nG));
00533 }
00534 
00535 node_t *choose_node(graph_t * G, int nG)
00536 {
00537     int i, k;
00538     double m, max;
00539     node_t *choice, *np;
00540     static int cnt = 0;
00541 #if 0
00542     double e;
00543     static double save_e = MAXDOUBLE;
00544 #endif
00545 
00546     cnt++;
00547     if (GD_move(G) >= MaxIter)
00548         return NULL;
00549 #if 0
00550     if ((cnt % 100) == 0) {
00551         e = total_e(G, nG);
00552         if (e - save_e > 0)
00553             return NULL;
00554         save_e = e;
00555     }
00556 #endif
00557     max = 0.0;
00558     choice = NULL;
00559     for (i = 0; i < nG; i++) {
00560         np = GD_neato_nlist(G)[i];
00561         if (ND_pinned(np) > P_SET)
00562             continue;
00563         for (m = 0.0, k = 0; k < Ndim; k++)
00564             m += (GD_sum_t(G)[i][k] * GD_sum_t(G)[i][k]);
00565         /* could set the color=energy of the node here */
00566         if (m > max) {
00567             choice = np;
00568             max = m;
00569         }
00570     }
00571     if (max < Epsilon2)
00572         choice = NULL;
00573     else {
00574         if (Verbose && (cnt % 100 == 0)) {
00575             fprintf(stderr, "%.3f ", sqrt(max));
00576             if (cnt % 1000 == 0)
00577                 fprintf(stderr, "\n");
00578         }
00579 #if 0
00580         e = total_e(G, nG);
00581         if (fabs((e - save_e) / save_e) < 1e-5) {
00582             choice = NULL;
00583         }
00584 #endif
00585     }
00586     return choice;
00587 }
00588 
00589 void move_node(graph_t * G, int nG, node_t * n)
00590 {
00591     int i, m;
00592     static double *a, b[MAXDIM], c[MAXDIM];
00593 
00594     m = ND_id(n);
00595     a = ALLOC(Ndim * Ndim, a, double);
00596     D2E(G, nG, m, a);
00597     for (i = 0; i < Ndim; i++)
00598         c[i] = -GD_sum_t(G)[m][i];
00599     solve(a, b, c, Ndim);
00600     for (i = 0; i < Ndim; i++) {
00601         b[i] = (Damping + 2 * (1 - Damping) * drand48()) * b[i];
00602         ND_pos(n)[i] += b[i];
00603     }
00604     GD_move(G)++;
00605     update_arrays(G, nG, m);
00606     if (test_toggle()) {
00607         double sum = 0;
00608         for (i = 0; i < Ndim; i++) {
00609             sum += fabs(b[i]);
00610         }                       /* Why not squared? */
00611         sum = sqrt(sum);
00612         fprintf(stderr, "%s %.3f\n", agnameof(n), sum);
00613     }
00614 }
00615 
00616 static node_t **Heap;
00617 static int Heapsize;
00618 static node_t *Src;
00619 
00620 void heapup(node_t * v)
00621 {
00622     int i, par;
00623     node_t *u;
00624 
00625     for (i = ND_heapindex(v); i > 0; i = par) {
00626         par = (i - 1) / 2;
00627         u = Heap[par];
00628         if (ND_dist(u) <= ND_dist(v))
00629             break;
00630         Heap[par] = v;
00631         ND_heapindex(v) = par;
00632         Heap[i] = u;
00633         ND_heapindex(u) = i;
00634     }
00635 }
00636 
00637 void heapdown(node_t * v)
00638 {
00639     int i, left, right, c;
00640     node_t *u;
00641 
00642     i = ND_heapindex(v);
00643     while ((left = 2 * i + 1) < Heapsize) {
00644         right = left + 1;
00645         if ((right < Heapsize)
00646             && (ND_dist(Heap[right]) < ND_dist(Heap[left])))
00647             c = right;
00648         else
00649             c = left;
00650         u = Heap[c];
00651         if (ND_dist(v) <= ND_dist(u))
00652             break;
00653         Heap[c] = v;
00654         ND_heapindex(v) = c;
00655         Heap[i] = u;
00656         ND_heapindex(u) = i;
00657         i = c;
00658     }
00659 }
00660 
00661 void neato_enqueue(node_t * v)
00662 {
00663     int i;
00664 
00665     assert(ND_heapindex(v) < 0);
00666     i = Heapsize++;
00667     ND_heapindex(v) = i;
00668     Heap[i] = v;
00669     if (i > 0)
00670         heapup(v);
00671 }
00672 
00673 node_t *neato_dequeue(void)
00674 {
00675     int i;
00676     node_t *rv, *v;
00677 
00678     if (Heapsize == 0)
00679         return NULL;
00680     rv = Heap[0];
00681     i = --Heapsize;
00682     v = Heap[i];
00683     Heap[0] = v;
00684     ND_heapindex(v) = 0;
00685     if (i > 1)
00686         heapdown(v);
00687     ND_heapindex(rv) = -1;
00688     return rv;
00689 }
00690 
00691 void shortest_path(graph_t * G, int nG)
00692 {
00693     node_t *v;
00694 
00695     Heap = N_NEW(nG + 1, node_t *);
00696     if (Verbose) {
00697         fprintf(stderr, "Calculating shortest paths: ");
00698         start_timer();
00699     }
00700     for (v = agfstnode(G); v; v = agnxtnode(G, v))
00701         s1(G, v);
00702     if (Verbose) {
00703         fprintf(stderr, "%.2f sec\n", elapsed_sec());
00704     }
00705     free(Heap);
00706 }
00707 
00708 void s1(graph_t * G, node_t * node)
00709 {
00710     node_t *v, *u;
00711     edge_t *e;
00712     int t;
00713     double f;
00714 
00715     for (t = 0; (v = GD_neato_nlist(G)[t]); t++)
00716         ND_dist(v) = Initial_dist;
00717     Src = node;
00718     ND_dist(Src) = 0;
00719     ND_hops(Src) = 0;
00720     neato_enqueue(Src);
00721 
00722     while ((v = neato_dequeue())) {
00723         if (v != Src)
00724             make_spring(G, Src, v, ND_dist(v));
00725         for (e = agfstedge(G, v); e; e = agnxtedge(G, e, v)) {
00726             if ((u = agtail(e)) == v)
00727                 u = aghead(e);
00728             f = ND_dist(v) + ED_dist(e);
00729             if (ND_dist(u) > f) {
00730                 ND_dist(u) = f;
00731                 if (ND_heapindex(u) >= 0)
00732                     heapup(u);
00733                 else {
00734                     ND_hops(u) = ND_hops(v) + 1;
00735                     neato_enqueue(u);
00736                 }
00737             }
00738         }
00739     }
00740 }
00741 
00742 void make_spring(graph_t * G, node_t * u, node_t * v, double f)
00743 {
00744     int i, j;
00745 
00746     i = ND_id(u);
00747     j = ND_id(v);
00748     GD_dist(G)[i][j] = GD_dist(G)[j][i] = f;
00749 }
00750 
00751 int allow_edits(int nsec)
00752 {
00753 #ifdef INTERACTIVE
00754     static int onetime = TRUE;
00755     static FILE *fp;
00756     static fd_set fd;
00757     static struct timeval tv;
00758 
00759     char buf[256], name[256];
00760     double x, y;
00761     node_t *np;
00762 
00763     if (onetime) {
00764         fp = fopen("/dev/tty", "r");
00765         if (fp == NULL)
00766             exit(1);
00767         setbuf(fp, NULL);
00768         tv.tv_usec = 0;
00769         onetime = FALSE;
00770     }
00771     tv.tv_sec = nsec;
00772     FD_ZERO(&fd);
00773     FD_SET(fileno(fp), &fd);
00774     if (select(32, &fd, (fd_set *) 0, (fd_set *) 0, &tv) > 0) {
00775         fgets(buf, sizeof(buf), fp);
00776         switch (buf[0]) {
00777         case 'm':               /* move node */
00778             if (sscanf(buf + 1, "%s %lf%lf", name, &x, &y) == 3) {
00779                 np = getnode(G, name);
00780                 if (np) {
00781                     NP_pos(np)[0] = x;
00782                     NP_pos(np)[1] = y;
00783                     diffeq_model();
00784                 }
00785             }
00786             break;
00787         case 'q':
00788             return FALSE;
00789         default:
00790             agerr(AGERR, "unknown command '%s', ignored\n", buf);
00791         }
00792         return TRUE;
00793     }
00794 #endif
00795     return FALSE;
00796 }