rectpacker.bmx 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. ' Copyright (c) 2024-2025 Bruce A Henderson
  2. '
  3. ' This software is provided 'as-is', without any express or implied
  4. ' warranty. In no event will the authors be held liable for any damages
  5. ' arising from the use of this software.
  6. '
  7. ' Permission is granted to anyone to use this software for any purpose,
  8. ' including commercial applications, and to alter it and redistribute it
  9. ' freely, subject to the following restrictions:
  10. '
  11. ' 1. The origin of this software must not be misrepresented; you must not
  12. ' claim that you wrote the original software. If you use this software
  13. ' in a product, an acknowledgment in the product documentation would be
  14. ' appreciated but is not required.
  15. ' 2. Altered source versions must be plainly marked as such, and must not be
  16. ' misrepresented as being the original software.
  17. ' 3. This notice may not be removed or altered from any source distribution.
  18. '
  19. SuperStrict
  20. Rem
  21. bbdoc: A module for packing rectangles into sheets.
  22. about: Useful for creating texture atlases, sprite sheets, and other similar things.
  23. End Rem
  24. Module BRL.RectPacker
  25. ModuleInfo "Version: 1.01"
  26. ModuleInfo "License: zlib/libpng"
  27. ModuleInfo "Copyright: 2024-2025 Bruce A Henderson"
  28. ModuleInfo "rect_pack: Albert Kalchmair 2021, Sean Barrett 2014, Jukka Jylänki"
  29. ModuleInfo "History: 1.01"
  30. ModuleInfo "History: borderPadding now applies to individual rects."
  31. ModuleInfo "History: Added sheetPadding to add padding around the edge of the sheet."
  32. ModuleInfo "History: 1.00 Initial Release"
  33. ModuleInfo "CPP_OPTS: -std=c++11"
  34. Import BRL.Collections
  35. Import "source.bmx"
  36. Rem
  37. bbdoc: Packs rectangles into sheets.
  38. about: The packer provides a number of settings that can be used to control how the rectangles are packed.
  39. The rectangles are added to the packer using the #Add method, and then the #Pack method is called to pack them into sheets.
  40. The packer will return an array of #TPackedSheet objects, each of which contains the rectangles that have been packed into it.
  41. An @id can be assigned to each rectangle, which can be used to identify the rectangle in the packed sheets.
  42. End Rem
  43. Type TRectPacker
  44. Rem
  45. bbdoc: The packing method to use.
  46. End Rem
  47. Field packingMethod:EPackingMethod = EPackingMethod.Best
  48. Rem
  49. bbdoc: The maximum number of sheets to produce.
  50. about: If the packer is unable to fit all the rectangles into the specified number of sheets, those that don't fit will be discarded.
  51. End Rem
  52. Field maxSheets:Int = 1
  53. Rem
  54. bbdoc: Whether to pack into power-of-two sized sheets.
  55. about: If this is set to #True, the width and height of the sheets will be rounded up to the nearest power of two.
  56. This is useful for creating sheets that are intended to be used for creating textures.
  57. End Rem
  58. Field powerOfTwo:Int = True
  59. Rem
  60. bbdoc: Whether to pack into square sheets.
  61. about: If this is set to #True, the width and height of the sheets will be the same.
  62. End Rem
  63. Field square:Int = False
  64. Rem
  65. bbdoc: Whether to allow rectangles to be rotated.
  66. about: If this is set to #True, the packer may attempt to rotate rectangles to help fit them into the sheets.
  67. End Rem
  68. Field allowRotate:Int = False
  69. Rem
  70. bbdoc: Whether to align the width of the rectangles.
  71. about: If this is set to #True, the packer will attempt to align the width of the rectangles to the width of the sheet.
  72. This can help to reduce the amount of wasted space in the sheet.
  73. End Rem
  74. Field alignWidth:Int = False
  75. Rem
  76. bbdoc: The amount of padding to add around individual rects.
  77. End Rem
  78. Field borderPadding:Int
  79. Rem
  80. bbdoc: The amount of padding to add around the edge of the sheet.
  81. End Rem
  82. Field sheetPadding:Int
  83. Rem
  84. bbdoc: The amount to over-allocate the sheet by.
  85. about: This is useful if you want to add a border around the sheet, or if you want to add some padding around the rectangles.
  86. End Rem
  87. Field overAllocate:Int
  88. Rem
  89. bbdoc: The minimum width of the sheets.
  90. End Rem
  91. Field minWidth:Int
  92. Rem
  93. bbdoc: The minimum height of the sheets.
  94. End Rem
  95. Field minHeight:Int
  96. Rem
  97. bbdoc: The maximum width of the sheets.
  98. End Rem
  99. Field maxWidth:Int
  100. Rem
  101. bbdoc: The maximum height of the sheets.
  102. End Rem
  103. Field maxHeight:Int
  104. Field sizes:TArrayList<SRectSize> = New TArrayList<SRectSize>
  105. Rem
  106. bbdoc: Adds a rectangle with the given @id to the packer.
  107. End Rem
  108. Method Add(width:Int, height:Int, id:Int)
  109. Local size:SRectSize = New SRectSize(width, height, id)
  110. sizes.Add(size)
  111. End Method
  112. Rem
  113. bbdoc: Packs the rectangles into sheets, based on the settings of the packer.
  114. about: This method will return an array of #TPackedSheet objects, each of which contains the rectangles that have been packed into it.
  115. Any rectangles that don't fit into the sheets will be discarded, and not be included in the returned array.
  116. End Rem
  117. Method Pack:TPackedSheet[]()
  118. Return bmx_rectpacker_pack(Self, packingMethod, maxSheets, powerOfTwo, square, allowRotate, alignWidth, borderPadding, sheetPadding, overAllocate, minWidth, minHeight, maxWidth, maxHeight, sizes.Count())
  119. End Method
  120. Private
  121. Function _GetSize(packer:TRectPacker, index:Int, width:Int Var, height:Int Var, id:Int Var) { nomangle }
  122. Local size:SRectSize = packer.sizes[index]
  123. width = size.width
  124. height = size.height
  125. id = size.id
  126. End Function
  127. Function _NewSheetArray:TPackedSheet[](size:Int) { nomangle }
  128. Return New TPackedSheet[size]
  129. End Function
  130. Function _SetSheet(sheets:TPackedSheet[], index:Int, sheet:TPackedSheet) { nomangle }
  131. sheets[index] = sheet
  132. End Function
  133. End Type
  134. Struct SRectSize
  135. Field width:Int
  136. Field height:Int
  137. Field id:Int
  138. Method New(width:Int, height:Int, id:Int)
  139. Self.width = width
  140. Self.height = height
  141. Self.id = id
  142. End Method
  143. Method Operator=:Int(other:SRectSize)
  144. Return width = other.width And height = other.height And id = other.id
  145. End Method
  146. End Struct
  147. Rem
  148. bbdoc: The packing method to use.
  149. about: The packing method determines how the rectangles are packed into the sheets.
  150. | Value | Description |
  151. |-------------------------------|----------------------------------------------|
  152. | #Best | The best fitting from all of the available methods. |
  153. | #BestSkyline | The best available skyline method. |
  154. | #BestMaxRects | The best available max rects method. |
  155. | #SkylineBottomLeft | The skyline bottom-left method. |
  156. | #SkylineBestFit | The skyline best-fit method. |
  157. | #MaxRectsBestShortSideFit | The max rects best short-side fit method. |
  158. | #MaxRectsBestLongSideFit | The max rects best long-side fit method. |
  159. | #MaxRectsBestAreaFit | The max rects best area fit method. |
  160. | #MaxRectsBottomLeftRule | The max rects bottom-left rule method. |
  161. | #MaxRectsContactPointRule | The max rects contact-point rule method. |
  162. End Rem
  163. Enum EPackingMethod
  164. Best
  165. BestSkyline
  166. BestMaxRects
  167. SkylineBottomLeft
  168. SkylineBestFit
  169. MaxRectsBestShortSideFit
  170. MaxRectsBestLongSideFit
  171. MaxRectsBestAreaFit
  172. MaxRectsBottomLeftRule
  173. MaxRectsContactPointRule
  174. End Enum
  175. Rem
  176. bbdoc: Represents a rectangle that has been packed into a sheet.
  177. End Rem
  178. Struct SPackedRect
  179. Rem
  180. bbdoc: The ID of the rectangle.
  181. End Rem
  182. Field id:Int
  183. Rem
  184. bbdoc: The X position of the rectangle.
  185. End Rem
  186. Field x:Int
  187. Rem
  188. bbdoc: The Y position of the rectangle.
  189. End Rem
  190. Field y:Int
  191. Rem
  192. bbdoc: The width of the rectangle.
  193. End Rem
  194. Field width:Int
  195. Rem
  196. bbdoc: The height of the rectangle.
  197. End Rem
  198. Field height:Int
  199. Rem
  200. bbdoc: Whether the rectangle has been rotated.
  201. End Rem
  202. Field rotated:Int
  203. Method New(id:Int, x:Int, y:Int, width:Int, height:Int, rotated:Int)
  204. Self.id = id
  205. Self.x = x
  206. Self.y = y
  207. Self.width = width
  208. Self.height = height
  209. Self.rotated = rotated
  210. End Method
  211. End Struct
  212. Rem
  213. bbdoc: Represents a sheet that has been packed with rectangles.
  214. End Rem
  215. Type TPackedSheet
  216. Rem
  217. bbdoc: The width of the sheet.
  218. End Rem
  219. Field width:Int
  220. Rem
  221. bbdoc: The height of the sheet.
  222. End Rem
  223. Field height:Int
  224. Rem
  225. bbdoc: The rectangles that have been packed into the sheet.
  226. End Rem
  227. Field rects:SPackedRect[]
  228. Private
  229. Function _Create:TPackedSheet(width:Int, height:Int, size:Int) { nomangle }
  230. Local sheet:TPackedSheet = New TPackedSheet
  231. sheet.width = width
  232. sheet.height = height
  233. sheet.rects = New SPackedRect[size]
  234. Return sheet
  235. End Function
  236. Function _SetRect(sheet:TPackedSheet, index:Int, id:Int, x:Int, y:Int, width:Int, height:Int, rotated:Int) { nomangle }
  237. Local rect:SPackedRect = New SPackedRect(id, x, y, width, height, rotated)
  238. sheet.rects[index] = rect
  239. End Function
  240. End Type
  241. Extern
  242. Function bmx_rectpacker_pack:TPackedSheet[](packer:TRectPacker, packingMethod:EPackingMethod, maxSheets:Int, powerOfTwo:Int, square:Int, allowRotate:Int, alignWidth:Int, borderPadding:Int, sheetPadding:Int, overAllocate:Int, minWidth:Int, minHeight:Int, maxWidth:Int, maxHeight:Int, count:Int)
  243. End Extern