hlfw.ca

binpack

Download patch

ref: 700756bf23b33de6c3a587cc2309a8174aa94153
parent: 2097280c9a33bfffec7c89b29bcdc1ceed9b472c
author: Halfwit <michaelmisch1985@gmail.com>
date: Sun Jan 24 16:43:29 PST 2016

Initial commit

--- /dev/null
+++ b/binpack.c
@@ -1,0 +1,294 @@
+/* Simple bin packing algorithm implementation */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <stdbool.h>
+
+struct mbin {
+  /* from -x -y -g flags */
+  unsigned x, y, gaps;
+};
+
+struct input {
+  /* from stdin */
+  unsigned min_w, min_h, max_w, max_h;
+  unsigned long wid;
+};
+
+struct output {
+  /* To hold final output */
+  unsigned x, y, w, h;
+  unsigned long wid;
+};
+
+struct bins {
+  /* temporary storage of available sub-bins */
+  unsigned x, y, w, h;
+};
+
+unsigned create_rect(struct input r[]) {
+  /* Read from stdin, create each rect */
+  unsigned 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;
+}
+
+void center(const unsigned *length, struct output r[], struct mbin mb) {
+  /* (max bin size - blob size) / 2 */
+  unsigned w = 0, h = 0;
+  for (unsigned 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 (unsigned i = 0; i < *length; i++) {
+    r[i].x += (mb.x - w) / 2;
+    r[i].y += (mb.y - h) / 2;
+  }
+}
+
+void sort_bins(struct bins b[], unsigned *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)) {
+        temp = b[j];
+        b[j] = b[j + 1];
+        b[j + 1] = temp;
+      }
+    }
+  }
+}
+
+void sort_input(struct input r[], const unsigned *length) {
+  /* arrange rectangles largest to smallest, normalized some over min/max */
+  struct input temp;
+  for (unsigned i = 1; i < *length; i++) {
+    for (unsigned 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[], unsigned i, unsigned j,
+                 unsigned *bin_count, struct mbin mb) {
+  /* New bins based on subsection of old  */
+  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 = 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 + 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;
+    *bin_count -= 1;
+  }
+}
+
+void save_rect(struct bins bin[], struct output out[], struct input r[],
+               unsigned i, unsigned j, struct mbin mb) {
+  /* Store rect x y w h 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(struct output out[], struct input r[], const unsigned *length,
+              unsigned *bin_width, unsigned *bin_height, struct mbin mb) {
+  /* Main algorithm */
+  struct bins bin[50];
+  for (int i = 0; i < 50; i++) {
+    bin[i] = (struct bins){.x = 0, .y = 0, .w = 0, .h = 0};
+  }
+  /* default bin */
+  bin[0] = (struct bins){.x = 0, .y = 0, .w = *bin_width + mb.gaps, .h = *bin_height + mb.gaps};
+
+  unsigned bin_count = 1;
+  int check;
+
+  /* loop through each rect */
+  for (unsigned i = 0; i < *length; i++) {
+    check = 0;
+    /* loop through each bin */
+    for (unsigned 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) {
+        check = 1;
+        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 does not fit all bin */
+    if (check == 0) {
+      /* Grow main bin if possible */
+      if (mb.x > *bin_width) {
+        *bin_width += 2;
+      }
+      if (mb.y > *bin_height) {
+        *bin_height += 2;
+      }
+      return true;
+    }
+  }
+  return false;
+}
+
+bool resize(struct output out[], struct input r[], const unsigned *length,
+            unsigned *bin_width, unsigned *bin_height, struct mbin mb) {
+  /* When a bin fills all the space available, pack_bin */
+
+  for (unsigned i = 0; i < *length; i++) {
+    unsigned check = 0;
+    while (pack_bin(out, r, length, bin_width, bin_height, mb)) {
+      if (check == mb.x) {
+        return false;
+      }
+      check++;
+    }
+    /* 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 (unsigned i = 0; i < *length; i++) {
+    if (w < (out[i].w + out[i].x)) {
+      w = out[i].w + out[i].x;
+    }
+    if (h < (out[i].h + out[i].y)) {
+      h = out[i].h + out[i].y;
+    }
+  }
+
+  if (h >= mb.y - mb.gaps) {
+    return false;
+  }
+  return true;
+}
+
+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': {
+        fputs("Usage: bin_pack -x screen_width -y screen_height -g gaps\n",
+              stderr);
+        return EXIT_SUCCESS;
+      }
+    }
+  }
+
+  struct input r[50];
+  struct output out[50];
+
+  const unsigned 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;
+
+  /* If we have no large windows */
+  bool test = true;
+  for (unsigned 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;
+      test = false;
+    }
+  }
+  unsigned count = 0;
+
+  if (test == 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 (count == mb.x) {
+        return EXIT_FAILURE;
+      }
+      count++;
+    }
+
+    count = 0;
+
+    /* Square out the blob as best as we can */
+    while (resize(out, r, &length, &bin_width, &bin_height, mb)) {
+      if (count == mb.x) {
+        return EXIT_FAILURE;
+      }
+      count++;
+    }
+  }
+
+  center(&length, out, mb);
+
+  for (unsigned 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;
+}