# I wrote this on Beatrice's birthday, but didn't get around to posting
# it until now.

#!/usr/bin/python
# 5-dimensional hypercube with 32 vertices for Bea's 32nd birthday
# Written in Python with list-comprehensions and "sum"; works in
# Python 2.3, might work in 2.2
import random, math

def vecmag(vec):
    return math.sqrt(sum([x*x for x in vec]))

def vec_norm(vec):
    size = vecmag(vec)  
    return [x / size for x in vec]

# generate randomish vectors to project things onto 
# These vectors are insufficiently random --- they're more likely to
# point toward the corners of the unit cube rather than the faces.  oh
# well.
def randvec():
    return vec_norm([random.random() * 2 - 1 for ii in range(5)])

def fourdcubepoints():
    rv = []
    for a in (0, 1):
        for b in (0, 1):
            for c in (0, 1):
                for d in (0, 1):
                    rv.append((a, b, c, d))
    return rv

def cubelines():
    points = fourdcubepoints()
    rv = []
    for dimension in range(5):
        for point in points:
            rv.append((
                point[:dimension] + (0,) + point[dimension:],
                point[:dimension] + (1,) + point[dimension:]))
    return rv

def dotprod(a, b):
    assert len(a) == len(b)
    return sum([a[ii] * b[ii] for ii in range(len(a))])

def twodlines(veca, vecb):
    lines = cubelines()
    return [((dotprod(line[0], veca), dotprod(line[0], vecb)),
         (dotprod(line[1], veca), dotprod(line[1], vecb)))
        for line in lines]

def pslines(lines, scale):
    rv = []
    for line in lines:
        rv.append("%d %d moveto\n" % (line[0][0] * scale, line[0][1] * scale))
        rv.append("%d %d lineto\n" % (line[1][0] * scale, line[1][1] * scale))
    rv.append("stroke showpage\n")
    return ''.join(rv)

def bbox(lines):
    minx, miny, maxx, maxy = 10000, 10000, -10000, -10000
    for line in lines:
        for x, y in line:
            if x < minx: minx = x
            if x > maxx: maxx = x
            if y < miny: miny = y
            if y > maxy: maxy = y
    return minx, miny, maxx, maxy

def getscale(box, width, height):
    minx, miny, maxx, maxy = box
    yscale = height / (maxy - miny)
    xscale = width / (maxx - minx)
    return min(xscale, yscale)

def orthvec(basevec, othervec):
    amount = sum([othervec[ii] * basevec[ii] 
        for ii in range(len(basevec))])
    return vec_norm([othervec[ii] - basevec[ii] * amount 
        for ii in range(len(basevec))])

pagewidth = 8 * 72
pageheight = 10.5 * 72
offset = 72 / 4
def goodpic():
    veca, vecb = randvec(), randvec()
    vecb = orthvec(veca, vecb)
    lines = twodlines(veca, vecb)
    box = bbox(lines)
    scale = getscale(box, pagewidth, pageheight)
    minx, miny, _, _ = box
    offsetx, offsety = offset - minx * scale, offset - miny * scale
    return "%%!\n%d %d translate\n" % (offsetx, offsety) + pslines(lines, scale)

Reply via email to