commit ee48872cc8dac3654b54058e0a4e665da1e534d6
Author: anon <anon@anon.anon>
Date:   Fri Jan 3 16:20:48 2025 +0100

    working demo

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f47cb20
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.out
diff --git a/documentation/reference.jpg b/documentation/reference.jpg
new file mode 100644
index 0000000..d85dd13
Binary files /dev/null and b/documentation/reference.jpg differ
diff --git a/examples/ncurses_columns.c b/examples/ncurses_columns.c
new file mode 100644
index 0000000..6e36536
--- /dev/null
+++ b/examples/ncurses_columns.c
@@ -0,0 +1,51 @@
+// @BAKE gcc $@ -o $*.out -I../ $(pkg-config --cflags --libs ncurses) -lm
+#include <stdio.h>
+#include <ncurses.h>
+#include "rect_layouts.h"
+
+WINDOW * windows[5] = { NULL, NULL, NULL, NULL, NULL };
+
+void do_resize(void) {
+    rect_t parent = center(get_screen_rect(), scale(get_screen_rect(), 0.8));
+    for (int i = 0; i < 5; i++) {
+        delwin(windows[i]);
+        // NOTE: we are actually redundantly recalculating,
+        //        both for scaling and in the expansion of UNPACK;
+        //       a temp var should be deployed;
+        //       now, this is generally bad and should be avoided,
+        //        however it *does work* and developer comfort is a top priority
+        windows[i] = newwin(UNPACK(next(rfloor(scalex(parent, 0.2)), i)));
+        refresh();
+    }
+    
+    clear();
+    wrefresh(stdscr);
+}
+
+signed main(void) {
+    initscr();
+    noecho();
+    curs_set(0);
+    halfdelay(1);
+
+    do_resize();
+
+    while(1) {
+        switch (wgetch(stdscr)) {
+            case KEY_RESIZE: do_resize();
+        }
+
+        for (int i = 0; i < 5; i++) {
+            int maxy, maxx;
+            getmaxyx(windows[i], maxy, maxx);
+            for (int h = 0; h < (maxy * maxx); h++) {
+                waddch(windows[i], '0' + i);
+            }
+            box(windows[i], ACS_VLINE, ACS_HLINE);
+            wrefresh(windows[i]);
+        }
+    }
+
+    endwin();
+    return 0;
+}
diff --git a/examples/raylib_diablo_like.c b/examples/raylib_diablo_like.c
new file mode 100644
index 0000000..561aac0
--- /dev/null
+++ b/examples/raylib_diablo_like.c
@@ -0,0 +1,60 @@
+// @BAKE gcc $@ -o $*.out -I../ -lraylib -lGL -lm -lpthread -ldl -lrt -lX11
+#include <raylib.h>
+#include "rect_layouts.h"
+
+rect_t hotbar;
+rect_t healt;
+rect_t manna;
+rect_t inventory;
+rect_t inventory_inner;
+rect_t inventory_slot;
+
+void do_resize(void) {
+    hotbar = ride(after(get_screen_rect(), 1), scaley(get_screen_rect(), 0.17f));
+    healt = ride(after(get_screen_rect(), 1), scale(get_unit_rect(), hotbar.height * 1.2f));
+    manna = paper(hotbar, healt);
+    inventory = stretchy(hotbar, paper(get_screen_rect(), scalex(get_screen_rect(), 0.5f)));
+    inventory_inner = buoyance(inventory, scale(inventory, 0.8f));
+    inventory_slot = rock(inventory_inner, hang(inventory_inner, scale(get_unit_rect(), inventory_inner.width / 9)));
+}
+
+signed main(void) {
+    SetConfigFlags(FLAG_WINDOW_RESIZABLE);
+    InitWindow(600, 600, "");
+
+    do_resize();
+
+    while (!WindowShouldClose()) {
+        if (IsWindowResized()) {
+            do_resize();
+        }
+
+      BeginDrawing();
+        DrawRectangleRec(get_screen_rect(), RAYWHITE);
+        DrawRectangleRec(inventory, BLACK);
+        DrawRectangleRec(inventory_inner, GRAY);
+
+        for (int i = 0; i < 6; i++) {
+            for (int h = 0; h < 9; h++) {
+                DrawRectangleRec(
+                    after(next(inventory_slot, h), i),
+                    (Color) {
+                        (unsigned char)(0 + ((i + h) * 10)),
+                        (unsigned char)(0 + ((i + h) * 10)),
+                        (unsigned char)(0 + ((i + h) * 10)),
+                        255,
+                    }
+                );
+            }
+        }
+
+        DrawRectangleRec(hotbar, GRAY);
+        DrawRectangleRec(healt, RED);
+        DrawRectangleRec(manna, BLUE);
+      EndDrawing();
+    }
+
+    CloseWindow();
+
+    return 0;
+}
diff --git a/rect_layouts.h b/rect_layouts.h
new file mode 100644
index 0000000..9811393
--- /dev/null
+++ b/rect_layouts.h
@@ -0,0 +1,404 @@
+#ifndef RECT_LAYOUTS_H
+#define RECT_LAYOUTS_H
+/* This header file defines with intuitive rectangle transformations.
+ *
+ * The intended purpose is to ease defining single screen interfaces.
+ *  Layout engines are great, but very complex to implement and master.
+ *  Static layouts on the otherhand tend to get very ugly, verbose and bug prone.
+ *  The idea is to have a very minimalistic abstraction layer,
+ *   with easy to visualize operations.
+ *  This solves readability and typo-ing x to y, while being widely usable,
+ *   adaptable or even reimplementable within just a few minutes.
+ *
+ * No doubt someone somewhere has adapted a similar approach before,
+ *  however to the best of my knowledge this is the first attempt
+ *  to normalize it into a library.
+ *  In case I'm wrong, please throw me and email.
+ */
+
+// TODO: further macro hell x, y, width and height so they can be user overwritten too
+// TODO: stretch(x|y) naming is meh
+// TODO: theres a logical inconsistency between ride-hang and rock-paper; either justify it or normalize it
+
+
+// ### --------------- ###
+// ### SPECIALIZATIONS ###
+// ### --------------- ###
+/* This has to go on top, because macros. Please read on.
+ */
+
+#ifdef RAYLIB_H
+# define rect_t Rectangle
+static inline
+rect_t get_screen_rect(void) {
+    return (rect_t) {
+        .x = 0,
+        .y = 0,
+        .width  = (float)GetScreenWidth(),
+        .height = (float)GetScreenHeight(),
+    };
+}
+#endif
+
+
+// ### ------- ###
+// ### General ###
+// ### ------- ###
+
+/* Our internal rectangle representation.
+ *  Feel free to overwrite it with whatever suits you,
+ *   just #define alias it to `rect_t`.
+ */
+#ifndef rect_t
+typedef struct rect_t {
+    float x, y, width, height;
+} rect_t;
+#endif
+
+#ifdef __NCURSES_H
+# define UNPACK(r) (int)r.height, (int)r.width, (int)r.y, (int)r.x
+static inline
+rect_t get_screen_rect(void) {
+    return (rect_t) {
+        .x = 0,
+        .y = 0,
+        .width  = (float)COLS,
+        .height = (float)LINES,
+    };
+}
+#endif
+
+// tl;dr
+static inline rect_t rfloor(rect_t r);
+static inline rect_t get_unit_rect(void);
+static inline rect_t scaley(rect_t a, float f);
+static inline rect_t scalex(rect_t a, float f);
+static inline rect_t scale(rect_t a, float f);
+static inline rect_t balance(rect_t dest, rect_t source);
+static inline rect_t buoyance(rect_t dest, rect_t source);
+static inline rect_t center(rect_t dest, rect_t source);
+static inline rect_t hang(rect_t dest, rect_t source);
+static inline rect_t ride(rect_t dest, rect_t source);
+static inline rect_t rock(rect_t dest, rect_t source);
+static inline rect_t paper(rect_t dest, rect_t source);
+static inline rect_t next(rect_t source, int n);
+static inline rect_t after(rect_t source, int n);
+static inline rect_t stretchy(rect_t dest, rect_t source);
+static inline rect_t stretchx(rect_t dest, rect_t source);
+
+
+
+/* Floor every field of a rect.
+ *  Useful if next() or after() create visible gaps.
+ *  NOTE: we are not actually floaring so we dont depend on <math.h>,
+ *         for our ends and purposes it should just werk™
+ */
+static inline
+rect_t rfloor(rect_t r) {
+    return (rect_t) {
+        .x = (long long)r.x,
+        .y = (long long)r.y,
+        .width  = (long long)r.width,
+        .height = (long long)r.height,
+    };
+}
+
+
+/* Return the easiest rect to transform.
+ *  Coordinates (0, 0) are easy to shift.
+ *  Size 1x1 is easy to scale.
+ * 
+ *   +-+
+ *   +-+
+ */
+static inline
+rect_t get_unit_rect(void) {
+    return (rect_t) {
+        .x = 0,
+        .y = 0,
+        .width  = 1,
+        .height = 1,
+    };
+}
+
+
+/* Modify the width by a factor.
+ *
+ *   +-+   __\  +---+
+ *   +-+     /  +---+
+ */
+static inline
+rect_t scalex(rect_t a, float f) {
+    return (rect_t) {
+        .x = a.x,
+        .y = a.y,
+        .width  = a.width  * f,
+        .height = a.height,
+    };
+}
+
+
+/* Modify the height by a factor.
+ *
+ *   +-+   __\  +-+
+ *   +-+     /  | |
+ *              +-+
+ */
+static inline
+rect_t scaley(rect_t a, float f) {
+    return (rect_t) {
+        .x = a.x,
+        .y = a.y,
+        .width  = a.width,
+        .height = a.height * f,
+    };
+}
+
+
+/* Modify the height and width by a factor.
+ *  Exists because its judged to be a common operation.
+ *
+ *   +-+   __\  +---+
+ *   +-+     /  |   |
+ *              +---+
+ */
+static inline
+rect_t scale(rect_t a, float f) {
+    return (rect_t) {
+        .x = a.x,
+        .y = a.y,
+        .width  = a.width  * f,
+        .height = a.height * f,
+    };
+}
+
+/* Align to the middle horiontally
+ *
+ *   +---+---+---+
+ *   | :         |
+ *   +---+       |
+ *   |   |       |
+ *   |   |       |
+ *   +---+       |
+ *   | :         |
+ *   +---+---+---+
+ */
+static inline
+rect_t balance(rect_t dest, rect_t source) {
+    return (rect_t) {
+        .x = dest.x + ((dest.width - source.width) / 2),
+        .y = source.y,
+        .width  = source.width,
+        .height = source.height,
+    };
+}
+
+/* Align to the middle vertically
+ *
+ *   +---+---+---+
+ *   |   |   |   |
+ *   | - |   | - |
+ *   |   +---+   |
+ *   |           |
+ *   |           |
+ *   |           |
+ *   +---+---+---+
+ */
+static inline
+rect_t buoyance(rect_t dest, rect_t source) {
+    return (rect_t) {
+        .x = source.x,
+        .y = dest.y + ((dest.height - source.height) / 2),
+        .width  = source.width,
+        .height = source.height,
+    };
+}
+
+
+/* Blance and Buoyance. Align to the middle vertically and horizontally.
+ *  Exists because its judged to be a common operation.
+ *
+ *   +-----------+
+ *   |     :     |
+ *   |   +---+   |
+ *   | - |   | - |
+ *   |   |   |   |
+ *   |   +---+   |
+ *   |     :     |
+ *   +-----------+
+ */
+static inline
+rect_t center(rect_t dest, rect_t source) {
+    return balance(dest, buoyance(dest, source));
+}
+
+
+/* Dangles from the top.
+ *
+ *   +---+-------+
+ *   |   |       |
+ *   |   |       |
+ *   +---+       |
+ *   |           |
+ *   |           |
+ *   |           |
+ *   +-----------+
+ */
+static inline
+rect_t hang(rect_t dest, rect_t source) {
+    return (rect_t) {
+        .x = source.x,
+        .y = dest.y,
+        .width  = source.width,
+        .height = source.height,
+    };
+}
+
+
+/* Places on the top.
+ *
+ *   +---+
+ *   |   |
+ *   |   |
+ *   +---+-------+
+ *   |           |
+ *   |           |
+ *   |           |
+ *   |           |
+ *   |           |
+ *   |           |
+ *   +-----------+
+ */
+static inline
+rect_t ride(rect_t dest, rect_t source) {
+    return (rect_t) {
+        .x = source.x,
+        .y = dest.y - source.height,
+        .width  = source.width,
+        .height = source.height,
+    };
+}
+
+
+/* Moves to the left.
+ *  NOTE: this is a reference to the political compas, for easy memorization.
+ *
+ *   +---+-------+
+ *   |   |       |
+ *   |   |       |
+ *   +---+       |
+ *   |           |
+ *   |           |
+ *   |           |
+ *   +-----------+
+ */
+static inline
+rect_t rock(rect_t dest, rect_t source) {
+    return (rect_t) {
+        .x = dest.x,
+        .y = source.y,
+        .width  = source.width,
+        .height = source.height,
+    };
+}
+
+
+/* Moves to the right.
+ *  NOTE: this is a reference to the political compas, for easy memorization.
+ *
+ *   +-------+---+
+ *   |       |   |
+ *   |       |   |
+ *   |       +---+
+ *   |           |
+ *   |           |
+ *   |           |
+ *   +-----------+
+ */
+static inline
+rect_t paper(rect_t dest, rect_t source) {
+    return (rect_t) {
+        .x = (dest.x + dest.width) - source.width,
+        .y = source.y,
+        .width  = source.width,
+        .height = source.height,
+    };
+}
+
+
+/* Gets the N-th horiontal neighbour.
+ *
+ *   +---+---+
+ *   |   |   |
+ *   |   |   |
+ *   +---+---+
+ */
+static inline
+rect_t next(rect_t source, int n) {
+    return (rect_t) {
+        .x = source.x + (source.width * n),
+        .y = source.y,
+        .width  = source.width,
+        .height = source.height,
+    };
+}
+
+/* Gets the N-th vectical neighbour.
+ *
+ *   +---+    
+ *   |   |
+ *   |   |
+ *   +---+
+ *   |   |
+ *   |   |
+ *   +---+
+ */
+static inline
+rect_t after(rect_t source, int n) {
+    return (rect_t) {
+        .x = source.x,
+        .y = source.y + (source.height * n),
+        .width  = source.width,
+        .height = source.height,
+    };
+}
+
+static inline
+rect_t stretchy(rect_t dest, rect_t source) {
+    return (dest.y > source.y) ?
+        (rect_t) {
+            .x = source.x,
+            .y = source.y,
+            .width  = source.width,
+            .height = dest.y - source.y,
+        }
+    :
+        (rect_t) {
+            .x = source.x,
+            .y = dest.y + dest.height,
+            .width  = source.width,
+            .height = source.height + (source.y - (dest.y + dest.height)),
+        }
+    ;
+}
+
+static inline
+rect_t stretchx(rect_t dest, rect_t source) {
+    return (dest.x > source.x) ?
+        (rect_t) {
+            .x = source.x,
+            .y = source.y,
+            .width  = dest.x - source.x,
+            .height = source.height,
+        }
+    :
+        (rect_t) {
+            .x = dest.x + dest.width,
+            .y = source.y,
+            .width  = source.width + (source.x - (dest.x + dest.width)),
+            .height = source.height,
+        }
+    ;
+}
+
+#endif