Penrose Tiling

Posted by Mark M on Feb 23, 2008



This is a short script to generate the non-periodic penrose tiling. As mentioned under the L-system posting a while back this can be produced by means of a well-known l-system recipe. The L-system works beautifully but it is very difficult to see how it works. This script uses a contraction method of starting with one half of one of the tiles and recursively breaking it down into smaller units. One of the goals was to show the intense relationship between the tiling and the golden ratio and also to see if it could be done using nothing except Nodebox's transformations. Enjoy:

from math import sqrt
stroke(.9)
strokewidth(.35)
translate(-9, 466)
transform(CORNER)
colormode(HSB)
 
### Golden Ratio
phi = 1.61803399
sqrt2_phi = sqrt(2 + phi)
 
class thinRhomb():
    def __init__(self,l, type, draw=True):
        self.length = l
        self.type = type
        if draw:
            self.draw()
    def draw(self):
        fill(self.type/3.0+.25, .3, self.type/3.0+.5)
        l = self.length
        beginpath(0,0)
        lineto(sqrt2_phi/2 * l, l/(2*phi))
        lineto(sqrt2_phi*l,0)
        lineto(sqrt2_phi/2 * l, -l/(2*phi))
        return endpath()
    def contract(self, minLength, rule=None):
        L = self.length
        if L/phi < minLength:
            self.draw()
            return
        if self.type == 0:
            ## scale with negative number==reflection
            scale(x=1, y=-1)
        # One Thin Rhomb becomes one thick via rotation and one thin via rotation/translation
        push()
        rotate(-18)
        Rhomb = thickRhomb(L/phi, 0,  draw=False)
        Rhomb.contract(minLength)
        pop()
        push()
        translate(0,L/phi)
        rotate(108)
        translate(0,L)
        Rhomb = thinRhomb(L/phi, 1,  draw=False)
        Rhomb.contract(minLength)
        pop()
        
class thickRhomb():
    def __init__(self, l, type, draw=True):
        self.length = l
        self.type = type
        if draw:
            self.draw()
    def draw(self):
        fill((self.type+.6)/3.0, .25, (self.type+.3)/2.0)
        l = self.length
        beginpath(0,0)
        lineto(phi/2*l, -(phi-1)* sqrt2_phi/2*l)
        lineto(phi*l,0)
        lineto(phi/2*l, (phi-1)* sqrt2_phi/2*l)
        return endpath()
    def contract(self, minLength, rule=None):
        L = self.length
        if L/phi < minLength:
            self.draw()
            return
        if self.type == 1:
            scale(-1,1)
            translate(-L*phi,0)
        # One Thick Rhomb becomes one thick via translation and on thick via rotation/translation
        # one thin via translation/rotation
        push()
        translate(L/phi, 0)
        Rh = thickRhomb(L/phi,1, draw=False)
        Rh.contract(minLength)
        pop()
        push()
        rotate(216)
        translate(-L, -0)
        Rh = thickRhomb(L/phi, 0, draw=False)
        Rh.contract(minLength)
        pop()
        push()
        translate(L/phi, 0)
        rotate(54)
        Rh = thinRhomb(L/phi, 0,  draw=False)
        Rh.contract(minLength)
        pop()
        
 
initial_Length = 600
 
R = thickRhomb(initial_Length, 0, draw=False)
min_side_length = 20
R.contract(min_side_length)


 
Posted by Zach on Mar 21, 2008

Awesome!!



Posted by Leandro on Nov 29, 2008

Great, thank you! it's beautiful!



Add comment