BinPacker.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. #include "BinPacker.hpp"
  2. #include <cassert>
  3. #include <algorithm>
  4. // ---------------------------------------------------------------------------
  5. void BinPacker::Pack(
  6. const std::vector<int>& rects,
  7. std::vector< std::vector<int> >& packs,
  8. int packSize,
  9. bool allowRotation)
  10. {
  11. assert(!(rects.size() % 2));
  12. Clear();
  13. m_packSize = packSize;
  14. // Add rects to member array, and check to make sure none is too big
  15. for (size_t i = 0; i < rects.size(); i += 2) {
  16. if (rects[i] > m_packSize || rects[i + 1] > m_packSize) {
  17. assert(!"All rect dimensions must be <= the pack size");
  18. }
  19. m_rects.push_back(Rect(0, 0, rects[i], rects[i + 1], i >> 1));
  20. }
  21. // Sort from greatest to least area
  22. std::sort(m_rects.rbegin(), m_rects.rend());
  23. // Pack
  24. while (m_numPacked < (int)m_rects.size()) {
  25. int i = m_packs.size();
  26. m_packs.push_back(Rect(m_packSize));
  27. m_roots.push_back(i);
  28. Fill(i, allowRotation);
  29. }
  30. // Write out
  31. packs.resize(m_roots.size());
  32. for (size_t i = 0; i < m_roots.size(); ++i) {
  33. packs[i].clear();
  34. AddPackToArray(m_roots[i], packs[i]);
  35. }
  36. // Check and make sure all rects were packed
  37. for (size_t i = 0; i < m_rects.size(); ++i) {
  38. if (!m_rects[i].packed) {
  39. assert(!"Not all rects were packed");
  40. }
  41. }
  42. }
  43. // ---------------------------------------------------------------------------
  44. void BinPacker::Clear()
  45. {
  46. m_packSize = 0;
  47. m_numPacked = 0;
  48. m_rects.clear();
  49. m_packs.clear();
  50. m_roots.clear();
  51. }
  52. // ---------------------------------------------------------------------------
  53. void BinPacker::Fill(int pack, bool allowRotation)
  54. {
  55. assert(PackIsValid(pack));
  56. int i = pack;
  57. // For each rect
  58. for (size_t j = 0; j < m_rects.size(); ++j) {
  59. // If it's not already packed
  60. if (!m_rects[j].packed) {
  61. // If it fits in the current working area
  62. if (Fits(m_rects[j], m_packs[i], allowRotation)) {
  63. // Store in lower-left of working area, split, and recurse
  64. ++m_numPacked;
  65. Split(i, j);
  66. Fill(m_packs[i].children[0], allowRotation);
  67. Fill(m_packs[i].children[1], allowRotation);
  68. return;
  69. }
  70. }
  71. }
  72. }
  73. // ---------------------------------------------------------------------------
  74. void BinPacker::Split(int pack, int rect)
  75. {
  76. assert(PackIsValid(pack));
  77. assert(RectIsValid(rect));
  78. int i = pack;
  79. int j = rect;
  80. // Split the working area either horizontally or vertically with respect
  81. // to the rect we're storing, such that we get the largest possible child
  82. // area.
  83. Rect left = m_packs[i];
  84. Rect right = m_packs[i];
  85. Rect bottom = m_packs[i];
  86. Rect top = m_packs[i];
  87. left.y += m_rects[j].h;
  88. left.w = m_rects[j].w;
  89. left.h -= m_rects[j].h;
  90. right.x += m_rects[j].w;
  91. right.w -= m_rects[j].w;
  92. bottom.x += m_rects[j].w;
  93. bottom.h = m_rects[j].h;
  94. bottom.w -= m_rects[j].w;
  95. top.y += m_rects[j].h;
  96. top.h -= m_rects[j].h;
  97. int maxLeftRightArea = left.GetArea();
  98. if (right.GetArea() > maxLeftRightArea) {
  99. maxLeftRightArea = right.GetArea();
  100. }
  101. int maxBottomTopArea = bottom.GetArea();
  102. if (top.GetArea() > maxBottomTopArea) {
  103. maxBottomTopArea = top.GetArea();
  104. }
  105. if (maxLeftRightArea > maxBottomTopArea) {
  106. if (left.GetArea() > right.GetArea()) {
  107. m_packs.push_back(left);
  108. m_packs.push_back(right);
  109. } else {
  110. m_packs.push_back(right);
  111. m_packs.push_back(left);
  112. }
  113. } else {
  114. if (bottom.GetArea() > top.GetArea()) {
  115. m_packs.push_back(bottom);
  116. m_packs.push_back(top);
  117. } else {
  118. m_packs.push_back(top);
  119. m_packs.push_back(bottom);
  120. }
  121. }
  122. // This pack area now represents the rect we've just stored, so save the
  123. // relevant info to it, and assign children.
  124. m_packs[i].w = m_rects[j].w;
  125. m_packs[i].h = m_rects[j].h;
  126. m_packs[i].ID = m_rects[j].ID;
  127. m_packs[i].rotated = m_rects[j].rotated;
  128. m_packs[i].children[0] = m_packs.size() - 2;
  129. m_packs[i].children[1] = m_packs.size() - 1;
  130. // Done with the rect
  131. m_rects[j].packed = true;
  132. }
  133. // ---------------------------------------------------------------------------
  134. bool BinPacker::Fits(Rect& rect1, const Rect& rect2, bool allowRotation)
  135. {
  136. // Check to see if rect1 fits in rect2, and rotate rect1 if that will
  137. // enable it to fit.
  138. if (rect1.w <= rect2.w && rect1.h <= rect2.h) {
  139. return true;
  140. } else if (allowRotation && rect1.h <= rect2.w && rect1.w <= rect2.h) {
  141. rect1.Rotate();
  142. return true;
  143. } else {
  144. return false;
  145. }
  146. }
  147. // ---------------------------------------------------------------------------
  148. void BinPacker::AddPackToArray(int pack, std::vector<int>& array) const
  149. {
  150. assert(PackIsValid(pack));
  151. int i = pack;
  152. if (m_packs[i].ID != -1) {
  153. array.push_back(m_packs[i].ID);
  154. array.push_back(m_packs[i].x);
  155. array.push_back(m_packs[i].y);
  156. array.push_back(m_packs[i].rotated);
  157. if (m_packs[i].children[0] != -1) {
  158. AddPackToArray(m_packs[i].children[0], array);
  159. }
  160. if (m_packs[i].children[1] != -1) {
  161. AddPackToArray(m_packs[i].children[1], array);
  162. }
  163. }
  164. }
  165. // ---------------------------------------------------------------------------
  166. bool BinPacker::RectIsValid(int i) const
  167. {
  168. return i >= 0 && i < (int)m_rects.size();
  169. }
  170. // ---------------------------------------------------------------------------
  171. bool BinPacker::PackIsValid(int i) const
  172. {
  173. return i >= 0 && i < (int)m_packs.size();
  174. }
  175. // ---------------------------------------------------------------------------