hlfw.ca

binpack

ref: 671771e4799498d22262f134274e804461a90e40
dir: /binpack.c/

View raw version
/* Simple bin packing algorithm implementation for use in window management */

#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
#include <stdbool.h>

/* from -x -y -g flags */
struct mbin {
	unsigned x, y, gaps;
};

struct input {
	unsigned min_w, min_h, max_w, max_h;
	unsigned long wid;
};

struct output {
	unsigned x, y, w, h;
	unsigned long wid;
};

/* temporary storage of available sub-bins */
struct bins {
	unsigned x, y, w, h;
};

size_t 
create_rect(struct input r[]) {
	size_t length = 0;
	char line[50] = "";

	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) {
	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;

	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)) {
				temp = b[j];
				b[j] = b[j + 1];
				b[j + 1] = temp;
			}
		}
	}
}

/* arrange rectangles largest to smallest */
void
sort_input(struct input r[], const size_t length)
{
	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)) {
				temp = r[j];
				r[j] = r[j + 1];
				r[j + 1] = temp;
			}
		}
	}
}

void
create_bins(struct bins bin[], struct output out[], size_t i, size_t j, size_t *bin_count, struct mbin mb)
{
	/* Local variables */
	unsigned x, y, w, h;
	x = bin[j].x;
	y = bin[j].y;
	w = bin[j].w;
	h = bin[j].h;

	/* rect smaller, make two sub bins */
	if (out[i].h + mb.gaps < h && out[i].w + mb.gaps < w) {
		bin[*bin_count] = (struct bins){x + out[i].w + mb.gaps, y, w - out[i].w - mb.gaps, 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 + 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 + mb.gaps < w) {
		bin[j].x += (out[i].w + mb.gaps);
		bin[j].w -= (out[i].w - mb.gaps);
	}

	/* rect fills space, lose a bin */
	else {
		bin[j].w = 0;
		bin[j].h = 0;
	}
}

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].min_h, r[i].wid};
}

/* Main algorithm */
bool 
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};
	}

	/* default bin */
	bin[0] = (struct bins){ 0, 0, *bin_width + mb.gaps, *bin_height + mb.gaps};
	size_t bin_count = 1;
	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++) {
			/* 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);
				sort_bins(bin, &bin_count);
				break;
			}
		}
		if (rect_fits == false)
		return true;
	}
	return false;
}

bool
resize(struct output out[], struct input r[], const size_t length, unsigned *bin_width, unsigned *bin_height, struct mbin mb, size_t index)
{
	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;
			limitcheck++;
		}

		/* 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);
	}

	/* max_h to handle cases of smaller windows */
	unsigned w = 0, h = 0;

	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;
	}

	return ((h < mb.y + (mb.gaps / 2)) && (w < mb.x + (mb.gaps / 2)));
}

int
main(int argc, char *argv[])
{
	struct mbin mb;
	mb.gaps = 0;
	int c;
	while ((c = getopt(argc, argv, "hg:x:y:")) != -1) {
		switch (c) {
		/* read in, all vars unsigned int */
		case 'g': {
			sscanf(optarg, "%u", &mb.gaps);
			break;
		}
		case 'x': {
			sscanf(optarg, "%u", &mb.x);
			break;
		}
		case 'y': {
			sscanf(optarg, "%u", &mb.y);
			break;
		}
		case 'h': {
			fprintf(stderr, "Usage: %s -x screen_width -y screen_height -g gaps\n", argv[0]);
			return EXIT_SUCCESS;
		}
		}
	}

	struct input r[100];
	struct output out[100];

	const size_t length = (create_rect(r));

	/* If we have no windows, exit */
	if (length == 0) {
		return EXIT_SUCCESS;
	}

	sort_input(r, length);

	unsigned bin_width = mb.x;
	unsigned bin_height = mb.y;
	/* TODO: We're going to instead use a binary search approach
	* to find a size for each bin, so the large windows optimization
	* can be omitted 
	*/
	/* Test if we can use maximums on all windows */
	bool no_large_windows = true;
	for (size_t i = 0; i < length; i++) {
		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;
		}
	}

	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);

	for (size_t i = 0; i < length; i++) {
		printf("%d %d %d %d %lx\n", out[i].x, out[i].y, out[i].w, out[i].h, out[i].wid);
	}
	
	return EXIT_SUCCESS;
}