hlfw.ca

binpack

Download patch

ref: 5a728711fa3bd16e4290aed341dc4e903eb27854
parent: c21f681808ae3e464374cb5917b9d4d684a4eedf
author: Halfwit <michaelmisch1985@gmail.com>
date: Fri Nov 25 02:47:31 PST 2016

Formatting

--- a/binpack.c
+++ b/binpack.c
@@ -1,4 +1,4 @@
-/* Simple bin packing algorithm implementation for use in window management */
+/* Simple bin packing algorithm implementation */
 
 #include <stdlib.h>
 #include <stdio.h>
@@ -5,7 +5,11 @@
 #include <getopt.h>
 #include <stdbool.h>
 
-/* from -x -y -g flags */
+enum {
+	MAX_RECTS	= 50,
+	LINE_LNG	= 50
+};
+
 struct mbin {
 	unsigned x, y, gaps;
 };
@@ -20,44 +24,54 @@
 	unsigned long wid;
 };
 
-/* temporary storage of available sub-bins */
 struct bins {
-	unsigned x, y, w, h;
+  /* temporary storage of available sub-bins */
+  unsigned x, y, w, h;
 };
 
 size_t
-create_rect(struct input r[]) {
-	size_t length = 0;
-	char line[50] = "";
+binpack_init(struct input r[])
+{
 
+	/* Read from stdin, create each rect */
+	size_t length = 0;
+	char line[LINE_LNG];
 	for (unsigned i = 0; fgets(line, sizeof(line), stdin); ++i) {
 		sscanf(line, "%d %d %d %d %lx", &r[i].min_w, &r[i].min_h, &r[i].max_w, &r[i].max_h, &r[i].wid);
 		length++;
 	}
+
 	return length;
+
 }
 
-/* (max bin size - blob size) / 2 */
 void
-center(const size_t length, struct output r[], struct mbin mb) {
+center(const size_t length, struct output r[], struct mbin mb)
+{
+
+	/* (max bin size - blob size) / 2 */
 	unsigned w = 0, h = 0;
 	for (size_t i = 0; i < length; i++) {
 		if (w < (r[i].w + r[i].x))
 			w = r[i].w + r[i].x;
+
 		if (h < (r[i].h + r[i].y))
 			h = r[i].h + r[i].y;
 	}
+
 	for (size_t i = 0; i < length; i++) {
 		r[i].x += (mb.x - w) / 2;
 		r[i].y += (mb.y - h) / 2;
 	}
+
 }
 
-/* arrange rectangles smallest to largest w*h */
 void
-sort_bins(struct bins b[], size_t *bin_count) {
-	struct bins temp;
+sort_bins(struct bins b[], size_t *bin_count)
+{
 
+	/* given set of bins, arrange them smallest to largest w*h */
+	struct bins temp;
 	for (unsigned i = 1; i < *bin_count; i++) {
 		for (unsigned j = 0; j < *bin_count - i; j++) {
 			if ((b[j + 1].w * b[j + 1].h) > (b[j].w * b[j].h)) {
@@ -67,45 +81,15 @@
 			}
 		}
 	}
-}
 
-void
-scrub_bins(struct bins b[], size_t *bin_count) {
-	if (*bin_count == 1)
-		return;
-		
-	for (unsigned i = 1; i < *bin_count; i++) {
-		if(b[i].w == 0 || b[i].h == 0)
-			continue;
-		for (unsigned j = 0; j < *bin_count - 1; j++) {
-			
-			/* Skip if same bin */
-			if (i == j)
-				continue;
-				
-			/* Same row */
-			if (b[j].y == b[i].y && b[j].h == b[i].h) {
-				b[j].w += b[i].w;
-				b[i].h = 0;
-				b[i].w = 0;
-			}
-			
-			/* Same column */
-			if (b[j].x == b[i].x && b[j].w == b[i].w) {
-				b[j].h += b[i].h;
-				b[i].h = 0;
-				b[i].w = 0;
-			}
-		}
-	}	
 }
 
-/* arrange rectangles largest to smallest */
 void
 sort_input(struct input r[], const size_t length)
 {
-	struct input temp;
 
+	/* arrange rectangles largest to smallest, normalized some over min/max */
+	struct input temp;
 	for (size_t i = 1; i < length; i++) {
 		for (size_t j = 0; j < length - i; j++) {
 			if ((r[j + 1].max_w * r[j + 1].min_h) > (r[j].max_w * r[j].min_h)) {
@@ -115,12 +99,14 @@
 			}
 		}
 	}
+
 }
 
 void
 create_bins(struct bins bin[], struct output out[], size_t i, size_t j, size_t *bin_count, struct mbin mb)
 {
-	/* Local variables */
+
+	/* New bins based on subsection of old  */
 	unsigned x, y, w, h;
 	x = bin[j].x;
 	y = bin[j].y;
@@ -128,23 +114,23 @@
 	h = bin[j].h;
 
 	/* rect smaller, make two sub bins */
-	if (out[i].h <= h && out[i].w <= w) {
-		bin[*bin_count] = (struct bins){x + out[i].w + mb.gaps, y, w - out[i].w - mb.gaps, h};
+	if (out[i].h + mb.gaps < h && out[i].w + mb.gaps < w) {
+		bin[*bin_count] = (struct bins){.x = x + out[i].w + mb.gaps, .y = y, .w = w - out[i].w - mb.gaps, .h = h};
 		bin[j].y += (out[i].h + mb.gaps);
 		bin[j].h -= (out[i].h - mb.gaps);
 		*bin_count += 1;
 	}
-	
+
 	/* rect same height */
-	else if (out[i].h == h) {
+	else if (out[i].h + mb.gaps < h) {
 		bin[j].y += (out[i].h + mb.gaps);
 		bin[j].h -= (out[i].h - mb.gaps);
 	}
 
 	/* rect same width */
-	else if (out[i].w == w) {
+	else if (out[i].w + mb.gaps < w) {
 		bin[j].x += (out[i].w + mb.gaps);
-		bin[j].w -= (out[i].w - mb.gaps);
+    	bin[j].w -= (out[i].w - mb.gaps);
 	}
 
 	/* rect fills space, lose a bin */
@@ -151,122 +137,124 @@
 	else {
 		bin[j].w = 0;
 		bin[j].h = 0;
+		*bin_count -= 1;
 	}
+
 }
 
 void
 save_rect(struct bins bin[], struct output out[], struct input r[], size_t i, size_t j, struct mbin mb)
 {
+
 	/* Store rect x y w h wid */
-	out[i] = (struct output){bin[j].x + (mb.gaps / 2), bin[j].y + (mb.gaps / 2), (r[i].min_w + r[i].max_w) / 2, (r[i].min_h + r[i].max_h) / 2, r[i].wid};
+	out[i] = (struct output){.x = bin[j].x + (mb.gaps / 2), .y = bin[j].y + (mb.gaps / 2), .w = r[i].min_w, .h = r[i].min_h, .wid = r[i].wid};
+
 }
 
 bool
-pack_bin(const size_t length, struct output out[], struct mbin mb, struct input r[])
+pack_bin(struct output out[], struct input r[], const size_t length, unsigned *bin_width, unsigned *bin_height, struct mbin mb)
 {
-	/* initialize bins */
-	struct bins bin[100];
-	for (int i = 0; i < 100; i++) {
-		bin[i] = (struct bins){0, 0, 0, 0};
+
+	/* Main algorithm */
+	struct bins bin[MAX_RECTS];
+
+	for (int i = 0; i < MAX_RECTS; i++) {
+		bin[i] = (struct bins){.x = 0, .y = 0, .w = 0, .h = 0};
 	}
 
 	/* default bin */
-	bin[0] = (struct bins){ 0, 0, mb.x, mb.y};
+	bin[0] = (struct bins){ .x = 0, .y = 0, .w = *bin_width + mb.gaps, .h = *bin_height + mb.gaps};
+
 	size_t bin_count = 1;
-	size_t packed = 0;
+	bool rect_fits;
 
 	/* loop through each rect */
 	for (size_t i = 0; i < length; i++) {
+		rect_fits = false;
 
 		/* loop through each bin */
 		for (size_t j = 0; j < bin_count; j++) {
-			/* If rect fits bin, store it */
-			if ((r[i].min_w + r[i].max_w) / 2 <= bin[j].w && (r[i].min_h + r[i].max_h) / 2 <= bin[j].h) {
-				packed++;
+			/* rect fits in current bin */
+			if (r[i].min_w + mb.gaps <= bin[j].w && r[i].min_h + mb.gaps <= bin[j].h) {
+				rect_fits = true;
 				save_rect(bin, out, r, i, j, mb);
 				create_bins(bin, out, i, j, &bin_count, mb);
-				scrub_bins(bin, &bin_count);
 				sort_bins(bin, &bin_count);
 				break;
 			}
 		}
+
+		/* if rect does not fit all bin */
+		if (rect_fits == false) {
+
+			/* Grow main bin if possible */
+			if (mb.x > *bin_width)
+				*bin_width += 2;
+
+			if (mb.y > *bin_height)
+				*bin_height += 2;
+
+			return true;
+		}
 	}
-	if (packed == length) {
-		return true;
-	}
+
 	return false;
+
 }
 
 bool
-resizewidth(const size_t length, struct output out[], struct mbin mb, struct input r[])
+resize(struct output out[], struct input r[], const size_t length, unsigned *bin_width, unsigned *bin_height, struct mbin mb, size_t index)
 {
-	size_t packed = 0;
-	for (size_t i = 0; i < length; i++) {
 
-		/* If we're within 1, bin is fully packed */
-		if (r[i].max_w - r[i].min_w < 2) {
-			packed++;
-			continue;
-		}
-		
-		/* In case something goes wonky */
-		if (r[i].min_w > r[i].max_w) {
-			r[i].max_w = r[i].min_w;
-			r[i].min_w--;
-		}
+	/* When a bin fills all the space available, pack_bin */
+	for (size_t i = index; i < length; i++) {
+		unsigned limitcheck = 0;
+		while (pack_bin(out, r, length, bin_width, bin_height, mb)) {
+			if (limitcheck == mb.x)
+				return false;
 
-		/* if binpack succeeds, we can grow --> set min as current, else set max as current */
-		if(pack_bin(length, out, mb, r)) {
-			r[i].min_w = (r[i].min_w + r[i].max_w) / 2;
-		} else {
-			r[i].max_w = (r[i].min_w + r[i].max_w) / 2;
+			limitcheck++;
 		}
-	}
 
-	if (packed == length) {
-		return false;
+		/* If rect can grow */
+		if (out[i].h < r[i].max_h)
+			r[i].min_h += 1;
+
+		if (out[i].w < r[i].max_w)
+			r[i].min_w += 1;
+
+		sort_input(r, length);
 	}
-	return true;
-}
-bool
-resizeheight(const size_t length, struct output out[], struct mbin mb, struct input r[])
-{
-	size_t packed_bin = 0;
-	for (size_t i = 0; i < length; i++) {
 
-		/* If we're within 1, bin is fully packed */
-		if (r[i].max_h - r[i].min_h < 2) {
-			packed_bin++;
-			continue;
-		}
-		
+	/* max_h to handle cases of smaller windows */
+	unsigned w = 0, h = 0;
 
-		/* In case something goes wonky */
-		if (r[i].min_h > r[i].max_h) {
-			r[i].max_h = r[i].min_h;
-			r[i].min_h--;
-		}
-		
-		/* if binpack succeeds, we can grow --> set min as current, else set max as current */
-		if(pack_bin(length, out, mb, r)) {
-			r[i].min_h = (r[i].min_h + r[i].max_h) / 2;
-		} else {
-			r[i].max_h = (r[i].min_h + r[i].max_h) / 2;
-		}
+	for (size_t i = index; i < length; i++) {
+		if (w < (out[i].w + out[i].x))
+			w = out[i].w + out[i].x + mb.gaps;
+
+		if (h < (out[i].h + out[i].y))
+			h = out[i].h + out[i].y + mb.gaps;
 	}
 
-	if (packed_bin == length) {
+	if (h >= mb.y + (mb.gaps / 2))
 		return false;
-	}
+
+	if (w >= mb.x + (mb.gaps / 2))
+		return false;
+
 	return true;
+
 }
 
 int
 main(int argc, char *argv[])
 {
+
 	struct mbin mb;
-	mb.gaps = 0;
+	mb.gaps = 0, mb.x = 0, mb.y = 0;
 	int c;
+
 	while ((c = getopt(argc, argv, "hg:x:y:")) != -1) {
 		switch (c) {
 		/* read in, all vars unsigned int */
@@ -280,39 +268,64 @@
 			sscanf(optarg, "%u", &mb.y);
 			break;
 		case 'h':
-			fprintf(stderr, "Usage: %s -x screen_width -y screen_height -g gaps\n", argv[0]);
+			fputs("Usage: bin_pack -x screen_width -y screen_height -g gaps\n", stderr);
 			return EXIT_SUCCESS;
 		}
 	}
 
-	struct input r[100];
-	struct output out[100];
+	struct input r[MAX_RECTS];
+	struct output out[MAX_RECTS];
 
-	const size_t length = (create_rect(r));
+	const size_t length = (binpack_init(r));
 
 	/* If we have no windows, exit */
-	if (length == 0) {
+	if (length == 0 || mb.x == 0 || mb.y == 0)
 		return EXIT_SUCCESS;
-	}
 
 	sort_input(r, length);
 
-	/* Initial pack to establish out bins */
-	pack_bin(length, out, mb, r);
+	unsigned bin_width = mb.x;
+	unsigned bin_height = mb.y;
 
-	int foo = 0;
-	while(resizeheight(length, out, mb, r) && resizewidth(length, out, mb, r)) {
-		printf("%d\n", ++foo);
-	}
-
-	unsigned area = 0;
+	/* If we have no large windows */
+	bool no_large_windows = true;
 	for (size_t i = 0; i < length; i++) {
-		area+=out[i].w * out[i].h;
+		struct input temp = r[i];
+		r[i].min_w = r[i].max_w;
+		r[i].min_h = r[i].max_h;
+
+		if (pack_bin(out, r, length, &bin_width, &bin_height, mb)) {
+			r[i] = temp;
+			no_large_windows = false;
+		}
 	}
 
-	/* If we have more windows than space */
-	if (area > (mb.x - mb.gaps) * (mb.y - mb.gaps))
-		return EXIT_SUCCESS;
+	unsigned limitcheck = 0;
+
+	if (no_large_windows == false) {
+		bin_width = r[0].min_w;
+		bin_height = r[0].min_h;
+
+		while (pack_bin(out, r, length, &bin_width, &bin_height, mb)) {
+			/* if we've ran this long, something is up */
+			if (limitcheck == mb.x)
+				return EXIT_FAILURE;
+
+			limitcheck++;
+		}
+
+		limitcheck = 0;
+
+		for (size_t i = 0; i < length; i++) {
+			/* Square out the blob as best as we can */
+			while (resize(out, r, length, &bin_width, &bin_height, mb, i)) {
+				if (limitcheck == mb.x)
+					return EXIT_FAILURE;
+
+				limitcheck++;
+			}
+		}
+	}
 
 	center(length, out, mb);