SkylineBinPack.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. /** @file SkylineBinPack.cpp
  2. @author Jukka Jylänki
  3. @brief Implements different bin packer algorithms that use the SKYLINE data structure.
  4. This work is released to Public Domain, do whatever you want with it.
  5. */
  6. #include "SkylineBinPack.h"
  7. SkylineBinPack::SkylineBinPack()
  8. :binWidth(0),
  9. binHeight(0),
  10. allowRotate(false)
  11. {
  12. }
  13. SkylineBinPack::SkylineBinPack(int width, int height, bool useWasteMap, bool allowRotate)
  14. {
  15. Init(width, height, useWasteMap, allowRotate);
  16. }
  17. void SkylineBinPack::Init(int width, int height, bool useWasteMap_, bool allowRotate_)
  18. {
  19. binWidth = width;
  20. binHeight = height;
  21. useWasteMap = useWasteMap_;
  22. allowRotate = allowRotate_;
  23. #ifdef DEBUG
  24. disjointRects.Clear();
  25. #endif
  26. usedSurfaceArea = 0;
  27. skyLine.clear();
  28. SkylineNode node;
  29. node.x = 0;
  30. node.y = 0;
  31. node.width = binWidth;
  32. skyLine.add(node);
  33. if (useWasteMap)
  34. {
  35. wasteMap.Init(width, height, allowRotate);
  36. wasteMap.GetFreeRectangles().clear();
  37. }
  38. }
  39. void SkylineBinPack::Insert(Memp<RectSizeIndex> rects, Memp<RectIndex> dst, LevelChoiceHeuristic method)
  40. {
  41. dst.clear();
  42. while(rects.elms() > 0)
  43. {
  44. Rect bestNode;
  45. int bestScore1 = INT_MAX;
  46. int bestScore2 = INT_MAX;
  47. int bestSkylineIndex = -1;
  48. int bestRectIndex = -1;
  49. for(size_t i = 0; i < rects.elms(); ++i)
  50. {
  51. Rect newNode;
  52. int score1;
  53. int score2;
  54. int index;
  55. switch(method)
  56. {
  57. case LevelBottomLeft:
  58. newNode = FindPositionForNewNodeBottomLeft(rects[i].width, rects[i].height, score1, score2, index);
  59. debug_assert(disjointRects.Disjoint(newNode));
  60. break;
  61. case LevelMinWasteFit:
  62. newNode = FindPositionForNewNodeMinWaste(rects[i].width, rects[i].height, score2, score1, index);
  63. debug_assert(disjointRects.Disjoint(newNode));
  64. break;
  65. default: assert(false); break;
  66. }
  67. if (newNode.height != 0)
  68. {
  69. if (score1 < bestScore1 || (score1 == bestScore1 && score2 < bestScore2))
  70. {
  71. bestNode = newNode;
  72. bestScore1 = score1;
  73. bestScore2 = score2;
  74. bestSkylineIndex = index;
  75. bestRectIndex = i;
  76. }
  77. }
  78. }
  79. if (bestRectIndex == -1)
  80. return;
  81. // Perform the actual packing.
  82. debug_assert(disjointRects.Disjoint(bestNode));
  83. #ifdef DEBUG
  84. disjointRects.Add(bestNode);
  85. #endif
  86. AddSkylineLevel(bestSkylineIndex, bestNode);
  87. usedSurfaceArea += rects[bestRectIndex].width * rects[bestRectIndex].height;
  88. RectIndex &dest=dst.New();
  89. SCAST(Rect, dest)=bestNode;
  90. dest.index=rects[bestRectIndex].index;
  91. rects.remove(bestRectIndex);
  92. }
  93. }
  94. Rect SkylineBinPack::Insert(int width, int height, LevelChoiceHeuristic method)
  95. {
  96. // First try to pack this rectangle into the waste map, if it fits.
  97. Rect node = wasteMap.Insert(width, height, true, GuillotineBinPack::RectBestShortSideFit,
  98. GuillotineBinPack::SplitMaximizeArea);
  99. debug_assert(disjointRects.Disjoint(node));
  100. if (node.height != 0)
  101. {
  102. Rect newNode;
  103. newNode.x = node.x;
  104. newNode.y = node.y;
  105. newNode.width = node.width;
  106. newNode.height = node.height;
  107. usedSurfaceArea += width * height;
  108. debug_assert(disjointRects.Disjoint(newNode));
  109. #ifdef DEBUG
  110. disjointRects.Add(newNode);
  111. #endif
  112. return newNode;
  113. }
  114. switch(method)
  115. {
  116. case LevelBottomLeft: return InsertBottomLeft(width, height);
  117. case LevelMinWasteFit: return InsertMinWaste(width, height);
  118. default: assert(false); return node;
  119. }
  120. }
  121. bool SkylineBinPack::RectangleFits(int skylineNodeIndex, int width, int height, int &y) const
  122. {
  123. int x = skyLine[skylineNodeIndex].x;
  124. if (x + width > binWidth)
  125. return false;
  126. int widthLeft = width;
  127. int i = skylineNodeIndex;
  128. y = skyLine[skylineNodeIndex].y;
  129. while(widthLeft > 0)
  130. {
  131. y = Max(y, skyLine[i].y);
  132. if (y + height > binHeight)
  133. return false;
  134. widthLeft -= skyLine[i].width;
  135. ++i;
  136. assert(i < (int)skyLine.elms() || widthLeft <= 0);
  137. }
  138. return true;
  139. }
  140. int SkylineBinPack::ComputeWastedArea(int skylineNodeIndex, int width, int height, int y) const
  141. {
  142. int wastedArea = 0;
  143. const int rectLeft = skyLine[skylineNodeIndex].x;
  144. const int rectRight = rectLeft + width;
  145. for(; skylineNodeIndex < (int)skyLine.elms() && skyLine[skylineNodeIndex].x < rectRight; ++skylineNodeIndex)
  146. {
  147. if (skyLine[skylineNodeIndex].x >= rectRight || skyLine[skylineNodeIndex].x + skyLine[skylineNodeIndex].width <= rectLeft)
  148. break;
  149. int leftSide = skyLine[skylineNodeIndex].x;
  150. int rightSide = Min(rectRight, leftSide + skyLine[skylineNodeIndex].width);
  151. assert(y >= skyLine[skylineNodeIndex].y);
  152. wastedArea += (rightSide - leftSide) * (y - skyLine[skylineNodeIndex].y);
  153. }
  154. return wastedArea;
  155. }
  156. bool SkylineBinPack::RectangleFits(int skylineNodeIndex, int width, int height, int &y, int &wastedArea) const
  157. {
  158. bool fits = RectangleFits(skylineNodeIndex, width, height, y);
  159. if (fits)
  160. wastedArea = ComputeWastedArea(skylineNodeIndex, width, height, y);
  161. return fits;
  162. }
  163. void SkylineBinPack::AddWasteMapArea(int skylineNodeIndex, int width, int height, int y)
  164. {
  165. const int rectLeft = skyLine[skylineNodeIndex].x;
  166. const int rectRight = rectLeft + width;
  167. for(; skylineNodeIndex < (int)skyLine.elms() && skyLine[skylineNodeIndex].x < rectRight; ++skylineNodeIndex)
  168. {
  169. if (skyLine[skylineNodeIndex].x >= rectRight || skyLine[skylineNodeIndex].x + skyLine[skylineNodeIndex].width <= rectLeft)
  170. break;
  171. int leftSide = skyLine[skylineNodeIndex].x;
  172. int rightSide = Min(rectRight, leftSide + skyLine[skylineNodeIndex].width);
  173. assert(y >= skyLine[skylineNodeIndex].y);
  174. Rect waste;
  175. waste.x = leftSide;
  176. waste.y = skyLine[skylineNodeIndex].y;
  177. waste.width = rightSide - leftSide;
  178. waste.height = y - skyLine[skylineNodeIndex].y;
  179. debug_assert(disjointRects.Disjoint(waste));
  180. wasteMap.GetFreeRectangles().add(waste);
  181. }
  182. }
  183. void SkylineBinPack::AddSkylineLevel(int skylineNodeIndex, const Rect &rect)
  184. {
  185. // First track all wasted areas and mark them into the waste map if we're using one.
  186. if (useWasteMap)
  187. AddWasteMapArea(skylineNodeIndex, rect.width, rect.height, rect.y);
  188. SkylineNode newNode;
  189. newNode.x = rect.x;
  190. newNode.y = rect.y + rect.height;
  191. newNode.width = rect.width;
  192. skyLine.NewAt(skylineNodeIndex)=newNode;
  193. assert(newNode.x + newNode.width <= binWidth);
  194. assert(newNode.y <= binHeight);
  195. for(size_t i = skylineNodeIndex+1; i < skyLine.elms(); ++i)
  196. {
  197. assert(skyLine[i-1].x <= skyLine[i].x);
  198. if (skyLine[i].x < skyLine[i-1].x + skyLine[i-1].width)
  199. {
  200. int shrink = skyLine[i-1].x + skyLine[i-1].width - skyLine[i].x;
  201. skyLine[i].x += shrink;
  202. skyLine[i].width -= shrink;
  203. if (skyLine[i].width <= 0)
  204. {
  205. skyLine.remove(i, true);
  206. --i;
  207. }
  208. else
  209. break;
  210. }
  211. else
  212. break;
  213. }
  214. MergeSkylines();
  215. }
  216. void SkylineBinPack::MergeSkylines()
  217. {
  218. for(size_t i = 0; i < skyLine.elms()-1; ++i)
  219. if (skyLine[i].y == skyLine[i+1].y)
  220. {
  221. skyLine[i].width += skyLine[i+1].width;
  222. skyLine.remove(i+1, true);
  223. --i;
  224. }
  225. }
  226. Rect SkylineBinPack::InsertBottomLeft(int width, int height)
  227. {
  228. int bestHeight;
  229. int bestWidth;
  230. int bestIndex;
  231. Rect newNode = FindPositionForNewNodeBottomLeft(width, height, bestHeight, bestWidth, bestIndex);
  232. if (bestIndex != -1)
  233. {
  234. debug_assert(disjointRects.Disjoint(newNode));
  235. // Perform the actual packing.
  236. AddSkylineLevel(bestIndex, newNode);
  237. usedSurfaceArea += width * height;
  238. #ifdef DEBUG
  239. disjointRects.Add(newNode);
  240. #endif
  241. }
  242. else
  243. memset(&newNode, 0, sizeof(Rect));
  244. return newNode;
  245. }
  246. Rect SkylineBinPack::FindPositionForNewNodeBottomLeft(int width, int height, int &bestHeight, int &bestWidth, int &bestIndex) const
  247. {
  248. bestHeight = INT_MAX;
  249. bestIndex = -1;
  250. // Used to break ties if there are nodes at the same level. Then pick the narrowest one.
  251. bestWidth = INT_MAX;
  252. Rect newNode;
  253. memset(&newNode, 0, sizeof(newNode));
  254. for(size_t i = 0; i < skyLine.elms(); ++i)
  255. {
  256. int y;
  257. if (RectangleFits(i, width, height, y))
  258. {
  259. if (y + height < bestHeight || (y + height == bestHeight && skyLine[i].width < bestWidth))
  260. {
  261. bestHeight = y + height;
  262. bestIndex = i;
  263. bestWidth = skyLine[i].width;
  264. newNode.x = skyLine[i].x;
  265. newNode.y = y;
  266. newNode.width = width;
  267. newNode.height = height;
  268. debug_assert(disjointRects.Disjoint(newNode));
  269. }
  270. }
  271. if (allowRotate && RectangleFits(i, height, width, y))
  272. {
  273. if (y + width < bestHeight || (y + width == bestHeight && skyLine[i].width < bestWidth))
  274. {
  275. bestHeight = y + width;
  276. bestIndex = i;
  277. bestWidth = skyLine[i].width;
  278. newNode.x = skyLine[i].x;
  279. newNode.y = y;
  280. newNode.width = height;
  281. newNode.height = width;
  282. debug_assert(disjointRects.Disjoint(newNode));
  283. }
  284. }
  285. }
  286. return newNode;
  287. }
  288. Rect SkylineBinPack::InsertMinWaste(int width, int height)
  289. {
  290. int bestHeight;
  291. int bestWastedArea;
  292. int bestIndex;
  293. Rect newNode = FindPositionForNewNodeMinWaste(width, height, bestHeight, bestWastedArea, bestIndex);
  294. if (bestIndex != -1)
  295. {
  296. debug_assert(disjointRects.Disjoint(newNode));
  297. // Perform the actual packing.
  298. AddSkylineLevel(bestIndex, newNode);
  299. usedSurfaceArea += width * height;
  300. #ifdef DEBUG
  301. disjointRects.Add(newNode);
  302. #endif
  303. }
  304. else
  305. memset(&newNode, 0, sizeof(newNode));
  306. return newNode;
  307. }
  308. Rect SkylineBinPack::FindPositionForNewNodeMinWaste(int width, int height, int &bestHeight, int &bestWastedArea, int &bestIndex) const
  309. {
  310. bestHeight = INT_MAX;
  311. bestWastedArea = INT_MAX;
  312. bestIndex = -1;
  313. Rect newNode;
  314. memset(&newNode, 0, sizeof(newNode));
  315. for(size_t i = 0; i < skyLine.elms(); ++i)
  316. {
  317. int y;
  318. int wastedArea;
  319. if (RectangleFits(i, width, height, y, wastedArea))
  320. {
  321. if (wastedArea < bestWastedArea || (wastedArea == bestWastedArea && y + height < bestHeight))
  322. {
  323. bestHeight = y + height;
  324. bestWastedArea = wastedArea;
  325. bestIndex = i;
  326. newNode.x = skyLine[i].x;
  327. newNode.y = y;
  328. newNode.width = width;
  329. newNode.height = height;
  330. debug_assert(disjointRects.Disjoint(newNode));
  331. }
  332. }
  333. if (allowRotate && RectangleFits(i, height, width, y, wastedArea))
  334. {
  335. if (wastedArea < bestWastedArea || (wastedArea == bestWastedArea && y + width < bestHeight))
  336. {
  337. bestHeight = y + width;
  338. bestWastedArea = wastedArea;
  339. bestIndex = i;
  340. newNode.x = skyLine[i].x;
  341. newNode.y = y;
  342. newNode.width = height;
  343. newNode.height = width;
  344. debug_assert(disjointRects.Disjoint(newNode));
  345. }
  346. }
  347. }
  348. return newNode;
  349. }
  350. /// Computes the ratio of used surface area.
  351. float SkylineBinPack::Occupancy() const
  352. {
  353. return (float)usedSurfaceArea / (binWidth * binHeight);
  354. }