tree_map.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  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 <common/render.h>
  11. #include <math.h>
  12. #include <patchwork/tree_map.h>
  13. #include <util/alloc.h>
  14. #include <util/prisize_t.h>
  15. static void squarify(size_t n, double *area, rectangle *recs, size_t nadded, double maxarea, double minarea, double totalarea,
  16. double asp, rectangle fillrec){
  17. /* add a list of area in fillrec using squarified treemap alg.
  18. n: number of items to add
  19. area: area of these items, Sum to 1 (?).
  20. nadded: number of items already added
  21. maxarea: maxarea of already added items
  22. minarea: min areas of already added items
  23. asp: current worst aspect ratio of the already added items so far
  24. fillrec: the rectangle to be filled in.
  25. */
  26. double w = fmin(fillrec.size[0], fillrec.size[1]);
  27. if (n == 0) return;
  28. if (Verbose) {
  29. fprintf(stderr, "trying to add to rect {%f +/- %f, %f +/- %f}\n",fillrec.x[0], fillrec.size[0], fillrec.x[1], fillrec.size[1]);
  30. fprintf(stderr, "total added so far = %" PRISIZE_T "\n", nadded);
  31. }
  32. if (nadded == 0){
  33. nadded = 1;
  34. maxarea = minarea = area[0];
  35. asp = fmax(area[0] / (w * w), w * w / area[0]);
  36. totalarea = area[0];
  37. squarify(n, area, recs, nadded, maxarea, minarea, totalarea, asp, fillrec);
  38. } else {
  39. double newmaxarea, newminarea, s, h, maxw, minw, newasp, hh, ww, xx, yy;
  40. if (nadded < n){
  41. newmaxarea = fmax(maxarea, area[nadded]);
  42. newminarea = fmin(minarea, area[nadded]);
  43. s = totalarea + area[nadded];
  44. h = s/w;
  45. maxw = newmaxarea/h;
  46. minw = newminarea/h;
  47. newasp = fmax(h / minw, maxw / h);/* same as MAX{s^2/(w^2*newminarea), (w^2*newmaxarea)/(s^2)}*/
  48. }
  49. if (nadded < n && newasp <= asp){/* aspectio improved, keep adding */
  50. squarify(n, area, recs, ++nadded, newmaxarea, newminarea, s, newasp, fillrec);
  51. } else {
  52. /* aspectio worsen if add another area, fixed the already added recs */
  53. if (Verbose) fprintf(stderr, "adding %" PRISIZE_T
  54. " items, total area = %f, w = %f, area/w=%f\n",
  55. nadded, totalarea, w, totalarea/w);
  56. if (fillrec.size[0] <= fillrec.size[1]) {
  57. // tall rec. fix the items along x direction, left to right, at top
  58. hh = totalarea/w;
  59. xx = fillrec.x[0] - fillrec.size[0]/2;
  60. for (size_t i = 0; i < nadded; i++){
  61. recs[i].size[1] = hh;
  62. ww = area[i]/hh;
  63. recs[i].size[0] = ww;
  64. recs[i].x[1] = fillrec.x[1] + 0.5*(fillrec.size[1]) - hh/2;
  65. recs[i].x[0] = xx + ww/2;
  66. xx += ww;
  67. }
  68. fillrec.x[1] -= hh/2;/* the new empty space is below the filled space */
  69. fillrec.size[1] -= hh;
  70. } else {/* short rec. fix along y top to bot, at left*/
  71. ww = totalarea/w;
  72. yy = fillrec.x[1] + fillrec.size[1]/2;
  73. for (size_t i = 0; i < nadded; i++){
  74. recs[i].size[0] = ww;
  75. hh = area[i]/ww;
  76. recs[i].size[1] = hh;
  77. recs[i].x[0] = fillrec.x[0] - 0.5*(fillrec.size[0]) + ww/2;
  78. recs[i].x[1] = yy - hh/2;
  79. yy -= hh;
  80. }
  81. fillrec.x[0] += ww/2;/* the new empty space is right of the filled space */
  82. fillrec.size[0] -= ww;
  83. }
  84. squarify(n - nadded, area + nadded, recs + nadded, 0, 0., 0., 0., 1., fillrec);
  85. }
  86. }
  87. }
  88. /* tree_map:
  89. * Perform a squarified treemap layout on a single level.
  90. * n - number of rectangles
  91. * area - area of rectangles
  92. * fillred - rectangle to be filled
  93. * return array of rectangles
  94. */
  95. rectangle* gv_tree_map(size_t n, double *area, rectangle fillrec){
  96. /* fill a rectangle rec with n items, each item i has area[i] area. */
  97. double total = 0, minarea = 1., maxarea = 0., asp = 1, totalarea = 0;
  98. for (size_t i = 0; i < n; i++) total += area[i];
  99. /* make sure there is enough area */
  100. if (total > fillrec.size[0] * fillrec.size[1] + 0.001)
  101. return NULL;
  102. rectangle *recs = gv_calloc(n, sizeof(rectangle));
  103. squarify(n, area, recs, 0, maxarea, minarea, totalarea, asp, fillrec);
  104. return recs;
  105. }