GuillotineBinPack.h 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. /** @file GuillotineBinPack.h
  2. @author Jukka Jylänki
  3. @brief Implements different bin packer algorithms that use the GUILLOTINE data structure.
  4. This work is released to Public Domain, do whatever you want with it.
  5. */
  6. #pragma once
  7. #include "Rect.h"
  8. /** GuillotineBinPack implements different variants of bin packer algorithms that use the GUILLOTINE data structure
  9. to keep track of the free space of the bin where rectangles may be placed. */
  10. class GuillotineBinPack
  11. {
  12. public:
  13. /// The initial bin size will be (0,0). Call Init to set the bin size.
  14. GuillotineBinPack();
  15. /// Initializes a new bin of the given size.
  16. GuillotineBinPack(int width, int height, bool allowRotate);
  17. /// (Re)initializes the packer to an empty bin of width x height units. Call whenever
  18. /// you need to restart with a new bin.
  19. void Init(int width, int height, bool allowRotate);
  20. /// Specifies the different choice heuristics that can be used when deciding which of the free subrectangles
  21. /// to place the to-be-packed rectangle into.
  22. enum FreeRectChoiceHeuristic
  23. {
  24. RectBestAreaFit, ///< -BAF
  25. RectBestShortSideFit, ///< -BSSF
  26. RectBestLongSideFit, ///< -BLSF
  27. RectWorstAreaFit, ///< -WAF
  28. RectWorstShortSideFit, ///< -WSSF
  29. RectWorstLongSideFit ///< -WLSF
  30. };
  31. /// Specifies the different choice heuristics that can be used when the packer needs to decide whether to
  32. /// subdivide the remaining free space in horizontal or vertical direction.
  33. enum GuillotineSplitHeuristic
  34. {
  35. SplitShorterLeftoverAxis, ///< -SLAS
  36. SplitLongerLeftoverAxis, ///< -LLAS
  37. SplitMinimizeArea, ///< -MINAS, Try to make a single big rectangle at the expense of making the other small.
  38. SplitMaximizeArea, ///< -MAXAS, Try to make both remaining rectangles as even-sized as possible.
  39. SplitShorterAxis, ///< -SAS
  40. SplitLongerAxis ///< -LAS
  41. };
  42. /// Inserts a single rectangle into the bin. The packer might rotate the rectangle, in which case the returned
  43. /// struct will have the width and height values swapped.
  44. /// @param merge If true, performs free Rectangle Merge procedure after packing the new rectangle. This procedure
  45. /// tries to defragment the list of disjoint free rectangles to improve packing performance, but also takes up
  46. /// some extra time.
  47. /// @param rectChoice The free rectangle choice heuristic rule to use.
  48. /// @param splitMethod The free rectangle split heuristic rule to use.
  49. Rect Insert(int width, int height, bool merge, FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod);
  50. /// Inserts a list of rectangles into the bin.
  51. /// @param rects The list of rectangles to add. This list will be destroyed in the packing process.
  52. /// @param dst The outputted list of rectangles. Note that the indices will not correspond to the input indices.
  53. /// @param merge If true, performs Rectangle Merge operations during the packing process.
  54. /// @param rectChoice The free rectangle choice heuristic rule to use.
  55. /// @param splitMethod The free rectangle split heuristic rule to use.
  56. void Insert(MemPtr<RectSizeIndex> rects, MemPtr<RectIndex> dst, bool merge,
  57. FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod);
  58. // Implements GUILLOTINE-MAXFITTING, an experimental heuristic that's really cool but didn't quite work in practice.
  59. // void InsertMaxFitting(std::vector<RectSize> &rects, std::vector<Rect> &dst, bool merge,
  60. // FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod);
  61. /// Computes the ratio of used/total surface area. 0.00 means no space is yet used, 1.00 means the whole bin is used.
  62. float Occupancy() const;
  63. /// Returns the internal list of disjoint rectangles that track the free area of the bin. You may alter this vector
  64. /// any way desired, as long as the end result still is a list of disjoint rectangles.
  65. Memc<Rect> &GetFreeRectangles() { return freeRectangles; }
  66. /// Returns the list of packed rectangles. You may alter this vector at will, for example, you can move a Rect from
  67. /// this list to the Free Rectangles list to free up space on-the-fly, but notice that this causes fragmentation.
  68. Memc<Rect> &GetUsedRectangles() { return usedRectangles; }
  69. /// Performs a Rectangle Merge operation. This procedure looks for adjacent free rectangles and merges them if they
  70. /// can be represented with a single rectangle. Takes up Theta(|freeRectangles|^2) time.
  71. void MergeFreeList();
  72. private:
  73. int binWidth;
  74. int binHeight;
  75. bool allowRotate;
  76. /// Stores a list of all the rectangles that we have packed so far. This is used only to compute the Occupancy ratio,
  77. /// so if you want to have the packer consume less memory, this can be removed.
  78. Memc<Rect> usedRectangles;
  79. /// Stores a list of rectangles that represents the free area of the bin. This rectangles in this list are disjoint.
  80. Memc<Rect> freeRectangles;
  81. #if DEBUG
  82. /// Used to track that the packer produces proper packings.
  83. DisjointRectCollection disjointRects;
  84. #endif
  85. /// Goes through the list of free rectangles and finds the best one to place a rectangle of given size into.
  86. /// Running time is Theta(|freeRectangles|).
  87. /// @param nodeIndex [out] The index of the free rectangle in the freeRectangles array into which the new
  88. /// rect was placed.
  89. /// @return A Rect structure that represents the placement of the new rect into the best free rectangle.
  90. Rect FindPositionForNewNode(int width, int height, FreeRectChoiceHeuristic rectChoice, int *nodeIndex);
  91. static int ScoreByHeuristic(int width, int height, const Rect &freeRect, FreeRectChoiceHeuristic rectChoice);
  92. // The following functions compute (penalty) score values if a rect of the given size was placed into the
  93. // given free rectangle. In these score values, smaller is better.
  94. static int ScoreBestAreaFit(int width, int height, const Rect &freeRect);
  95. static int ScoreBestShortSideFit(int width, int height, const Rect &freeRect);
  96. static int ScoreBestLongSideFit(int width, int height, const Rect &freeRect);
  97. static int ScoreWorstAreaFit(int width, int height, const Rect &freeRect);
  98. static int ScoreWorstShortSideFit(int width, int height, const Rect &freeRect);
  99. static int ScoreWorstLongSideFit(int width, int height, const Rect &freeRect);
  100. /// Splits the given L-shaped free rectangle into two new free rectangles after placedRect has been placed into it.
  101. /// Determines the split axis by using the given heuristic.
  102. void SplitFreeRectByHeuristic(const Rect &freeRect, const Rect &placedRect, GuillotineSplitHeuristic method);
  103. /// Splits the given L-shaped free rectangle into two new free rectangles along the given fixed split axis.
  104. void SplitFreeRectAlongAxis(const Rect &freeRect, const Rect &placedRect, bool splitHorizontal);
  105. };