patchwork.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  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 <stdio.h>
  11. #include <stdlib.h>
  12. #include <patchwork/patchwork.h>
  13. #include <patchwork/tree_map.h>
  14. #include <common/render.h>
  15. #include <util/alloc.h>
  16. typedef struct treenode_t treenode_t;
  17. struct treenode_t {
  18. double area;
  19. double child_area;
  20. rectangle r;
  21. treenode_t *leftchild, *rightsib;
  22. union {
  23. Agraph_t *subg;
  24. Agnode_t *n;
  25. } u;
  26. int kind;
  27. size_t n_children;
  28. };
  29. #define DFLT_SZ 1.0
  30. #define SCALE 1000.0 /* scale up so that 1 is a reasonable default size */
  31. #ifdef DEBUG
  32. void dumpTree (treenode_t* r, int ind)
  33. {
  34. int i;
  35. treenode_t* cp;
  36. for (i=0; i < ind; i++) fputs(" ", stderr);
  37. fprintf (stderr, "%s (%f)\n", (r->kind == AGNODE?agnameof(r->u.n):agnameof(r->u.subg)), r->area);
  38. for (cp = r->leftchild; cp; cp = cp->rightsib)
  39. dumpTree (cp, ind+1);
  40. }
  41. #endif
  42. /* fullArea:
  43. * Allow extra area for specified inset. Assume p->kind = AGRAPH
  44. * and p->child_area is set. At present, inset is additive; we
  45. * may want to allow a multiplicative inset as well.
  46. */
  47. static double fullArea (treenode_t* p, attrsym_t* mp)
  48. {
  49. double m = late_double (p->u.subg, mp, 0, 0);
  50. double wid = 2.0 * m + sqrt(p->child_area);
  51. return wid * wid;
  52. }
  53. static double getArea (void* obj, attrsym_t* ap)
  54. {
  55. double area = late_double (obj, ap, DFLT_SZ, 0);
  56. if (area == 0) area = DFLT_SZ;
  57. area *= SCALE;
  58. return area;
  59. }
  60. /* mkTreeNode:
  61. */
  62. static treenode_t* mkTreeNode (Agnode_t* n, attrsym_t* ap)
  63. {
  64. treenode_t *p = gv_alloc(sizeof(treenode_t));
  65. p->area = getArea (n, ap);
  66. p->kind = AGNODE;
  67. p->u.n = n;
  68. return p;
  69. }
  70. #define INSERT(cp) if(!first) first=cp; if(prev) prev->rightsib=cp; prev=cp;
  71. /* mkTree:
  72. * Recursively build tree from graph
  73. * Pre-condition: agnnodes(g) != 0
  74. */
  75. static treenode_t *mkTree (Agraph_t * g, attrsym_t* gp, attrsym_t* ap, attrsym_t* mp)
  76. {
  77. treenode_t *p = gv_alloc(sizeof(treenode_t));
  78. Agraph_t *subg;
  79. Agnode_t *n;
  80. treenode_t *cp;
  81. treenode_t *first = 0;
  82. treenode_t *prev = 0;
  83. int i;
  84. double area = 0;
  85. p->kind = AGRAPH;
  86. p->u.subg = g;
  87. size_t n_children = 0;
  88. for (i = 1; i <= GD_n_cluster(g); i++) {
  89. subg = GD_clust(g)[i];
  90. cp = mkTree (subg, gp, ap, mp);
  91. n_children++;
  92. area += cp->area;
  93. INSERT(cp);
  94. }
  95. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  96. if (SPARENT(n))
  97. continue;
  98. cp = mkTreeNode (n, ap);
  99. n_children++;
  100. area += cp->area;
  101. INSERT(cp);
  102. SPARENT(n) = g;
  103. }
  104. p->n_children = n_children;
  105. if (n_children) {
  106. p->child_area = area;
  107. p->area = fullArea (p, mp);
  108. }
  109. else {
  110. p->area = getArea (g, gp);
  111. }
  112. p->leftchild = first;
  113. return p;
  114. }
  115. static int nodecmp(const void *x, const void *y) {
  116. const treenode_t *const *p0 = x;
  117. const treenode_t *const *p1 = y;
  118. if ((*p0)->area < (*p1)->area) {
  119. return 1;
  120. }
  121. if ((*p0)->area > (*p1)->area) {
  122. return -1;
  123. }
  124. return 0;
  125. }
  126. static void layoutTree(treenode_t * tree)
  127. {
  128. rectangle *recs;
  129. treenode_t* cp;
  130. /* if (tree->kind == AGNODE) return; */
  131. if (tree->n_children == 0) return;
  132. size_t nc = tree->n_children;
  133. treenode_t** nodes = gv_calloc(nc, sizeof(treenode_t*));
  134. cp = tree->leftchild;
  135. for (size_t i = 0; i < nc; i++) {
  136. nodes[i] = cp;
  137. cp = cp->rightsib;
  138. }
  139. qsort(nodes, nc, sizeof(treenode_t *), nodecmp);
  140. double* areas_sorted = gv_calloc(nc, sizeof(double));
  141. for (size_t i = 0; i < nc; i++) {
  142. areas_sorted[i] = nodes[i]->area;
  143. }
  144. if (tree->area == tree->child_area)
  145. recs = gv_tree_map(nc, areas_sorted, tree->r);
  146. else {
  147. rectangle crec;
  148. double disc, delta, m, h = tree->r.size[1], w = tree->r.size[0];
  149. crec.x[0] = tree->r.x[0];
  150. crec.x[1] = tree->r.x[1];
  151. delta = h - w;
  152. disc = sqrt(delta*delta + 4.0*tree->child_area);
  153. m = (h + w - disc)/2.0;
  154. crec.size[0] = w - m;
  155. crec.size[1] = h - m;
  156. recs = gv_tree_map(nc, areas_sorted, crec);
  157. }
  158. if (Verbose)
  159. fprintf (stderr, "rec %f %f %f %f\n", tree->r.x[0], tree->r.x[1], tree->r.size[0], tree->r.size[1]);
  160. for (size_t i = 0; i < nc; i++) {
  161. nodes[i]->r = recs[i];
  162. if (Verbose)
  163. fprintf (stderr, "%f - %f %f %f %f = %f (%f %f %f %f)\n", areas_sorted[i],
  164. recs[i].x[0]-recs[i].size[0]*0.5, recs[i].x[1]-recs[i].size[1]*0.5,
  165. recs[i].x[0]+recs[i].size[0]*0.5, recs[i].x[1]+recs[i].size[1]*0.5, recs[i].size[0]*recs[i].size[1],
  166. recs[i].x[0], recs[i].x[1], recs[i].size[0], recs[i].size[1]);
  167. }
  168. free (nodes);
  169. free (areas_sorted);
  170. free (recs);
  171. cp = tree->leftchild;
  172. for (size_t i = 0; i < nc; i++) {
  173. if (cp->kind == AGRAPH)
  174. layoutTree (cp);
  175. cp = cp->rightsib;
  176. }
  177. }
  178. static void finishNode(node_t * n)
  179. {
  180. char buf [40];
  181. if (N_fontsize) {
  182. char* str = agxget(n, N_fontsize);
  183. if (*str == '\0') {
  184. snprintf(buf, sizeof(buf), "%.03f", ND_ht(n)*0.7);
  185. agxset(n, N_fontsize, buf);
  186. }
  187. }
  188. common_init_node (n);
  189. }
  190. static void walkTree(treenode_t * tree)
  191. {
  192. treenode_t *p;
  193. Agnode_t *n;
  194. pointf center;
  195. rectangle rr;
  196. boxf r;
  197. double x0, y0, wd, ht;
  198. if (tree->kind == AGRAPH) {
  199. for (p = tree->leftchild; p; p = p->rightsib)
  200. walkTree (p);
  201. x0 = tree->r.x[0];
  202. y0 = tree->r.x[1];
  203. wd = tree->r.size[0];
  204. ht = tree->r.size[1];
  205. r.LL.x = x0 - wd/2.0;
  206. r.LL.y = y0 - ht/2.0;
  207. r.UR.x = r.LL.x + wd;
  208. r.UR.y = r.LL.y + ht;
  209. GD_bb(tree->u.subg) = r;
  210. }
  211. else {
  212. rr = tree->r;
  213. center.x = rr.x[0];
  214. center.y = rr.x[1];
  215. n = tree->u.n;
  216. ND_coord(n) = center;
  217. ND_width(n) = PS2INCH(rr.size[0]);
  218. ND_height(n) = PS2INCH(rr.size[1]);
  219. gv_nodesize(n, GD_flip(agraphof(n)));
  220. finishNode(n);
  221. if (Verbose)
  222. fprintf(stderr,"%s coord %.5g %.5g ht %f width %f\n",
  223. agnameof(n), ND_coord(n).x, ND_coord(n).y, ND_ht(n), ND_xsize(n));
  224. }
  225. }
  226. /* freeTree:
  227. */
  228. static void freeTree (treenode_t* tp)
  229. {
  230. treenode_t* cp = tp->leftchild;
  231. treenode_t* rp;
  232. size_t nc = tp->n_children;
  233. for (size_t i = 0; i < nc; i++) {
  234. rp = cp->rightsib;
  235. freeTree (cp);
  236. cp = rp;
  237. }
  238. free (tp);
  239. }
  240. /* patchworkLayout:
  241. */
  242. void patchworkLayout(Agraph_t * g)
  243. {
  244. treenode_t* root;
  245. attrsym_t * ap = agfindnodeattr(g, "area");
  246. attrsym_t * gp = agfindgraphattr(g, "area");
  247. attrsym_t * mp = agfindgraphattr(g, "inset");
  248. double total;
  249. root = mkTree (g,gp,ap,mp);
  250. total = root->area;
  251. root->r = (rectangle){{0, 0}, {sqrt(total + 0.1), sqrt(total + 0.1)}};
  252. layoutTree(root);
  253. walkTree(root);
  254. freeTree (root);
  255. }