Weekend Nerd Diversions

, None

by · Posted in: nerdiness

Math and visual representations of mathematical ideas is something of a hobby for me. One of my favorite tools for this is Nodebox, which is a Mac OS X application that gives you a basic canvas and some simple python classes to make it easy to create vector images. To make a Nodebox rendering you simply write a python script. The possibilities are limited only by your imagination and programming skill. The following script was inspired by the book Indra's Pearls: The Vision of Felix Klein by David Mumford, Caroline Series, and David Wright. It's a gorgeous book investigating limit sets of discrete groups on the complex plane. Because python has excellent support for complex numbers, it is a natural choice for this sort of work. If you have some skills in scripting or programming, the pseudo-code throughout the books provides some useful guidance in recreating many of the graphic images in the book.

The following is a simple python script that generates the above image.

from cmath import *
background(.96, .98, 1)

class cCircle():
    '''A simple circle on the complex plane'''
    def __init__(self, center, radius, draw=True):
        self.center = center
        self.radius = radius
        if draw:
            self.draw()
    def draw(self):
        oval(self.center.real-self.radius, self.center.imag-self.radius, self.radius*2, self.radius*2)

def marker(point, stroke=(.2), strokewidth=(.25), fill=None):
    return oval(point[0]-2, point[1]-2, 4, 4, stroke=stroke, strokewidth=strokewidth, fill=fill)

def cmarker(z, stroke=(.2), strokewidth=(.25), fill=None):
    return marker((z.real, z.imag), stroke=stroke, strokewidth=strokewidth, fill=fill)

def cpolyline(Z, draw=True):
    beginpath(Z[0].real, Z[0].imag)
    for z in Z[1:]:
        lineto(z.real, z.imag)
    return endpath(draw=draw)

def mobius_on_point(t, z):
    return (t[0] * z +t[1])/(t[2]*z + t[3])

def mobius_on_circle(t, c):
    '''t should be 1x4 array, c is a cCirlce'''
    z = c.center -(c.radius**2/(t[3]/t[2] + c.center).conjugate())
    center = mobius_on_point(t, z)
    radius = abs(center - mobius_on_point(t, c.center+c.radius))
    return cCircle(center, radius)

nofill()
transform(CORNER)
translate(408, 410)
scale(95)
strokewidth(0.0015)    
stroke(.1)
fill(.98,.99,.9, .035)
colormode(HSB)

a = complex(1., -5.)
b = 0.04
c = 0.39
d = complex(0.99, -5.)

cent = complex(1.16,0.91)

C = cCircle(cent,1.)
t = [a, b, c, d]

C2 = cCircle(-cent, 1)
t2 = [a, -b, -c, d]

for i in range(600):
    C = mobius_on_circle(t, C)
    C2 = mobius_on_circle(t2, C2)