Death Star

Writes a PGM to stdout.

func sq(*nums) {
    nums »**» 2 «+»;
}

func hitf(sph, x, y) {
    x -= sph[0];
    y -= sph[1];

    var z = (sq(sph[3]) - sq(x, y));
    z < 0 && return();

    z.sqrt!;
    [sph[2] - z, sph[2] + z];
}

func normalize(v) {
    var n = sq(v...).sqrt;
    v »/» n;
}

func dot(x, y) {
    var s = (x[0]*y[0] + x[1]*y[1] + x[2]*y[2]);
    s > 0 ? s : 0;
}

var pos = [120, 120, 0, 120];
var neg = [-77, -33, -100, 190];
var light = normalize([-12, 13, -10]);

func draw(k, amb) {
    STDOUT.binmode(':raw');
    print ("P5\n", pos[0]*2 + 3, " ", pos[1]*2 + 3, "\n255\n");

    for y in ((pos[1] - pos[3] - 1) .. (pos[1] + pos[3] + 1)) {
        var row = [];
        for x in ((pos[0] - pos[3] - 1) .. (pos[0] + pos[3] + 1)) {
            var hit = 0;
            var hs = [];
            var h = hitf(pos, x, y);

            if    (!h)                      { hit = 0; h  = [0, 0] }
            elsif (!(hs = hitf(neg, x, y))) { hit = 1; hs = [0, 0] }
            elsif (hs[0] > h[0])            { hit = 1 }
            elsif (hs[1] > h[0])            { hit = (hs[1] > h[1] ? 0 : 2) }
            else                            { hit = 1 };

            var (val, v);
            given(hit) {
                when (0) { val = 0}
                when (1) { v = [x-pos[0], y-pos[1], h[0]-pos[2]] }
                default  { v = [neg[0]-x, neg[1]-y, neg[2]-hs[1]] }
            }

            if (v) {
                v = normalize(v);
                val = int((dot(v, light)**k + amb) * 255);
                val = (val > 255 ? 255 : (val < 0 ? 0 : val));
            };
            row.append(val);
        }
        print 'C*'.pack(row...);
    }
}

draw(2, 0.2);