dotinit.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  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 <assert.h>
  11. #include <limits.h>
  12. #include <time.h>
  13. #include <dotgen/dot.h>
  14. #include <pack/pack.h>
  15. #include <dotgen/aspect.h>
  16. #include <stdbool.h>
  17. #include <stddef.h>
  18. #include <stdint.h>
  19. #include <util/agxbuf.h>
  20. #include <util/alloc.h>
  21. #include <util/streq.h>
  22. static void
  23. dot_init_subg(graph_t * g, graph_t* droot)
  24. {
  25. graph_t* subg;
  26. if ((g != agroot(g)))
  27. agbindrec(g, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
  28. if (g == droot)
  29. GD_dotroot(agroot(g)) = droot;
  30. for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
  31. dot_init_subg(subg, droot);
  32. }
  33. }
  34. static void
  35. dot_init_node(node_t * n)
  36. {
  37. agbindrec(n, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true); //graph custom data
  38. common_init_node(n);
  39. gv_nodesize(n, GD_flip(agraphof(n)));
  40. alloc_elist(4, ND_in(n));
  41. alloc_elist(4, ND_out(n));
  42. alloc_elist(2, ND_flat_in(n));
  43. alloc_elist(2, ND_flat_out(n));
  44. alloc_elist(2, ND_other(n));
  45. ND_UF_size(n) = 1;
  46. }
  47. static void
  48. dot_init_edge(edge_t * e)
  49. {
  50. char *tailgroup, *headgroup;
  51. agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true); //graph custom data
  52. common_init_edge(e);
  53. ED_weight(e) = late_int(e, E_weight, 1, 0);
  54. tailgroup = late_string(agtail(e), N_group, "");
  55. headgroup = late_string(aghead(e), N_group, "");
  56. ED_count(e) = ED_xpenalty(e) = 1;
  57. if (tailgroup[0] && (tailgroup == headgroup)) {
  58. ED_xpenalty(e) = CL_CROSS;
  59. ED_weight(e) *= 100;
  60. }
  61. if (nonconstraint_edge(e)) {
  62. ED_xpenalty(e) = 0;
  63. ED_weight(e) = 0;
  64. }
  65. {
  66. int showboxes = late_int(e, E_showboxes, 0, 0);
  67. if (showboxes > UCHAR_MAX) {
  68. showboxes = UCHAR_MAX;
  69. }
  70. ED_showboxes(e) = (unsigned char)showboxes;
  71. }
  72. ED_minlen(e) = late_int(e, E_minlen, 1, 0);
  73. }
  74. void
  75. dot_init_node_edge(graph_t * g)
  76. {
  77. node_t *n;
  78. edge_t *e;
  79. for (n = agfstnode(g); n; n = agnxtnode(g, n))
  80. dot_init_node(n);
  81. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  82. for (e = agfstout(g, n); e; e = agnxtout(g, e))
  83. dot_init_edge(e);
  84. }
  85. }
  86. static void
  87. dot_cleanup_node(node_t * n)
  88. {
  89. free_list(ND_in(n));
  90. free_list(ND_out(n));
  91. free_list(ND_flat_out(n));
  92. free_list(ND_flat_in(n));
  93. free_list(ND_other(n));
  94. free_label(ND_label(n));
  95. free_label(ND_xlabel(n));
  96. if (ND_shape(n))
  97. ND_shape(n)->fns->freefn(n);
  98. agdelrec(n, "Agnodeinfo_t");
  99. }
  100. static void free_virtual_edge_list(node_t * n)
  101. {
  102. edge_t *e;
  103. for (size_t i = ND_in(n).size - 1; i != SIZE_MAX; i--) {
  104. e = ND_in(n).list[i];
  105. delete_fast_edge(e);
  106. free(e->base.data);
  107. free(e);
  108. }
  109. for (size_t i = ND_out(n).size - 1; i != SIZE_MAX; i--) {
  110. e = ND_out(n).list[i];
  111. delete_fast_edge(e);
  112. free(e->base.data);
  113. free(e);
  114. }
  115. }
  116. static void free_virtual_node_list(node_t * vn)
  117. {
  118. node_t *next_vn;
  119. while (vn) {
  120. next_vn = ND_next(vn);
  121. free_virtual_edge_list(vn);
  122. if (ND_node_type(vn) == VIRTUAL) {
  123. free_list(ND_out(vn));
  124. free_list(ND_in(vn));
  125. free(vn->base.data);
  126. free(vn);
  127. }
  128. vn = next_vn;
  129. }
  130. }
  131. static void
  132. dot_cleanup_graph(graph_t * g)
  133. {
  134. int i;
  135. graph_t *subg;
  136. for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
  137. dot_cleanup_graph(subg);
  138. }
  139. if (! agbindrec(g, "Agraphinfo_t", 0, true)) return;
  140. free (GD_clust(g));
  141. free (GD_rankleader(g));
  142. free_list(GD_comp(g));
  143. if (GD_rank(g)) {
  144. for (i = GD_minrank(g); i <= GD_maxrank(g); i++)
  145. free(GD_rank(g)[i].av);
  146. if (GD_minrank(g) == -1)
  147. free(GD_rank(g)-1);
  148. else
  149. free(GD_rank(g));
  150. }
  151. if (g != agroot(g)) {
  152. free_label (GD_label(g));
  153. }
  154. }
  155. /* delete the layout (but retain the underlying graph) */
  156. void dot_cleanup(graph_t * g)
  157. {
  158. node_t *n;
  159. edge_t *e;
  160. free_virtual_node_list(GD_nlist(g));
  161. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  162. for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
  163. gv_cleanup_edge(e);
  164. }
  165. dot_cleanup_node(n);
  166. }
  167. dot_cleanup_graph(g);
  168. }
  169. #ifdef DEBUG
  170. int
  171. fastn (graph_t * g)
  172. {
  173. node_t* u;
  174. int cnt = 0;
  175. for (u = GD_nlist(g); u; u = ND_next(u)) cnt++;
  176. return cnt;
  177. }
  178. #if DEBUG > 1
  179. static void
  180. dumpRanks (graph_t * g)
  181. {
  182. int i, j;
  183. node_t* u;
  184. rank_t *rank = GD_rank(g);
  185. int rcnt = 0;
  186. for (i = GD_minrank(g); i <= GD_maxrank(g); i++) {
  187. fprintf (stderr, "[%d] :", i);
  188. for (j = 0; j < rank[i].n; j++) {
  189. u = rank[i].v[j];
  190. rcnt++;
  191. if (streq(agnameof(u),"virtual"))
  192. fprintf (stderr, " %x", u);
  193. else
  194. fprintf (stderr, " %s", agnameof(u));
  195. }
  196. fprintf (stderr, "\n");
  197. }
  198. fprintf (stderr, "count %d rank count = %d\n", fastn(g), rcnt);
  199. }
  200. #endif
  201. #endif
  202. static void
  203. remove_from_rank (Agraph_t * g, Agnode_t* n)
  204. {
  205. Agnode_t* v = NULL;
  206. int j, rk = ND_rank(n);
  207. for (j = 0; j < GD_rank(g)[rk].n; j++) {
  208. v = GD_rank(g)[rk].v[j];
  209. if (v == n) {
  210. for (j++; j < GD_rank(g)[rk].n; j++) {
  211. GD_rank(g)[rk].v[j-1] = GD_rank(g)[rk].v[j];
  212. }
  213. GD_rank(g)[rk].n--;
  214. break;
  215. }
  216. }
  217. assert (v == n); /* if found */
  218. }
  219. /* removeFill:
  220. * This removes all of the fill nodes added in mincross.
  221. * It appears to be sufficient to remove them only from the
  222. * rank array and fast node list of the root graph.
  223. */
  224. static void
  225. removeFill (Agraph_t * g)
  226. {
  227. Agnode_t* n;
  228. Agnode_t* nxt;
  229. Agraph_t* sg = agsubg (g, "_new_rank", 0);
  230. if (!sg) return;
  231. for (n = agfstnode(sg); n; n = nxt) {
  232. nxt = agnxtnode(sg, n);
  233. delete_fast_node (g, n);
  234. remove_from_rank (g, n);
  235. dot_cleanup_node (n);
  236. agdelnode(g, n);
  237. }
  238. agdelsubg (g, sg);
  239. }
  240. #define agnodeattr(g,n,v) agattr(g,AGNODE,n,v)
  241. static void
  242. attach_phase_attrs (Agraph_t * g, int maxphase)
  243. {
  244. Agsym_t* rk = agnodeattr(g,"rank","");
  245. Agsym_t* order = agnodeattr(g,"order","");
  246. Agnode_t* n;
  247. agxbuf buf = {0};
  248. for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
  249. if (maxphase >= 1) {
  250. agxbprint(&buf, "%d", ND_rank(n));
  251. agxset(n, rk, agxbuse(&buf));
  252. }
  253. if (maxphase >= 2) {
  254. agxbprint(&buf, "%d", ND_order(n));
  255. agxset(n, order, agxbuse(&buf));
  256. }
  257. }
  258. agxbfree(&buf);
  259. }
  260. static void dotLayout(Agraph_t * g)
  261. {
  262. int maxphase = late_int(g, agfindgraphattr(g,"phase"), -1, 1);
  263. setEdgeType (g, EDGETYPE_SPLINE);
  264. setAspect(g);
  265. dot_init_subg(g,g);
  266. dot_init_node_edge(g);
  267. dot_rank(g);
  268. if (maxphase == 1) {
  269. attach_phase_attrs (g, 1);
  270. return;
  271. }
  272. dot_mincross(g);
  273. if (maxphase == 2) {
  274. attach_phase_attrs (g, 2);
  275. return;
  276. }
  277. dot_position(g);
  278. if (maxphase == 3) {
  279. attach_phase_attrs (g, 2); /* positions will be attached on output */
  280. return;
  281. }
  282. if (GD_flags(g) & NEW_RANK)
  283. removeFill (g);
  284. dot_sameports(g);
  285. dot_splines(g);
  286. if (mapbool(agget(g, "compound")))
  287. dot_compoundEdges(g);
  288. }
  289. static void
  290. initSubg (Agraph_t* sg, Agraph_t* g)
  291. {
  292. agbindrec(sg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
  293. GD_drawing(sg) = gv_alloc(sizeof(layout_t));
  294. GD_drawing(sg)->quantum = GD_drawing(g)->quantum;
  295. GD_drawing(sg)->dpi = GD_drawing(g)->dpi;
  296. GD_gvc(sg) = GD_gvc (g);
  297. GD_charset(sg) = GD_charset (g);
  298. GD_rankdir2(sg) = GD_rankdir2 (g);
  299. GD_nodesep(sg) = GD_nodesep(g);
  300. GD_ranksep(sg) = GD_ranksep(g);
  301. GD_fontnames(sg) = GD_fontnames(g);
  302. }
  303. /* attachPos:
  304. * the packing library assumes all units are in inches stored in ND_pos, so we
  305. * have to copy the position info there.
  306. */
  307. static void
  308. attachPos (Agraph_t* g)
  309. {
  310. node_t* np;
  311. double* ps = gv_calloc(2 * agnnodes(g), sizeof(double));
  312. for (np = agfstnode(g); np; np = agnxtnode(g, np)) {
  313. ND_pos(np) = ps;
  314. ps[0] = PS2INCH(ND_coord(np).x);
  315. ps[1] = PS2INCH(ND_coord(np).y);
  316. ps += 2;
  317. }
  318. }
  319. /* resetCoord:
  320. * Store new position info from pack library call, stored in ND_pos in inches,
  321. * back to ND_coord in points.
  322. */
  323. static void
  324. resetCoord (Agraph_t* g)
  325. {
  326. node_t* np = agfstnode(g);
  327. double* sp = ND_pos(np);
  328. double* ps = sp;
  329. for (np = agfstnode(g); np; np = agnxtnode(g, np)) {
  330. ND_pos(np) = 0;
  331. ND_coord(np).x = INCH2PS(ps[0]);
  332. ND_coord(np).y = INCH2PS(ps[1]);
  333. ps += 2;
  334. }
  335. free (sp);
  336. }
  337. static void
  338. copyCluster (Agraph_t* scl, Agraph_t* cl)
  339. {
  340. int nclust, j;
  341. Agraph_t* cg;
  342. agbindrec(cl, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
  343. GD_bb(cl) = GD_bb(scl);
  344. GD_label_pos(cl) = GD_label_pos(scl);
  345. memcpy(GD_border(cl), GD_border(scl), 4*sizeof(pointf));
  346. nclust = GD_n_cluster(cl) = GD_n_cluster(scl);
  347. GD_clust(cl) = gv_calloc(nclust + 1, sizeof(Agraph_t*));
  348. for (j = 1; j <= nclust; j++) {
  349. cg = mapClust(GD_clust(scl)[j]);
  350. GD_clust(cl)[j] = cg;
  351. copyCluster (GD_clust(scl)[j], cg);
  352. }
  353. /* transfer cluster label to original cluster */
  354. GD_label(cl) = GD_label(scl);
  355. GD_label(scl) = NULL;
  356. }
  357. /* copyClusterInfo:
  358. * Copy cluster tree and info from components to main graph.
  359. * Note that the original clusters have no Agraphinfo_t at this time.
  360. */
  361. static void copyClusterInfo(size_t ncc, Agraph_t **ccs, Agraph_t *root) {
  362. int j, nclust = 0;
  363. Agraph_t* sg;
  364. Agraph_t* cg;
  365. for (size_t i = 0; i < ncc; i++)
  366. nclust += GD_n_cluster(ccs[i]);
  367. GD_n_cluster(root) = nclust;
  368. GD_clust(root) = gv_calloc(nclust + 1, sizeof(Agraph_t*));
  369. nclust = 1;
  370. for (size_t i = 0; i < ncc; i++) {
  371. sg = ccs[i];
  372. for (j = 1; j <= GD_n_cluster(sg); j++) {
  373. cg = mapClust(GD_clust(sg)[j]);
  374. GD_clust(root)[nclust++] = cg;
  375. copyCluster (GD_clust(sg)[j], cg);
  376. }
  377. }
  378. }
  379. /* doDot:
  380. * Assume g has nodes.
  381. */
  382. static void doDot (Agraph_t* g)
  383. {
  384. Agraph_t **ccs;
  385. Agraph_t *sg;
  386. pack_info pinfo;
  387. int Pack = getPack(g, -1, CL_OFFSET);
  388. pack_mode mode = getPackModeInfo (g, l_undef, &pinfo);
  389. getPackInfo(g, l_node, CL_OFFSET, &pinfo);
  390. if (mode == l_undef && Pack < 0) {
  391. /* No pack information; use old dot with components
  392. * handled during layout
  393. */
  394. dotLayout(g);
  395. } else {
  396. /* fill in default values */
  397. if (mode == l_undef)
  398. pinfo.mode = l_graph;
  399. else if (Pack < 0)
  400. Pack = CL_OFFSET;
  401. assert(Pack >= 0);
  402. pinfo.margin = (unsigned)Pack;
  403. pinfo.fixed = NULL;
  404. /* components using clusters */
  405. size_t ncc;
  406. ccs = cccomps(g, &ncc, 0);
  407. if (ncc == 1) {
  408. dotLayout(g);
  409. } else if (GD_drawing(g)->ratio_kind == R_NONE) {
  410. pinfo.doSplines = true;
  411. for (size_t i = 0; i < ncc; i++) {
  412. sg = ccs[i];
  413. initSubg (sg, g);
  414. dotLayout (sg);
  415. }
  416. attachPos (g);
  417. packSubgraphs(ncc, ccs, g, &pinfo);
  418. resetCoord (g);
  419. copyClusterInfo (ncc, ccs, g);
  420. } else {
  421. /* Not sure what semantics should be for non-trivial ratio
  422. * attribute with multiple components.
  423. * One possibility is to layout nodes, pack, then apply the ratio
  424. * adjustment. We would then have to re-adjust all positions.
  425. */
  426. dotLayout(g);
  427. }
  428. for (size_t i = 0; i < ncc; i++) {
  429. free (GD_drawing(ccs[i]));
  430. dot_cleanup_graph(ccs[i]);
  431. agdelete(g, ccs[i]);
  432. }
  433. free(ccs);
  434. }
  435. }
  436. void dot_layout(Agraph_t * g)
  437. {
  438. if (agnnodes(g)) doDot (g);
  439. dotneato_postprocess(g);
  440. }
  441. Agraph_t * dot_root (void* p)
  442. {
  443. return GD_dotroot(agroot(p));
  444. }
  445. /**
  446. * @defgroup engines Graphviz layout engines
  447. * @{
  448. * @dir lib/dotgen
  449. * @brief [hierarchical or layered](https://en.wikipedia.org/wiki/Layered_graph_drawing) layout engine, API dotgen/dotprocs.h
  450. *
  451. * [Dot layout user manual](https://graphviz.org/docs/layouts/dot/)
  452. *
  453. * Other @ref engines
  454. * @}
  455. */