Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenlib/intern/boxpack_2d.c')
-rw-r--r--source/blender/blenlib/intern/boxpack_2d.c108
1 files changed, 108 insertions, 0 deletions
diff --git a/source/blender/blenlib/intern/boxpack_2d.c b/source/blender/blenlib/intern/boxpack_2d.c
index ddc7f9ee4c7..8a2427a32a8 100644
--- a/source/blender/blenlib/intern/boxpack_2d.c
+++ b/source/blender/blenlib/intern/boxpack_2d.c
@@ -24,6 +24,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
+#include "BLI_listbase.h"
#include "BLI_boxpack_2d.h" /* own include */
#include "BLI_sort.h" /* qsort_r */
@@ -673,3 +674,110 @@ void BLI_box_pack_2d(BoxPack *boxarray, const uint len, float *r_tot_x, float *r
MEM_freeN(vertex_pack_indices);
MEM_freeN(vs_ctx.vertarray);
}
+
+/* Packs boxes into a fixed area.
+ * boxes and packed are linked lists containing structs that can be cast to
+ * FixedSizeBoxPack (i.e. contains a FixedSizeBoxPack as its first element).
+ * Boxes that were packed successfully are placed into *packed and removed from *boxes.
+ *
+ * The algorithm is a simplified version of https://github.com/TeamHypersomnia/rectpack2D.
+ * Better ones could be used, but for the current use case (packing Image tiles into GPU
+ * textures) this is fine.
+ *
+ * Note that packing efficiency depends on the order of the input boxes. Generally speaking,
+ * larger boxes should come first, though how exactly size is best defined (e.g. area,
+ * perimeter) depends on the particular application. */
+void BLI_box_pack_2d_fixedarea(ListBase *boxes, int width, int height, ListBase *packed)
+{
+ ListBase spaces = {NULL};
+ FixedSizeBoxPack *full_rect = MEM_callocN(sizeof(FixedSizeBoxPack), __func__);
+ full_rect->w = width;
+ full_rect->h = height;
+
+ BLI_addhead(&spaces, full_rect);
+
+ /* The basic idea of the algorithm is to keep a list of free spaces in the packing area.
+ * Then, for each box to be packed, we try to find a space that can contain it.
+ * The found space is then split into the area that is occupied by the box and the
+ * remaining area, which is reinserted into the free space list.
+ * By inserting the smaller remaining spaces first, the algorithm tries to use these
+ * smaller spaces first instead of "wasting" a large space. */
+ LISTBASE_FOREACH_MUTABLE (FixedSizeBoxPack *, box, boxes) {
+ LISTBASE_FOREACH (FixedSizeBoxPack *, space, &spaces) {
+ /* Skip this space if it's too small. */
+ if (box->w > space->w || box->h > space->w) {
+ continue;
+ }
+
+ /* Pack this box into this space. */
+ box->x = space->x;
+ box->y = space->y;
+ BLI_remlink(boxes, box);
+ BLI_addtail(packed, box);
+
+ if (box->w == space->w && box->h == space->h) {
+ /* Box exactly fills space, so just remove the space. */
+ BLI_remlink(&spaces, space);
+ MEM_freeN(space);
+ }
+ else if (box->w == space->w) {
+ /* Box fills the entire width, so we can just contract the box
+ * to the upper part that remains. */
+ space->y += box->h;
+ space->h -= box->h;
+ }
+ else if (box->h == space->h) {
+ /* Box fills the entire height, so we can just contract the box
+ * to the right part that remains. */
+ space->x += box->w;
+ space->w -= box->w;
+ }
+ else {
+ /* Split the remaining L-shaped space into two spaces.
+ * There are two ways to do so, we pick the one that produces the biggest
+ * remaining space:
+ *
+ * Horizontal Split Vertical Split
+ * ################### ###################
+ * # # # - #
+ * # Large # # Small - #
+ * # # # - #
+ * #********---------# #******** Large #
+ * # Box * Small # # Box * #
+ * # * # # * #
+ * ################### ###################
+ *
+ */
+ int area_hsplit_large = space->w * (space->h - box->h);
+ int area_vsplit_large = (space->w - box->w) * space->h;
+
+ /* Perform split. This space becomes the larger space,
+ * while the new smaller space is inserted _before_ it. */
+ FixedSizeBoxPack *new_space = MEM_callocN(sizeof(FixedSizeBoxPack), __func__);
+ if (area_hsplit_large > area_vsplit_large) {
+ new_space->x = space->x + box->w;
+ new_space->y = space->y;
+ new_space->w = space->w - box->w;
+ new_space->h = box->h;
+
+ space->y += box->h;
+ space->h -= box->h;
+ }
+ else {
+ new_space->x = space->x;
+ new_space->y = space->y + box->h;
+ new_space->w = box->w;
+ new_space->h = space->h - box->h;
+
+ space->x += box->w;
+ space->w -= box->w;
+ }
+ BLI_addhead(&spaces, new_space);
+ }
+
+ break;
+ }
+ }
+
+ BLI_freelistN(&spaces);
+} \ No newline at end of file