neatoinit.c 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504
  1. /**
  2. * @file
  3. * @brief API neatogen/neatoprocs.h:
  4. * @ref neato_init_node, @ref user_pos, @ref neato_cleanup,
  5. * @ref init_nop, @ref setSeed, @ref checkStart, @ref neato_layout
  6. */
  7. /*************************************************************************
  8. * Copyright (c) 2011 AT&T Intellectual Property
  9. * All rights reserved. This program and the accompanying materials
  10. * are made available under the terms of the Eclipse Public License v1.0
  11. * which accompanies this distribution, and is available at
  12. * https://www.eclipse.org/legal/epl-v10.html
  13. *
  14. * Contributors: Details at https://graphviz.org
  15. *************************************************************************/
  16. #include "config.h"
  17. #include <time.h>
  18. #ifndef _WIN32
  19. #include <unistd.h>
  20. #endif
  21. #include <neatogen/neato.h>
  22. #include <pack/pack.h>
  23. #include <neatogen/stress.h>
  24. #ifdef DIGCOLA
  25. #include <neatogen/digcola.h>
  26. #endif
  27. #include <neatogen/kkutils.h>
  28. #include <common/pointset.h>
  29. #include <common/render.h>
  30. #include <common/utils.h>
  31. #include <neatogen/sgd.h>
  32. #include <cgraph/cgraph.h>
  33. #include <cgraph/gv_ctype.h>
  34. #include <float.h>
  35. #include <stdbool.h>
  36. #include <stddef.h>
  37. #include <util/agxbuf.h>
  38. #include <util/alloc.h>
  39. #include <util/bitarray.h>
  40. #include <util/prisize_t.h>
  41. #include <util/startswith.h>
  42. #include <util/strcasecmp.h>
  43. #include <util/streq.h>
  44. #ifndef HAVE_SRAND48
  45. #define srand48 srand
  46. #endif
  47. static attrsym_t *N_pos;
  48. static int Pack; /* If >= 0, layout components separately and pack together
  49. * The value of Pack gives margins around graphs.
  50. */
  51. static char *cc_pfx = "_neato_cc";
  52. void neato_init_node(node_t * n)
  53. {
  54. agbindrec(n, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true); //node custom data
  55. common_init_node(n);
  56. ND_pos(n) = gv_calloc(GD_ndim(agraphof(n)), sizeof(double));
  57. gv_nodesize(n, GD_flip(agraphof(n)));
  58. }
  59. static void neato_init_edge(edge_t * e)
  60. {
  61. agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true); //node custom data
  62. common_init_edge(e);
  63. ED_factor(e) = late_double(e, E_weight, 1.0, 1.0);
  64. }
  65. bool user_pos(attrsym_t *posptr, attrsym_t *pinptr, node_t *np, int nG) {
  66. double *pvec;
  67. char *p, c;
  68. double z;
  69. if (posptr == NULL)
  70. return false;
  71. pvec = ND_pos(np);
  72. p = agxget(np, posptr);
  73. if (p[0]) {
  74. c = '\0';
  75. if (Ndim >= 3 && sscanf(p, "%lf,%lf,%lf%c", pvec, pvec+1, pvec+2, &c) >= 3){
  76. ND_pinned(np) = P_SET;
  77. if (PSinputscale > 0.0) {
  78. int i;
  79. for (i = 0; i < Ndim; i++)
  80. pvec[i] = pvec[i] / PSinputscale;
  81. }
  82. if (Ndim > 3)
  83. jitter_d(np, nG, 3);
  84. if (c == '!' || (pinptr && mapbool(agxget(np, pinptr))))
  85. ND_pinned(np) = P_PIN;
  86. return true;
  87. }
  88. else if (sscanf(p, "%lf,%lf%c", pvec, pvec + 1, &c) >= 2) {
  89. ND_pinned(np) = P_SET;
  90. if (PSinputscale > 0.0) {
  91. int i;
  92. for (i = 0; i < Ndim; i++)
  93. pvec[i] /= PSinputscale;
  94. }
  95. if (Ndim > 2) {
  96. if (N_z && (p = agxget(np, N_z)) && sscanf(p,"%lf",&z) == 1) {
  97. if (PSinputscale > 0.0) {
  98. pvec[2] = z / PSinputscale;
  99. }
  100. else
  101. pvec[2] = z;
  102. jitter_d(np, nG, 3);
  103. }
  104. else
  105. jitter3d(np, nG);
  106. }
  107. if (c == '!' || (pinptr && mapbool(agxget(np, pinptr))))
  108. ND_pinned(np) = P_PIN;
  109. return true;
  110. } else
  111. agerrorf("node %s, position %s, expected two doubles\n",
  112. agnameof(np), p);
  113. }
  114. return false;
  115. }
  116. static void neato_init_node_edge(graph_t * g)
  117. {
  118. node_t *n;
  119. edge_t *e;
  120. int nG = agnnodes(g);
  121. attrsym_t *N_pin;
  122. N_pos = agfindnodeattr(g, "pos");
  123. N_pin = agfindnodeattr(g, "pin");
  124. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  125. neato_init_node(n);
  126. user_pos(N_pos, N_pin, n, nG); /* set user position if given */
  127. }
  128. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  129. for (e = agfstout(g, n); e; e = agnxtout(g, e))
  130. neato_init_edge(e);
  131. }
  132. }
  133. static void neato_cleanup_graph(graph_t * g)
  134. {
  135. if (Nop || Pack < 0) {
  136. free_scan_graph(g);
  137. }
  138. free(GD_clust(g));
  139. }
  140. void neato_cleanup(graph_t * g)
  141. {
  142. node_t *n;
  143. edge_t *e;
  144. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  145. for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
  146. gv_cleanup_edge(e);
  147. }
  148. gv_cleanup_node(n);
  149. }
  150. neato_cleanup_graph(g);
  151. }
  152. static int numFields(const char *pos) {
  153. int cnt = 0;
  154. char c;
  155. do {
  156. while (gv_isspace(*pos))
  157. pos++; /* skip white space */
  158. if ((c = *pos)) { /* skip token */
  159. cnt++;
  160. while ((c = *pos) && !gv_isspace(c) && c != ';')
  161. pos++;
  162. }
  163. } while (gv_isspace(c));
  164. return cnt;
  165. }
  166. static void set_label(void* obj, textlabel_t * l, char *name)
  167. {
  168. double x, y;
  169. char *lp;
  170. lp = agget(obj, name);
  171. if (lp && sscanf(lp, "%lf,%lf", &x, &y) == 2) {
  172. l->pos = (pointf){x, y};
  173. l->set = true;
  174. }
  175. }
  176. #ifdef IPSEPCOLA
  177. static cluster_data cluster_map(graph_t *mastergraph, graph_t *g) {
  178. graph_t *subg;
  179. node_t *n;
  180. /* array of arrays of node indices in each cluster */
  181. int **cs,*cn;
  182. int i,j,nclusters=0;
  183. bitarray_t assigned = bitarray_new(agnnodes(g));
  184. cluster_data cdata = {0};
  185. cdata.ntoplevel = agnnodes(g);
  186. for (subg = agfstsubg(mastergraph); subg; subg = agnxtsubg(subg)) {
  187. if (is_a_cluster(subg)) {
  188. nclusters++;
  189. }
  190. }
  191. cdata.nvars=0;
  192. cdata.nclusters = nclusters;
  193. cs = cdata.clusters = gv_calloc(nclusters, sizeof(int*));
  194. cn = cdata.clustersizes = gv_calloc(nclusters, sizeof(int));
  195. for (subg = agfstsubg(mastergraph); subg; subg = agnxtsubg(subg)) {
  196. /* clusters are processed by separate calls to ordered_edges */
  197. if (is_a_cluster(subg)) {
  198. int *c;
  199. *cn = agnnodes(subg);
  200. cdata.nvars += *cn;
  201. c = *cs++ = gv_calloc(*cn++, sizeof(int));
  202. for (n = agfstnode(subg); n; n = agnxtnode(subg, n)) {
  203. node_t *gn;
  204. int ind = 0;
  205. for (gn = agfstnode(g); gn; gn = agnxtnode(g, gn)) {
  206. if(AGSEQ(gn)==AGSEQ(n)) break;
  207. ind++;
  208. }
  209. *c++=ind;
  210. bitarray_set(&assigned, ind, true);
  211. cdata.ntoplevel--;
  212. }
  213. }
  214. }
  215. cdata.bb = gv_calloc(cdata.nclusters, sizeof(boxf));
  216. cdata.toplevel = gv_calloc(cdata.ntoplevel, sizeof(int));
  217. for(i=j=0;i<agnnodes(g);i++) {
  218. if(!bitarray_get(assigned, i)) {
  219. cdata.toplevel[j++] = i;
  220. }
  221. }
  222. assert(cdata.ntoplevel == agnnodes(g) - cdata.nvars);
  223. bitarray_reset(&assigned);
  224. return cdata;
  225. }
  226. static void freeClusterData(cluster_data c) {
  227. if (c.nclusters > 0) {
  228. free(c.clusters[0]);
  229. free(c.clusters);
  230. free(c.clustersizes);
  231. free(c.toplevel);
  232. free(c.bb);
  233. }
  234. }
  235. #endif
  236. /* user_spline:
  237. * Attempt to use already existing pos info for spline
  238. * Return 1 if successful, 0 otherwise.
  239. * Assume E_pos != NULL and ED_spl(e) == NULL.
  240. */
  241. static int user_spline(attrsym_t * E_pos, edge_t * e)
  242. {
  243. char *pos;
  244. int i, n, npts, nc;
  245. pointf *pp;
  246. double x, y;
  247. int sflag = 0, eflag = 0;
  248. pointf sp = { 0, 0 }, ep = { 0, 0};
  249. bezier *newspl;
  250. int more = 1;
  251. static bool warned;
  252. pos = agxget(e, E_pos);
  253. if (*pos == '\0')
  254. return 0;
  255. uint32_t stype, etype;
  256. arrow_flags(e, &stype, &etype);
  257. do {
  258. /* check for s head */
  259. i = sscanf(pos, "s,%lf,%lf%n", &x, &y, &nc);
  260. if (i == 2) {
  261. sflag = 1;
  262. pos = pos + nc;
  263. sp.x = x;
  264. sp.y = y;
  265. }
  266. /* check for e head */
  267. i = sscanf(pos, " e,%lf,%lf%n", &x, &y, &nc);
  268. if (i == 2) {
  269. eflag = 1;
  270. pos = pos + nc;
  271. ep.x = x;
  272. ep.y = y;
  273. }
  274. npts = numFields(pos); // count potential points
  275. n = npts;
  276. if (n < 4 || n % 3 != 1) {
  277. gv_free_splines(e);
  278. if (!warned) {
  279. warned = true;
  280. agwarningf("pos attribute for edge (%s,%s) doesn't have 3n+1 points\n", agnameof(agtail(e)), agnameof(aghead(e)));
  281. }
  282. return 0;
  283. }
  284. pointf *ps = gv_calloc(n, sizeof(pointf));
  285. pp = ps;
  286. while (n) {
  287. i = sscanf(pos, "%lf,%lf%n", &x, &y, &nc);
  288. if (i < 2) {
  289. if (!warned) {
  290. warned = true;
  291. agwarningf("syntax error in pos attribute for edge (%s,%s)\n", agnameof(agtail(e)), agnameof(aghead(e)));
  292. }
  293. free(ps);
  294. gv_free_splines(e);
  295. return 0;
  296. }
  297. pos += nc;
  298. pp->x = x;
  299. pp->y = y;
  300. pp++;
  301. n--;
  302. }
  303. while (gv_isspace(*pos)) pos++;
  304. if (*pos == '\0')
  305. more = 0;
  306. else
  307. pos++;
  308. /* parsed successfully; create spline */
  309. assert(npts >= 0);
  310. newspl = new_spline(e, (size_t)npts);
  311. if (sflag) {
  312. newspl->sflag = stype;
  313. newspl->sp = sp;
  314. }
  315. if (eflag) {
  316. newspl->eflag = etype;
  317. newspl->ep = ep;
  318. }
  319. for (i = 0; i < npts; i++) {
  320. newspl->list[i] = ps[i];
  321. }
  322. free(ps);
  323. } while (more);
  324. if (ED_label(e))
  325. set_label(e, ED_label(e), "lp");
  326. if (ED_xlabel(e))
  327. set_label(e, ED_xlabel(e), "xlp");
  328. if (ED_head_label(e))
  329. set_label(e, ED_head_label(e), "head_lp");
  330. if (ED_tail_label(e))
  331. set_label(e, ED_tail_label(e), "tail_lp");
  332. return 1;
  333. }
  334. /* Nop can be:
  335. * 0 - do full layout
  336. * 1 - assume initial node positions, do (optional) adjust and all splines
  337. * 2 - assume final node and edges positions, do nothing except compute
  338. * missing splines
  339. */
  340. /* Indicates the amount of edges with position information */
  341. typedef enum { NoEdges, SomeEdges, AllEdges } pos_edge;
  342. /* nop_init_edges:
  343. * Check edges for position info.
  344. * If position info exists, check for edge label positions.
  345. * Return number of edges with position info.
  346. */
  347. static pos_edge nop_init_edges(Agraph_t * g)
  348. {
  349. node_t *n;
  350. edge_t *e;
  351. int nedges = 0;
  352. attrsym_t *E_pos;
  353. if (agnedges(g) == 0)
  354. return AllEdges;
  355. E_pos = agfindedgeattr(g, "pos");
  356. if (!E_pos || Nop < 2)
  357. return NoEdges;
  358. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  359. for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
  360. if (user_spline(E_pos, e)) {
  361. nedges++;
  362. }
  363. }
  364. }
  365. if (nedges) {
  366. if (nedges == agnedges(g))
  367. return AllEdges;
  368. else
  369. return SomeEdges;
  370. } else
  371. return NoEdges;
  372. }
  373. /* freeEdgeInfo:
  374. */
  375. static void freeEdgeInfo (Agraph_t * g)
  376. {
  377. node_t *n;
  378. edge_t *e;
  379. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  380. for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
  381. gv_free_splines(e);
  382. free_label(ED_label(e));
  383. free_label(ED_xlabel(e));
  384. free_label(ED_head_label(e));
  385. free_label(ED_tail_label(e));
  386. }
  387. }
  388. }
  389. /* chkBB:
  390. * Scans for a correct bb attribute. If available, sets it
  391. * in the graph and returns 1.
  392. */
  393. #define BS "%lf,%lf,%lf,%lf"
  394. static int chkBB(Agraph_t * g, attrsym_t * G_bb, boxf* bbp)
  395. {
  396. char *s;
  397. boxf bb;
  398. s = agxget(g, G_bb);
  399. if (sscanf(s, BS, &bb.LL.x, &bb.LL.y, &bb.UR.x, &bb.UR.y) == 4) {
  400. if (bb.LL.y > bb.UR.y) {
  401. /* If the LL.y coordinate is bigger than the UR.y coordinate,
  402. * we assume the input was produced using -y, so we normalize
  403. * the bb.
  404. */
  405. double tmp = bb.LL.y;
  406. bb.LL.y = bb.UR.y;
  407. bb.UR.y = tmp;
  408. }
  409. *bbp = bb;
  410. return 1;
  411. } else
  412. return 0;
  413. }
  414. static void add_cluster(Agraph_t * g, Agraph_t * subg)
  415. {
  416. int cno;
  417. cno = ++(GD_n_cluster(g));
  418. GD_clust(g) = gv_recalloc(GD_clust(g), GD_n_cluster(g), cno + 1,
  419. sizeof(graph_t*));
  420. GD_clust(g)[cno] = subg;
  421. do_graph_label(subg);
  422. }
  423. static void nop_init_graphs(Agraph_t *, attrsym_t *, attrsym_t *);
  424. /* dfs:
  425. * Process subgraph subg of parent graph g
  426. * If subg is a cluster, add its bounding box, if any; attach to
  427. * cluster array of parent, and recursively initialize subg.
  428. * If not a cluster, recursively call this function on the subgraphs
  429. * of subg, using parentg as the parent graph.
  430. */
  431. static void
  432. dfs(Agraph_t * subg, Agraph_t * parentg, attrsym_t * G_lp, attrsym_t * G_bb)
  433. {
  434. boxf bb;
  435. if (is_a_cluster(subg) && chkBB(subg, G_bb, &bb)) {
  436. agbindrec(subg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
  437. GD_bb(subg) = bb;
  438. add_cluster(parentg, subg);
  439. nop_init_graphs(subg, G_lp, G_bb);
  440. } else {
  441. graph_t *sg;
  442. for (sg = agfstsubg(subg); sg; sg = agnxtsubg(sg)) {
  443. dfs(sg, parentg, G_lp, G_bb);
  444. }
  445. }
  446. }
  447. /* nop_init_graphs:
  448. * Read in clusters and graph label info.
  449. * A subgraph is a cluster if its name starts with "cluster" and
  450. * it has a valid bb.
  451. */
  452. static void
  453. nop_init_graphs(Agraph_t * g, attrsym_t * G_lp, attrsym_t * G_bb)
  454. {
  455. graph_t *subg;
  456. char *s;
  457. double x, y;
  458. if (GD_label(g) && G_lp) {
  459. s = agxget(g, G_lp);
  460. if (sscanf(s, "%lf,%lf", &x, &y) == 2) {
  461. GD_label(g)->pos = (pointf){x, y};
  462. GD_label(g)->set = true;
  463. }
  464. }
  465. if (!G_bb)
  466. return;
  467. for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
  468. dfs(subg, g, G_lp, G_bb);
  469. }
  470. }
  471. /* init_nop:
  472. * This assumes all nodes have been positioned.
  473. * It also assumes none of the relevant fields in A*info_t have been set.
  474. * The input may provide additional position information for
  475. * clusters, edges and labels. If certain position information
  476. * is missing, init_nop will use a standard neato technique to
  477. * supply it.
  478. *
  479. * If adjust is false, init_nop does nothing but initialize all
  480. * of the basic graph information. No tweaking of positions or
  481. * filling in edge splines is done.
  482. *
  483. * Returns 0 on normal success, 1 if layout has a background, and -1
  484. * on failure.
  485. */
  486. int init_nop(Agraph_t * g, int adjust)
  487. {
  488. int i;
  489. node_t *np;
  490. pos_edge posEdges; /* How many edges have spline info */
  491. attrsym_t *G_lp = agfindgraphattr(g, "lp");
  492. attrsym_t *G_bb = agfindgraphattr(g, "bb");
  493. int didAdjust = 0; /* Have nodes been moved? */
  494. int haveBackground;
  495. bool translate = !mapbool(agget(g, "notranslate"));
  496. /* If G_bb not defined, define it */
  497. if (!G_bb)
  498. G_bb = agattr(g, AGRAPH, "bb", "");
  499. scan_graph(g); /* mainly to set up GD_neato_nlist */
  500. for (i = 0; (np = GD_neato_nlist(g)[i]); i++) {
  501. if (!hasPos(np) && !startswith(agnameof(np), "cluster")) {
  502. agerrorf("node %s in graph %s has no position\n",
  503. agnameof(np), agnameof(g));
  504. return -1;
  505. }
  506. if (ND_xlabel(np))
  507. set_label(np, ND_xlabel(np), "xlp");
  508. }
  509. nop_init_graphs(g, G_lp, G_bb);
  510. posEdges = nop_init_edges(g);
  511. if (GD_drawing(g)->xdots) {
  512. haveBackground = 1;
  513. GD_drawing(g)->ratio_kind = R_NONE; /* Turn off any aspect change if background present */
  514. }
  515. else
  516. haveBackground = 0;
  517. if (adjust && Nop == 1 && !haveBackground)
  518. didAdjust = adjustNodes(g);
  519. if (didAdjust) {
  520. if (GD_label(g)) GD_label(g)->set = false;
  521. /* FIX:
  522. * - if nodes are moved, clusters are no longer valid.
  523. */
  524. }
  525. compute_bb(g);
  526. /* Adjust bounding box for any background */
  527. if (haveBackground)
  528. GD_bb(g) = xdotBB (g);
  529. /* At this point, all bounding boxes should be correctly defined.
  530. */
  531. if (!adjust) {
  532. node_t *n;
  533. State = GVSPLINES;
  534. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  535. ND_coord(n).x = POINTS_PER_INCH * ND_pos(n)[0];
  536. ND_coord(n).y = POINTS_PER_INCH * ND_pos(n)[1];
  537. }
  538. }
  539. else {
  540. bool didShift;
  541. if (translate && !haveBackground && (GD_bb(g).LL.x != 0||GD_bb(g).LL.y != 0))
  542. neato_translate (g);
  543. didShift = neato_set_aspect(g);
  544. /* if we have some edge positions and we either shifted or adjusted, free edge positions */
  545. if (posEdges != NoEdges && (didShift || didAdjust)) {
  546. freeEdgeInfo (g);
  547. posEdges = NoEdges;
  548. }
  549. if (posEdges != AllEdges)
  550. spline_edges0(g, false); /* add edges */
  551. else
  552. State = GVSPLINES;
  553. }
  554. return haveBackground;
  555. }
  556. static void neato_init_graph (Agraph_t * g)
  557. {
  558. int outdim;
  559. setEdgeType (g, EDGETYPE_LINE);
  560. outdim = late_int(g, agfindgraphattr(g, "dimen"), 2, 2);
  561. GD_ndim(agroot(g)) = late_int(g, agfindgraphattr(g, "dim"), outdim, 2);
  562. Ndim = GD_ndim(g->root) = MIN(GD_ndim(g->root), MAXDIM);
  563. GD_odim(g->root) = MIN(outdim, Ndim);
  564. neato_init_node_edge(g);
  565. }
  566. static int neatoModel(graph_t * g)
  567. {
  568. char *p = agget(g, "model");
  569. if (!p || streq(p, "")) /* if p is NULL or "" */
  570. return MODEL_SHORTPATH;
  571. if (streq(p, "circuit"))
  572. return MODEL_CIRCUIT;
  573. if (streq(p, "subset"))
  574. return MODEL_SUBSET;
  575. if (streq(p, "shortpath"))
  576. return MODEL_SHORTPATH;
  577. if (streq(p, "mds")) {
  578. if (agattr(g, AGEDGE, "len", 0))
  579. return MODEL_MDS;
  580. else {
  581. agwarningf(
  582. "edges in graph %s have no len attribute. Hence, the mds model\n", agnameof(g));
  583. agerr(AGPREV, "is inappropriate. Reverting to the shortest path model.\n");
  584. return MODEL_SHORTPATH;
  585. }
  586. }
  587. agwarningf(
  588. "Unknown value %s for attribute \"model\" in graph %s - ignored\n",
  589. p, agnameof(g));
  590. return MODEL_SHORTPATH;
  591. }
  592. /* neatoMode:
  593. */
  594. static int neatoMode(graph_t * g)
  595. {
  596. char *str;
  597. int mode = MODE_MAJOR; /* default mode */
  598. str = agget(g, "mode");
  599. if (str && !streq(str, "")) {
  600. if (streq(str, "KK"))
  601. mode = MODE_KK;
  602. else if (streq(str, "major"))
  603. mode = MODE_MAJOR;
  604. else if (streq(str, "sgd"))
  605. mode = MODE_SGD;
  606. #ifdef DIGCOLA
  607. else if (streq(str, "hier"))
  608. mode = MODE_HIER;
  609. #ifdef IPSEPCOLA
  610. else if (streq(str, "ipsep"))
  611. mode = MODE_IPSEP;
  612. #endif
  613. #endif
  614. else
  615. agwarningf(
  616. "Illegal value %s for attribute \"mode\" in graph %s - ignored\n",
  617. str, agnameof(g));
  618. }
  619. return mode;
  620. }
  621. /* checkEdge:
  622. *
  623. */
  624. static int checkEdge(PointMap * pm, edge_t * ep, int idx)
  625. {
  626. int i = ND_id(agtail(ep));
  627. int j = ND_id(aghead(ep));
  628. int tmp;
  629. if (i > j) {
  630. tmp = i;
  631. i = j;
  632. j = tmp;
  633. }
  634. return insertPM(pm, i, j, idx);
  635. }
  636. #ifdef DIGCOLA
  637. /* dfsCycle:
  638. * dfs for breaking cycles in vtxdata
  639. */
  640. static void
  641. dfsCycle (vtx_data* graph, int i,int mode, node_t* nodes[])
  642. {
  643. node_t *np, *hp;
  644. int j;
  645. /* if mode is IPSEP make it an in-edge
  646. * at both ends, so that an edge constraint won't be generated!
  647. */
  648. double x = mode==MODE_IPSEP?-1.0:1.0;
  649. np = nodes[i];
  650. ND_mark(np) = true;
  651. ND_onstack(np) = true;
  652. for (size_t e = 1; e < graph[i].nedges; e++) {
  653. if (graph[i].edists[e] == 1.0) continue; /* in edge */
  654. j = graph[i].edges[e];
  655. hp = nodes[j];
  656. if (ND_onstack(hp)) { /* back edge: reverse it */
  657. graph[i].edists[e] = x;
  658. size_t f;
  659. for (f = 1; f < graph[j].nedges && graph[j].edges[f] != i; f++) ;
  660. assert (f < graph[j].nedges);
  661. graph[j].edists[f] = -1.0;
  662. }
  663. else if (!ND_mark(hp)) dfsCycle(graph, j, mode, nodes);
  664. }
  665. ND_onstack(np) = false;
  666. }
  667. /* acyclic:
  668. * Do a dfs of the vtx_data, looking for cycles, reversing edges.
  669. */
  670. static void
  671. acyclic (vtx_data* graph, int nv, int mode, node_t* nodes[])
  672. {
  673. int i;
  674. node_t* np;
  675. for (i = 0; i < nv; i++) {
  676. np = nodes[i];
  677. ND_mark(np) = false;
  678. ND_onstack(np) = false;
  679. }
  680. for (i = 0; i < nv; i++) {
  681. if (ND_mark(nodes[i])) continue;
  682. dfsCycle (graph, i, mode, nodes);
  683. }
  684. }
  685. #endif
  686. /* makeGraphData:
  687. * Create sparse graph representation via arrays.
  688. * Each node is represented by a vtx_data.
  689. * The index of each neighbor is stored in the edges array;
  690. * the corresponding edge lengths and weights go on ewgts and eweights.
  691. * We do not allocate the latter 2 if the graph does not use them.
  692. * By convention, graph[i].edges[0] == i.
  693. * The values graph[i].ewgts[0] and graph[i].eweights[0] are left undefined.
  694. *
  695. * In constructing graph from g, we neglect loops. We track multiedges (ignoring
  696. * direction). Edge weights are additive; the final edge length is the max.
  697. *
  698. * If direction is used, we set the edists field, -1 for tail, +1 for head.
  699. * graph[i].edists[0] is left undefined. If multiedges exist, the direction
  700. * of the first one encountered is used. Finally, a pass is made to guarantee
  701. * the graph is acyclic.
  702. *
  703. */
  704. static vtx_data *makeGraphData(graph_t * g, int nv, int *nedges, int mode, int model, node_t*** nodedata)
  705. {
  706. int ne = agnedges(g); /* upper bound */
  707. float *ewgts = NULL;
  708. node_t *np;
  709. edge_t *ep;
  710. float *eweights = NULL;
  711. #ifdef DIGCOLA
  712. float *edists = NULL;
  713. #endif
  714. PointMap *ps = newPM();
  715. int i, idx;
  716. /* lengths and weights unused in reweight model */
  717. bool haveLen = false;
  718. bool haveWt = false;
  719. if (model != MODEL_SUBSET) {
  720. haveLen = agattr(g, AGEDGE, "len", 0) != NULL;
  721. haveWt = E_weight != 0;
  722. }
  723. bool haveDir = mode == MODE_HIER || mode == MODE_IPSEP;
  724. vtx_data *graph = gv_calloc(nv, sizeof(vtx_data));
  725. node_t** nodes = gv_calloc(nv, sizeof(node_t*));
  726. const size_t edges_size = (size_t)(2 * ne + nv);
  727. int *edges = gv_calloc(edges_size, sizeof(int)); // reserve space for self loops
  728. if (haveLen || haveDir)
  729. ewgts = gv_calloc(edges_size, sizeof(float));
  730. if (haveWt)
  731. eweights = gv_calloc(edges_size, sizeof(float));
  732. #ifdef DIGCOLA
  733. if (haveDir)
  734. edists = gv_calloc(edges_size, sizeof(float));
  735. #endif
  736. i = 0;
  737. ne = 0;
  738. for (np = agfstnode(g); np; np = agnxtnode(g, np)) {
  739. int j = 1; /* index of neighbors */
  740. clearPM(ps);
  741. assert(ND_id(np) == i);
  742. nodes[i] = np;
  743. graph[i].edges = edges++; /* reserve space for the self loop */
  744. if (haveLen || haveDir)
  745. graph[i].ewgts = ewgts++;
  746. else
  747. graph[i].ewgts = NULL;
  748. if (haveWt)
  749. graph[i].eweights = eweights++;
  750. else
  751. graph[i].eweights = NULL;
  752. #ifdef DIGCOLA
  753. if (haveDir) {
  754. graph[i].edists = edists++;
  755. }
  756. else
  757. graph[i].edists = NULL;
  758. #endif
  759. size_t i_nedges = 1; // one for the self
  760. for (ep = agfstedge(g, np); ep; ep = agnxtedge(g, ep, np)) {
  761. if (aghead(ep) == agtail(ep))
  762. continue; /* ignore loops */
  763. idx = checkEdge(ps, ep, j);
  764. if (idx != j) { /* seen before */
  765. if (haveWt)
  766. graph[i].eweights[idx] += ED_factor(ep);
  767. if (haveLen) {
  768. graph[i].ewgts[idx] = fmax(graph[i].ewgts[idx], ED_dist(ep));
  769. }
  770. } else {
  771. node_t *vp = agtail(ep) == np ? aghead(ep) : agtail(ep);
  772. ne++;
  773. j++;
  774. *edges++ = ND_id(vp);
  775. if (haveWt)
  776. *eweights++ = ED_factor(ep);
  777. if (haveLen)
  778. *ewgts++ = ED_dist(ep);
  779. else if (haveDir)
  780. *ewgts++ = 1.0;
  781. #ifdef DIGCOLA
  782. if (haveDir) {
  783. char *s = agget(ep,"dir");
  784. if(s && startswith(s, "none")) {
  785. *edists++ = 0;
  786. } else {
  787. *edists++ = np == aghead(ep) ? 1.0 : -1.0;
  788. }
  789. }
  790. #endif
  791. i_nedges++;
  792. }
  793. }
  794. graph[i].nedges = i_nedges;
  795. graph[i].edges[0] = i;
  796. i++;
  797. }
  798. #ifdef DIGCOLA
  799. if (haveDir) {
  800. /* Make graph acyclic */
  801. acyclic (graph, nv, mode, nodes);
  802. }
  803. #endif
  804. ne /= 2; /* every edge is counted twice */
  805. /* If necessary, release extra memory. */
  806. if (ne != agnedges(g)) {
  807. edges = gv_recalloc(graph[0].edges, edges_size, 2 * ne + nv, sizeof(int));
  808. if (haveLen)
  809. ewgts = gv_recalloc(graph[0].ewgts, edges_size, 2 * ne + nv, sizeof(float));
  810. if (haveWt)
  811. eweights = gv_recalloc(graph[0].eweights, edges_size, 2 * ne + nv, sizeof(float));
  812. for (i = 0; i < nv; i++) {
  813. const size_t sz = graph[i].nedges;
  814. graph[i].edges = edges;
  815. edges += sz;
  816. if (haveLen) {
  817. graph[i].ewgts = ewgts;
  818. ewgts += sz;
  819. }
  820. if (haveWt) {
  821. graph[i].eweights = eweights;
  822. eweights += sz;
  823. }
  824. }
  825. }
  826. *nedges = ne;
  827. if (nodedata)
  828. *nodedata = nodes;
  829. else
  830. free (nodes);
  831. freePM(ps);
  832. return graph;
  833. }
  834. static void initRegular(graph_t * G, int nG)
  835. {
  836. double a, da;
  837. node_t *np;
  838. a = 0.0;
  839. da = 2 * M_PI / nG;
  840. for (np = agfstnode(G); np; np = agnxtnode(G, np)) {
  841. ND_pos(np)[0] = nG * Spring_coeff * cos(a);
  842. ND_pos(np)[1] = nG * Spring_coeff * sin(a);
  843. ND_pinned(np) = P_SET;
  844. a = a + da;
  845. if (Ndim > 2)
  846. jitter3d(np, nG);
  847. }
  848. }
  849. #define SLEN(s) (sizeof(s)-1)
  850. #define SMART "self"
  851. #define REGULAR "regular"
  852. #define RANDOM "random"
  853. /* setSeed:
  854. * Analyze "start" attribute. If unset, return dflt.
  855. * If it begins with self, regular, or random, return set init to same,
  856. * else set init to dflt.
  857. * If init is random, look for value integer suffix to use a seed; if not
  858. * found, use time to set seed and store seed in graph.
  859. * Return seed in seedp.
  860. * Return init.
  861. */
  862. int
  863. setSeed (graph_t * G, int dflt, long* seedp)
  864. {
  865. char *p = agget(G, "start");
  866. int init = dflt;
  867. if (!p || *p == '\0') return dflt;
  868. if (gv_isalpha(*p)) {
  869. if (startswith(p, SMART)) {
  870. init = INIT_SELF;
  871. p += SLEN(SMART);
  872. } else if (startswith(p, REGULAR)) {
  873. init = INIT_REGULAR;
  874. p += SLEN(REGULAR);
  875. } else if (startswith(p, RANDOM)) {
  876. init = INIT_RANDOM;
  877. p += SLEN(RANDOM);
  878. }
  879. else init = dflt;
  880. }
  881. else if (gv_isdigit(*p)) {
  882. init = INIT_RANDOM;
  883. }
  884. if (init == INIT_RANDOM) {
  885. long seed;
  886. /* Check for seed value */
  887. if (!gv_isdigit(*p) || sscanf(p, "%ld", &seed) < 1) {
  888. #if defined(_WIN32)
  889. seed = (unsigned) time(NULL);
  890. #else
  891. seed = (unsigned) getpid() ^ (unsigned) time(NULL);
  892. #endif
  893. agxbuf buf = {0};
  894. agxbprint(&buf, "%ld", seed);
  895. agset(G, "start", agxbuse(&buf));
  896. agxbfree(&buf);
  897. }
  898. *seedp = seed;
  899. }
  900. return init;
  901. }
  902. /* checkExp:
  903. * Allow various weights for the scale factor in used to calculate stress.
  904. * At present, only 1 or 2 are allowed, with 2 the default.
  905. */
  906. #define exp_name "stresswt"
  907. static int checkExp (graph_t * G)
  908. {
  909. int exp = late_int(G, agfindgraphattr(G, exp_name), 2, 0);
  910. if (exp == 0 || exp > 2) {
  911. agwarningf("%s attribute value must be 1 or 2 - ignoring\n", exp_name);
  912. exp = 2;
  913. }
  914. return exp;
  915. }
  916. /* checkStart:
  917. * Analyzes start attribute, setting seed.
  918. * If set,
  919. * If start is regular, places nodes and returns INIT_REGULAR.
  920. * If start is self, returns INIT_SELF.
  921. * If start is random, returns INIT_RANDOM
  922. * Set RNG seed
  923. * else return default
  924. *
  925. */
  926. int checkStart(graph_t * G, int nG, int dflt)
  927. {
  928. long seed;
  929. int init;
  930. seed = 1;
  931. init = setSeed (G, dflt, &seed);
  932. if (N_pos && init != INIT_RANDOM) {
  933. agwarningf("node positions are ignored unless start=random\n");
  934. }
  935. if (init == INIT_REGULAR) initRegular(G, nG);
  936. srand48(seed);
  937. return init;
  938. }
  939. #ifdef DEBUG_COLA
  940. void dumpData(graph_t * g, vtx_data * gp, int nv, int ne)
  941. {
  942. node_t *v;
  943. int i;
  944. fprintf(stderr, "#nodes %d #edges %d\n", nv, ne);
  945. for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
  946. fprintf(stderr, "\"%s\" %d\n", agnameof(v), ND_id(v));
  947. }
  948. for (i = 0; i < nv; i++) {
  949. const size_t n = gp[i].nedges;
  950. fprintf(stderr, "[%d] %" PRISIZE_T "\n", i, n);
  951. for (size_t j = 0; j < n; j++) {
  952. fprintf(stderr, " %3d", gp[i].edges[j]);
  953. }
  954. fputs("\n", stderr);
  955. if (gp[i].ewgts) {
  956. fputs(" ewgts", stderr);
  957. for (size_t j = 0; j < n; j++) {
  958. fprintf(stderr, " %3f", gp[i].ewgts[j]);
  959. }
  960. fputs("\n", stderr);
  961. }
  962. if (gp[i].eweights) {
  963. fputs(" eweights", stderr);
  964. for (size_t j = 0; j < n; j++) {
  965. fprintf(stderr, " %3f", gp[i].eweights[j]);
  966. }
  967. fputs("\n", stderr);
  968. }
  969. if (gp[i].edists) {
  970. fputs(" edists", stderr);
  971. for (size_t j = 0; j < n; j++) {
  972. fprintf(stderr, " %3f", gp[i].edists[j]);
  973. }
  974. fputs("\n", stderr);
  975. }
  976. fputs("\n", stderr);
  977. }
  978. }
  979. void dumpClusterData (cluster_data* dp)
  980. {
  981. int i, j, sz;
  982. fprintf (stderr, "nvars %d nclusters %d ntoplevel %d\n", dp->nvars, dp->nclusters, dp->ntoplevel);
  983. fprintf (stderr, "Clusters:\n");
  984. for (i = 0; i < dp->nclusters; i++) {
  985. sz = dp->clustersizes[i];
  986. fprintf (stderr, " [%d] %d vars\n", i, sz);
  987. for (j = 0; j < sz; j++)
  988. fprintf (stderr, " %d", dp->clusters[i][j]);
  989. fprintf (stderr, "\n");
  990. }
  991. fprintf (stderr, "Toplevel:\n");
  992. for (i = 0; i < dp->ntoplevel; i++)
  993. fprintf (stderr, " %d\n", dp->toplevel[i]);
  994. fprintf (stderr, "Boxes:\n");
  995. for (i = 0; i < dp->nclusters; i++) {
  996. boxf bb = dp->bb[i];
  997. fprintf (stderr, " (%f,%f) (%f,%f)\n", bb.LL.x, bb.LL.y, bb.UR.x, bb.UR.y);
  998. }
  999. }
  1000. void dumpOpts (ipsep_options* opp, int nv)
  1001. {
  1002. int i;
  1003. fprintf (stderr, "diredges %d edge_gap %f noverlap %d gap (%f,%f)\n", opp->diredges, opp->edge_gap, opp->noverlap, opp->gap.x, opp->gap.y);
  1004. for (i = 0; i < nv; i++)
  1005. fprintf (stderr, " (%f,%f)\n", opp->nsize[i].x, opp->nsize[i].y);
  1006. if (opp->clusters)
  1007. dumpClusterData (opp->clusters);
  1008. }
  1009. #endif
  1010. /* majorization:
  1011. * Solve stress using majorization.
  1012. * Old neato attributes to incorporate:
  1013. * weight
  1014. * mode will be MODE_MAJOR, MODE_HIER or MODE_IPSEP
  1015. */
  1016. static void
  1017. majorization(graph_t *mg, graph_t * g, int nv, int mode, int model, int dim, adjust_data* am)
  1018. {
  1019. int ne;
  1020. int rv = 0;
  1021. node_t *v;
  1022. vtx_data *gp;
  1023. node_t** nodes;
  1024. #ifdef DIGCOLA
  1025. #ifdef IPSEPCOLA
  1026. expand_t margin;
  1027. #endif
  1028. #endif
  1029. int init = checkStart(g, nv, mode == MODE_HIER ? INIT_SELF : INIT_RANDOM);
  1030. int opts = checkExp (g);
  1031. if (init == INIT_SELF)
  1032. opts |= opt_smart_init;
  1033. double **coords = gv_calloc(dim, sizeof(double *));
  1034. coords[0] = gv_calloc(nv * dim, sizeof(double));
  1035. for (int i = 1; i < Ndim; i++) {
  1036. coords[i] = coords[0] + i * nv;
  1037. }
  1038. if (Verbose) {
  1039. fprintf(stderr, "model %d smart_init %d stresswt %d iterations %d tol %f\n",
  1040. model, init == INIT_SELF, opts & opt_exp_flag, MaxIter, Epsilon);
  1041. fprintf(stderr, "convert graph: ");
  1042. start_timer();
  1043. fprintf(stderr, "majorization\n");
  1044. }
  1045. gp = makeGraphData(g, nv, &ne, mode, model, &nodes);
  1046. if (Verbose) {
  1047. fprintf(stderr, "%d nodes %.2f sec\n", nv, elapsed_sec());
  1048. }
  1049. #ifdef DIGCOLA
  1050. if (mode != MODE_MAJOR) {
  1051. double lgap = late_double(g, agfindgraphattr(g, "levelsgap"), 0.0, -DBL_MAX);
  1052. if (mode == MODE_HIER) {
  1053. rv = stress_majorization_with_hierarchy(gp, nv, coords, nodes, Ndim,
  1054. opts, model, MaxIter, lgap);
  1055. }
  1056. #ifdef IPSEPCOLA
  1057. else {
  1058. char* str;
  1059. ipsep_options opt;
  1060. cluster_data cs = cluster_map(mg,g);
  1061. pointf *nsize = gv_calloc(nv, sizeof(pointf));
  1062. opt.edge_gap = lgap;
  1063. opt.nsize = nsize;
  1064. opt.clusters = cs;
  1065. str = agget(g, "diredgeconstraints");
  1066. if (mapbool(str)) {
  1067. opt.diredges = 1;
  1068. if(Verbose)
  1069. fprintf(stderr,"Generating Edge Constraints...\n");
  1070. } else if (str && !strncasecmp(str,"hier",4)) {
  1071. opt.diredges = 2;
  1072. if(Verbose)
  1073. fprintf(stderr,"Generating DiG-CoLa Edge Constraints...\n");
  1074. }
  1075. else opt.diredges = 0;
  1076. if (am->mode == AM_IPSEP) {
  1077. opt.noverlap = 1;
  1078. if(Verbose)
  1079. fprintf(stderr,"Generating Non-overlap Constraints...\n");
  1080. } else if (am->mode == AM_VPSC) {
  1081. opt.noverlap = 2;
  1082. if(Verbose)
  1083. fprintf(stderr,"Removing overlaps as postprocess...\n");
  1084. }
  1085. else opt.noverlap = 0;
  1086. margin = sepFactor (g);
  1087. /* Multiply by 2 since opt.gap is the gap size, not the margin */
  1088. if (margin.doAdd) {
  1089. opt.gap.x = 2.0*PS2INCH(margin.x);
  1090. opt.gap.y = 2.0*PS2INCH(margin.y);
  1091. }
  1092. else opt.gap.x = opt.gap.y = 2.0*PS2INCH(DFLT_MARGIN);
  1093. if(Verbose)
  1094. fprintf(stderr,"gap=%f,%f\n",opt.gap.x,opt.gap.y);
  1095. {
  1096. size_t i = 0;
  1097. for (v = agfstnode(g); v; v = agnxtnode(g, v),i++) {
  1098. nsize[i].x = ND_width(v);
  1099. nsize[i].y = ND_height(v);
  1100. }
  1101. }
  1102. #ifdef DEBUG_COLA
  1103. fprintf (stderr, "nv %d ne %d Ndim %d model %d MaxIter %d\n", nv, ne, Ndim, model, MaxIter);
  1104. fprintf (stderr, "Nodes:\n");
  1105. for (int i = 0; i < nv; i++) {
  1106. fprintf (stderr, " %s (%f,%f)\n", nodes[i]->name, coords[0][i], coords[1][i]);
  1107. }
  1108. fprintf (stderr, "\n");
  1109. dumpData(g, gp, nv, ne);
  1110. fprintf (stderr, "\n");
  1111. dumpOpts (&opt, nv);
  1112. #endif
  1113. rv = stress_majorization_cola(gp, nv, coords, nodes, Ndim, model, MaxIter, &opt);
  1114. freeClusterData(cs);
  1115. free (nsize);
  1116. }
  1117. #endif
  1118. }
  1119. else
  1120. #endif
  1121. rv = stress_majorization_kD_mkernel(gp, nv, coords, nodes, Ndim, opts, model, MaxIter);
  1122. if (rv < 0) {
  1123. agerr(AGPREV, "layout aborted\n");
  1124. }
  1125. else for (v = agfstnode(g); v; v = agnxtnode(g, v)) { /* store positions back in nodes */
  1126. int idx = ND_id(v);
  1127. for (int i = 0; i < Ndim; i++) {
  1128. ND_pos(v)[i] = coords[i][idx];
  1129. }
  1130. }
  1131. freeGraphData(gp);
  1132. free(coords[0]);
  1133. free(coords);
  1134. free(nodes);
  1135. }
  1136. static void subset_model(Agraph_t * G, int nG)
  1137. {
  1138. int i, j, ne;
  1139. vtx_data *gp;
  1140. gp = makeGraphData(G, nG, &ne, MODE_KK, MODEL_SUBSET, NULL);
  1141. DistType **Dij = compute_apsp_artificial_weights(gp, nG);
  1142. for (i = 0; i < nG; i++) {
  1143. for (j = 0; j < nG; j++) {
  1144. GD_dist(G)[i][j] = Dij[i][j];
  1145. }
  1146. }
  1147. free(Dij[0]);
  1148. free(Dij);
  1149. freeGraphData(gp);
  1150. }
  1151. /* mds_model:
  1152. * Assume the matrix already contains shortest path values.
  1153. * Use the actual lengths provided the input for edges.
  1154. */
  1155. static void mds_model(graph_t * g)
  1156. {
  1157. long i, j;
  1158. node_t *v;
  1159. edge_t *e;
  1160. for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
  1161. for (e = agfstout(g, v); e; e = agnxtout(g, e)) {
  1162. i = AGSEQ(agtail(e));
  1163. j = AGSEQ(aghead(e));
  1164. if (i == j)
  1165. continue;
  1166. GD_dist(g)[i][j] = GD_dist(g)[j][i] = ED_dist(e);
  1167. }
  1168. }
  1169. }
  1170. /* kkNeato:
  1171. * Solve using gradient descent a la Kamada-Kawai.
  1172. */
  1173. static void kkNeato(Agraph_t * g, int nG, int model)
  1174. {
  1175. if (model == MODEL_SUBSET) {
  1176. subset_model(g, nG);
  1177. } else if (model == MODEL_CIRCUIT) {
  1178. if (!circuit_model(g, nG)) {
  1179. agwarningf(
  1180. "graph %s is disconnected. Hence, the circuit model\n",
  1181. agnameof(g));
  1182. agerr(AGPREV,
  1183. "is undefined. Reverting to the shortest path model.\n");
  1184. agerr(AGPREV,
  1185. "Alternatively, consider running neato using -Gpack=true or decomposing\n");
  1186. agerr(AGPREV, "the graph into connected components.\n");
  1187. shortest_path(g, nG);
  1188. }
  1189. } else if (model == MODEL_MDS) {
  1190. shortest_path(g, nG);
  1191. mds_model(g);
  1192. } else
  1193. shortest_path(g, nG);
  1194. initial_positions(g, nG);
  1195. diffeq_model(g, nG);
  1196. if (Verbose) {
  1197. fprintf(stderr, "Solving model %d iterations %d tol %f\n",
  1198. model, MaxIter, Epsilon);
  1199. start_timer();
  1200. }
  1201. solve_model(g, nG);
  1202. }
  1203. /* neatoLayout:
  1204. * Use stress optimization to layout a single component
  1205. */
  1206. static void
  1207. neatoLayout(Agraph_t * mg, Agraph_t * g, int layoutMode, int layoutModel,
  1208. adjust_data* am)
  1209. {
  1210. int nG;
  1211. char *str;
  1212. if ((str = agget(g, "maxiter")))
  1213. MaxIter = atoi(str);
  1214. else if (layoutMode == MODE_MAJOR)
  1215. MaxIter = DFLT_ITERATIONS;
  1216. else if (layoutMode == MODE_SGD)
  1217. MaxIter = 30;
  1218. else
  1219. MaxIter = 100 * agnnodes(g);
  1220. nG = scan_graph_mode(g, layoutMode);
  1221. if (nG < 2 || MaxIter < 0)
  1222. return;
  1223. if (layoutMode == MODE_KK)
  1224. kkNeato(g, nG, layoutModel);
  1225. else if (layoutMode == MODE_SGD)
  1226. sgd(g, layoutModel);
  1227. else
  1228. majorization(mg, g, nG, layoutMode, layoutModel, Ndim, am);
  1229. }
  1230. /* addZ;
  1231. * If dimension == 3 and z attribute is declared,
  1232. * attach z value to nodes if not defined.
  1233. */
  1234. static void addZ (Agraph_t* g)
  1235. {
  1236. node_t* n;
  1237. char buf[BUFSIZ];
  1238. if (Ndim >= 3 && N_z) {
  1239. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  1240. snprintf(buf, sizeof(buf), "%lf", POINTS_PER_INCH * ND_pos(n)[2]);
  1241. agxset(n, N_z, buf);
  1242. }
  1243. }
  1244. }
  1245. #ifdef IPSEPCOLA
  1246. static void
  1247. addCluster (graph_t* g)
  1248. {
  1249. graph_t *subg;
  1250. for (subg = agfstsubg(agroot(g)); subg; subg = agnxtsubg(subg)) {
  1251. if (is_a_cluster(subg)) {
  1252. agbindrec(subg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
  1253. add_cluster(g, subg);
  1254. compute_bb(subg);
  1255. }
  1256. }
  1257. }
  1258. #endif
  1259. /* doEdges:
  1260. * Simple wrapper to compute graph's bb, then route edges after
  1261. * a possible aspect ratio adjustment.
  1262. */
  1263. static void doEdges(Agraph_t* g)
  1264. {
  1265. compute_bb(g);
  1266. spline_edges0(g, true);
  1267. }
  1268. /* neato_layout:
  1269. */
  1270. void neato_layout(Agraph_t * g)
  1271. {
  1272. int layoutMode;
  1273. int model;
  1274. pack_mode mode;
  1275. pack_info pinfo;
  1276. adjust_data am;
  1277. double save_scale = PSinputscale;
  1278. if (Nop) {
  1279. int ret;
  1280. PSinputscale = POINTS_PER_INCH;
  1281. neato_init_graph(g);
  1282. addZ (g);
  1283. ret = init_nop(g, 1);
  1284. if (ret < 0) {
  1285. agerr(AGPREV, "as required by the -n flag\n");
  1286. return;
  1287. }
  1288. else gv_postprocess(g, 0);
  1289. } else {
  1290. bool noTranslate = mapbool(agget(g, "notranslate"));
  1291. PSinputscale = get_inputscale (g);
  1292. neato_init_graph(g);
  1293. layoutMode = neatoMode(g);
  1294. graphAdjustMode (g, &am, 0);
  1295. model = neatoModel(g);
  1296. mode = getPackModeInfo (g, l_undef, &pinfo);
  1297. Pack = getPack(g, -1, CL_OFFSET);
  1298. /* pack if just packmode defined. */
  1299. if (mode == l_undef) {
  1300. /* If the user has not indicated packing but we are
  1301. * using the new neato, turn packing on.
  1302. */
  1303. if (Pack < 0 && layoutMode)
  1304. Pack = CL_OFFSET;
  1305. pinfo.mode = l_node;
  1306. } else if (Pack < 0)
  1307. Pack = CL_OFFSET;
  1308. if (Pack >= 0) {
  1309. graph_t *gc;
  1310. graph_t **cc;
  1311. size_t n_cc;
  1312. bool pin;
  1313. cc = pccomps(g, &n_cc, cc_pfx, &pin);
  1314. if (n_cc > 1) {
  1315. bool *bp;
  1316. for (size_t i = 0; i < n_cc; i++) {
  1317. gc = cc[i];
  1318. (void)graphviz_node_induce(gc, NULL);
  1319. neatoLayout(g, gc, layoutMode, model, &am);
  1320. removeOverlapWith(gc, &am);
  1321. setEdgeType (gc, EDGETYPE_LINE);
  1322. if (noTranslate) doEdges(gc);
  1323. else spline_edges(gc);
  1324. }
  1325. if (pin) {
  1326. bp = gv_calloc(n_cc, sizeof(bool));
  1327. bp[0] = true;
  1328. } else
  1329. bp = NULL;
  1330. pinfo.margin = (unsigned)Pack;
  1331. pinfo.fixed = bp;
  1332. pinfo.doSplines = true;
  1333. packGraphs(n_cc, cc, g, &pinfo);
  1334. free(bp);
  1335. }
  1336. else {
  1337. neatoLayout(g, g, layoutMode, model, &am);
  1338. removeOverlapWith(g, &am);
  1339. if (noTranslate) doEdges(g);
  1340. else spline_edges(g);
  1341. }
  1342. compute_bb(g);
  1343. addZ (g);
  1344. /* cleanup and remove component subgraphs */
  1345. for (size_t i = 0; i < n_cc; i++) {
  1346. gc = cc[i];
  1347. free_scan_graph(gc);
  1348. agdelrec (gc, "Agraphinfo_t");
  1349. agdelete(g, gc);
  1350. }
  1351. free (cc);
  1352. #ifdef IPSEPCOLA
  1353. addCluster (g);
  1354. #endif
  1355. } else {
  1356. neatoLayout(g, g, layoutMode, model, &am);
  1357. removeOverlapWith(g, &am);
  1358. addZ (g);
  1359. if (noTranslate) doEdges(g);
  1360. else spline_edges(g);
  1361. }
  1362. gv_postprocess(g, !noTranslate);
  1363. }
  1364. PSinputscale = save_scale;
  1365. }
  1366. /**
  1367. * @dir lib/neatogen
  1368. * @brief "spring model" layout engine, API neatogen/neatoprocs.h
  1369. * @ingroup engines
  1370. *
  1371. * [Neato layout user manual](https://graphviz.org/docs/layouts/neato/)
  1372. *
  1373. * Other @ref engines
  1374. */