Protogestling

Protogestling

This is the drawing code needed to make the protogestling face. It uses SDFs to draw the primitives, which are then rasterized to a btprnt canvas.

To see it in action, see the Protogestling Mockup.

<<protogestling.c>>=
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

#include "mathc/mathc.h"
#include "sdf2d/sdf.h"
#include "btprnt/btprnt.h"

static void draw_ellipse(btprnt_region *r, float a, float b)
{
    int x, y;
    int w, h;
    float ratio;
    w = r->w;
    h = r->h;

    ratio = (float)w / h;

    if (ratio > 1) {
        ratio = 1.0 / ratio;
    }
    for (y = 0; y < h; y++) {
        for (x = 0; x < w; x++) {
            struct vec2 p;
            float d;
            struct vec2 res;
            struct vec2 ab;
            int c;
            res = svec2(w, h);

            p = sdf_normalize(svec2(x, y), res);
            p.x *= ratio;
            ab = svec2(a, b);
            d = sdf_ellipse(p, ab);

            if (isnan(d)) {
                printf("oops\n");
            }

            c = (int)(1 - sdf_sign(d));
            c = c != 0;
            if (c) {
                btprnt_region_draw(r, x, y, c);
            }
        }
    }
}

static void draw_face(btprnt_region *r,
                      double mouth_w,
                      double mouth_h,
                      double eye_left_w,
                      double eye_left_h,
                      double eye_right_w,
                      double eye_right_h)
{
    btprnt_region mouth;
    btprnt_region eyes[2];

    mouth = *r;

    mouth.y += mouth.h / 2;
    mouth.h = mouth.h / 2;
    eyes[0] = *r;
    eyes[0].w /= 2;
    eyes[0].h /= 2;
    eyes[1] = eyes[0];
    eyes[1].x += eyes[1].w;

    draw_ellipse(&mouth, mouth_w, mouth_h);
    draw_ellipse(&eyes[0], eye_left_w, eye_left_h);
    draw_ellipse(&eyes[1], eye_right_w, eye_right_h);
}

static int face(lua_State *L)
{
    btprnt_region *reg;
    double mouth_w;
    double mouth_h;
    double eye_left_w;
    double eye_left_h;
    double eye_right_w;
    double eye_right_h;
    reg = lua_touserdata(L, 1);
    mouth_w = lua_tonumber(L, 2);
    mouth_h = lua_tonumber(L, 3);
    eye_left_w = lua_tonumber(L, 4);
    eye_left_h = lua_tonumber(L, 5);
    eye_right_w = lua_tonumber(L, 6);
    eye_right_h = lua_tonumber(L, 7);
    draw_face(reg,
      mouth_w, mouth_h,
      eye_left_w, eye_left_h,
      eye_right_w, eye_right_h);
    return 0;
}

struct lil_bpfont;

btprnt_buf * lil_bpfont_buf(struct lil_bpfont *fnt);
int lil_bpfont_width(struct lil_bpfont *fnt);
int lil_bpfont_height(struct lil_bpfont *fnt);

static int textline(lua_State *L)
{
    btprnt_region *reg;
    struct lil_bpfont *fnt;
    int x, y;
    int clr, scale;
    const char *str;
    btprnt_buf *buf;
    int fw, fh;
    int sz;
    int n;
    int nargs;

    nargs = lua_gettop(L);
    reg = lua_touserdata(L, 1);
    fnt = lua_touserdata(L, 2);
    x = lua_tointeger(L, 3);
    y = lua_tointeger(L, 4);
    str = lua_tostring(L, 5);
    scale = lua_tointeger(L, 6);
    clr = lua_tointeger(L, 7);

    buf = lil_bpfont_buf(fnt);
    fw = lil_bpfont_width(fnt);
    fh = lil_bpfont_height(fnt);

    sz = strlen(str);
    if (nargs >= 8) {
        int tmp;
        tmp = lua_tointeger(L, 8);

        if (tmp < sz) {
            sz = tmp;
        }
    }

    for (n = 0; n < sz; n++) {
        btprnt_draw_char(reg, buf, x, y, fw, fh, str[n], scale, clr);
        x += fw * scale;
    }

    return 0;
}

static const luaL_Reg protogestling_lib[] = {
    {"face", face},
    {"textline", textline},
    {NULL, NULL}
};

int luaopen_protogestling(lua_State *L)
{
    luaL_newlib(L, protogestling_lib);
    return 1;
}

1. Lua Mockup Code