py_fixpt.c

download raw

/* Draw a pythagoras triangle with no trig calls */
#include <math.h>
#include <stdio.h>
#include <stdint.h>
#include <assert.h>

#if 1
// Implementation for 16.16 fixed point
#include <inttypes.h> // PRId32
typedef int32_t F; // or long if stdint not available
#define M(name) (name ## fix)
#define ZERO (0)
#define ONE (INT32_C(1) << 16)
#define PRIF(a, b) #a PRId32
#define MUL(a,b) mulfix((a), (b))
#define RECIP(x) ((0x80000000 / (x)) << 1)
#define PROLOGUE "1 65536 div dup scale"

// From "Hacker's Delight" figure 11-1 adapted for 16.16 fixed point
int32_t mulfix(int32_t u, int32_t v) {
    uint32_t u0, v0, w0;
    int32_t u1, v1, w1, w2, t;
    u0 = u & 0xFFFF; u1 = u >> 16;
    v0 = v & 0xFFFF; v1 = v >> 16;
    w0 = u0*v0;
    t = u1*v0 + (w0 >> 16);
    w1 = t & 0xFFFF;
    w2 = t >> 16;
    w1 = u0*v1 + w1;
    return ((u1*v1 + w2) << 16) + w1;
}


// From https://github.com/chmike/fpsqrt/blob/master/fpsqrt.c MIT license
F sqrtfix(F v) {
    uint32_t t, q, b, r;
    r = (int32_t)v; 
    q = 0;          
    b = 0x40000000UL;
    if( r < 0x40000200 )
    {
        while( b != 0x40 )
        {
            t = q + b;
            if( r >= t )
            {
                r -= t;
                q = t + b; // equivalent to q += 2*b
            }
            r <<= 1;
            b >>= 1;
        }
        q >>= 8;
        return q;
    }
    while( b > 0x40 )
    {
        t = q + b;
        if( r >= t )
        {
            r -= t;
            q = t + b; // equivalent to q += 2*b
        }
        if( (r & 0x80000000) != 0 )
        {
            q >>= 1;
            b >>= 1;
            r >>= 1;
            while( b > 0x20 )
            {
                t = q + b;
                if( r >= t )
                {
                    r -= t;
                    q = t + b;
                }
                r <<= 1;
                b >>= 1;
            }
            q >>= 7;
            return q;
        }
        r <<= 1;
        b >>= 1;
    }
    q >>= 8;
    return q;
}
#else
typedef float F;
#define M(name) (name ## fix)
#define ZERO (0.f)
#define ONE (1.f)
#define PRIF(a, b) #a "." #b "f"
#define MUL(a,b) (a*b)
#define RECIP(x) (1.f/x)
#define PROLOGUE ""
#endif

#define MOVETO "moveto"
#define LINETO "lineto"
#define LINETO_CLOSEPATH_STROKE "lineto closepath stroke"

typedef struct { F a, b, c, d, e, f; } PSMatrix;

void matmul(PSMatrix *result, PSMatrix *m1, PSMatrix *m2) {
    PSMatrix r;
    r.a = MUL(m2->a, m1->a) + MUL(m2->b, m1->c);
    r.b = MUL(m2->a, m1->b) + MUL(m2->b, m1->d);
    r.c = MUL(m2->c, m1->a) + MUL(m2->d, m1->c);
    r.d = MUL(m2->c, m1->b) + MUL(m2->d, m1->d);
    r.e = MUL(m2->e, m1->a) + MUL(m2->f, m1->c) + m1->e;
    r.f = MUL(m2->e, m1->b) + MUL(m2->f, m1->d) + m1->f;
    *result = r;
}

F A = 4 * ONE, B = 3 * ONE, C, C_recip;
F cos_theta, sin_theta;

// Each matrix moves from the old baseline [bottom of square] to the baseline of the square on the B or C side
// of the triangle.
PSMatrix toB, toC, identity = {ONE, ZERO, ZERO, ONE, ZERO, ZERO};

void make_matrix(PSMatrix *m, F x1, F y1, F x2, F y2) {
    F dx = x2-x1, dy = y2-y1;
    F ux = MUL(dx, C_recip);
    F uy = MUL(dy, C_recip);

    PSMatrix mm = { ux, uy, -uy, ux, x1, y1 };

    *m = mm;
}

void op_transformed(const char *op, PSMatrix *m, F x, F y) {
    F tx = MUL(m->a, x) + MUL(m->c, y) + m->e,
      ty = MUL(m->b, x) + MUL(m->d, y) + m->f;
    printf("%" PRIF(,1) " %" PRIF(,1) " %s\n", tx, ty, op);
}

void recurse(PSMatrix *m, int n) {
    op_transformed(MOVETO, m, 0, 0);
    op_transformed(LINETO, m, 0, C);
    op_transformed(LINETO, m, C, C);
    op_transformed(LINETO_CLOSEPATH_STROKE, m, C, 0);

    if (n > 0) {
        PSMatrix m1;
        matmul(&m1, m, &toB);
        recurse(&m1, n-1);
        matmul(&m1, m, &toC);
        recurse(&m1, n-1);
    }

}

int main() {
    C = M(sqrt)(MUL(A, A) + MUL(B, B));
    C_recip = RECIP(C);
    F dy = MUL(MUL(A, B), C_recip);
    F dx = M(sqrt)(MUL(A, A) - MUL(dy, dy));
    make_matrix(&toB, 0, C, dx, C+dy);
    make_matrix(&toC, dx, C+dy, C, C);

    fprintf(stderr, "% " PRIF(6,2) " % " PRIF(6,2) " % " PRIF(6,2) "\n% " PRIF(6,2) " % " PRIF(6,2) " % " PRIF(6,2) "\n\n",
            toB.a, toB.b, toB.c,
            toB.d, toB.e, toB.f);

    PSMatrix initial = {ONE * 72 / 8, ZERO, ZERO, ONE*72 / 8, ONE*72*3, ONE*8};

    printf("%s\n", PROLOGUE);
    recurse(&initial, 6);
    printf("showpage\n");
}