TriKuf

TriKuf

All ideas and code are placed in the public domain.

A trikuf is a 3x3 tile that conforms to the basic rules of square geometric kufic calligraphy. It is inspired by the kuf project.

The basic rule is: given any 2x2 tile quad ABCD (left to right, top to bottom), cannot have the following patterns: 0000 (0x00), 0110 (0x6), 1001 (0x9), 1111 (0xF).

This C program below will generate all the possible 3x3 tiles, and filter out the tiles that do not conform to square kufic. The valid tiles will be printed to standard output.

In addition to being technically correct square kufic, the trikufs selected to be printed also do not have any empty columns. This is an extra constraint added for aesthetic reasons. The idea behind this was to create a set of glyphs that could be more visually distinct on the horizontal axis when separated by whitespace. (And the reason why I want to do build glyphs like this is because I'm designing a symbolic programming language and VM for the monome grid).

If USE_BTPRNT is defined, it will also use btprnt to generate a pretty PBM that looks like this:

To begin, the top level code.

<<trikuf.c>>=
#include <stdio.h>
#include <stdint.h>
#include "btprnt/btprnt.h"

<<make_quad>>
<<violation_quad>>
<<trirow>>
<<trirow_get>>
<<triglyph_to_trirows>>
<<trikuf>>
<<print>>
<<noblankcols>>

#ifdef USE_BTPRNT
int drawglyph(btprnt_region *reg, uint16_t g, int pos)
{
    int x, y;
    int offx, offy;

    offx = 2 + (pos % 14) * 19;
    offy = 2 + (pos / 14) * 19;

    btprnt_draw_rect(reg, offx, offy, 16, 16, 1);

    offx += 2;
    offy += 2;

    for (y = 0; y < 3; y++) {
        uint8_t r;
        r = g >> 3*y;
        for (x = 0; x < 3; x++) {
            if (r & (1 << x)) {
                btprnt_draw_rect_filled(reg,
                offx + 4*x,
                offy + 4*y, 4, 4, 1);
            }
        }
    }
}
#endif

int main(int argc, char *argv[])
{
    uint16_t g;
    uint16_t gmax;
    int count;
#ifdef USE_BTPRNT
    btprnt *bp;
    btprnt_region r;
#endif
    int w, h;


    w = 4 + 14 * 19;
    h = 4 + 7 * 19;

#ifdef USE_BTPRNT
    bp = btprnt_new(w, h);

    btprnt_region_init(btprnt_canvas_get(bp),
                       &r, 2, 2,
                       w - 4, h - 4);
#endif

    gmax = (1 << 9) - 1;

    count = 0;

    for (g = 0; g <= gmax; g++) {
        if (trikuf(g) && noblankcols(g)) {
            print_triglyph(g);
#ifdef USE_BTPRNT
            drawglyph(&r, g, count);
#endif
            count++;
        }
    }


#ifdef USE_BTPRNT
    btprnt_buf_pbm(btprnt_buf_get(bp), "out.pbm");
    btprnt_del(&bp);
#endif

    printf("%d total trikufs\n", count);
    return 0;
}

A quad is a set of 4 tiles A, B, C, and D, which gets represented 4 bits in a binary value. mkquad will construct one of these quads.

<<make_quad>>=
uint8_t mkquad(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
{
    return (a & 1) | (b & 1) << 1 | (c & 1) << 2 | (d  & 1) << 3;
}

The binary representation fo the quad allows to quickly check if a quad is a so-called "violation" pattern.

<<violation_quad>>=
int violation(uint8_t q)
{
    return q == 0x00 || q == 0x6 || q == 0x9 || q == 0xF;
}

A trirow is a set of 3 tiles aligned horizontally, represented as 3 bits in an 8-bit integer.

A trirow can be created with mktrirow, with bits x, y, and z (which correspond from left to right).

<<trirow>>=
uint8_t mktrirow(uint8_t x, uint8_t y, uint8_t z)
{
    return (x & 1) | (y & 1) << 1 | (z & 1) << 2;
}

For a given row, a bit at position p can be retrieved. This is expected to be in range 0-2.

<<trirow_get>>=
uint8_t trirow_get(uint8_t r, int p)
{
    return (r & (1 << p)) >> p;
}

A 3x3 set of tiles is referred here as a triglyph, stored as 9 bits inside of a 16-bit integer. The bits of a triglyph can be broken up into 3 trirows, which will be called a, b, and c. This is done with the function triglyph2trirows.

<<triglyph_to_trirows>>=
void triglyph2trirows(uint16_t g, uint8_t *a, uint8_t *b, uint8_t *c)
{
    *a = g & 0x7;
    g >>= 3;
    *b = g & 0x7;
    g >>= 3;
    *c = g & 0x7;
}

These operations defined above can now be used to create the trikuf checker.

A triglyph g can be represented as 3 trirows top-to-bottom a, b, c, each consisting of 3 bits:

A position a(0) would indicate the first bit at row a.

A map of the layout:

a 0 1 2
b 0 1 2
c 0 1 2

Up to 4 quads will need to be checked for violations. Using they notation described above, the quads are the following (moving left to right, top to bottom):

a(0) a(1) b(0) b(1)
a(1) a(2) b(1) b(2)
b(0) b(1) c(0) c(1)
b(1) b(2) c(1) c(2)

<<trikuf>>=
int check(uint8_t a, uint8_t b,
          uint8_t w, uint8_t x, uint8_t y, uint8_t z)
{
    return violation(mkquad(
        trirow_get(a, w),
        trirow_get(a, x),
        trirow_get(b, y),
        trirow_get(b, z)));
}

int trikuf(uint16_t g)
{
    uint8_t a, b, c;
    int rc;

    a = b = c = 0;

    triglyph2trirows(g, &a, &b, &c);

    rc = check(a, b, 0, 1, 0, 1);
    if (rc) return 0;
    rc = check(a, b, 1, 2, 1, 2);
    if (rc) return 0;
    rc = check(b, c, 0, 1, 0, 1);
    if (rc) return 0;
    rc = check(b, c, 1, 2, 1, 2);
    if (rc) return 0;

    return 1;
}

The function print_triglyph is used to print a triglyph to standard output, using '#' to represent a filled tile, and '-' to represent an empty tile. The function will also place a little border around the triglyph as well.

<<print>>=
void print_triglyph(uint16_t g)
{
    int x, y;

    printf("%x:\n", g);
    printf("+---+\n");
    for (y = 0; y < 3; y++) {
        uint8_t r;
        r = g >> 3*y;
        putchar('|');
        for (x = 0; x < 3; x++) {
            if (r & (1 << x)) {
                putchar('#');
            } else {
                putchar('-');
            }
        }
        printf("|\n");
    }
    printf("+---+\n");
}

An additional constraint added to this program is to check that a glyph has no empty columns. This is done with the function noblankcols.

<<noblankcols>>=
int checkcol(uint8_t a, uint8_t b, uint8_t c, int pos)
{
    return trirow_get(a, pos) == 0 &&
           trirow_get(b, pos) == 0 &&
           trirow_get(c, pos) == 0;
}

int noblankcols(uint16_t g)
{
    uint8_t a, b, c;
    int rc;

    a = b = c = 0;
    triglyph2trirows(g, &a, &b, &c);

    rc = checkcol(a, b, c, 0);
    if (rc) return 0;
    rc = checkcol(a, b, c, 1);
    if (rc) return 0;
    rc = checkcol(a, b, c, 2);
    if (rc) return 0;

    return 1;
}

home | index