Tuesday, March 22, 2016

Drawing Triangles the Hard Way

I'm a big fan of fractals, and dedicated a whole chapter of Hacking Math Class with Python to them. The Sierpinski Triangle (or "Sieve") is a famous shape that pops up in the most unexpected ways.


In my book I showed the easy way to draw one by making a turtle do it, but there's a little more complicated way that involves another famous triangle. It was known to the Chinese and Indians while we Europeans were still hiding from the Vikings, but we like to call it Pascal's Triangle after a more recent French mathematician.


The numbers can be generated by adding the numbers in the row above, but using a computer it's quicker to use the combination formula.



Here's the (Python 2) code I copied and pasted from dheerosaur on Stack Overflow:

import operator as op
...
def ncr(n, r):
    r = min(r, n-r)
    if r == 0: return 1
    numer = reduce(op.mul, xrange(n, n-r, -1))
    denom = reduce(op.mul, xrange(1, r+1))
    return numer//denom

(To convert it to Python 3, just change "xrange" to "range" and add "from itertools import reduce" to the imports.)

The 1st row and column are really the 0th, so the "20" in row 7 is actually row 6, column 3:

>>> ncr(6,3)
20

How could you possibly get The Sierpinski Sieve from Pascal's Triangle? Let's use Pygame.

I use a template I found at Professor Craven's excellent Pygame tutorial. This sets up the screen and the colors and the display loop. All I have to do is draw shapes; it even updates the display and quits for me. I have to create a function to draw a triangle where I want it and what size I want it. The number (calculated with ncr above) will determine whether it's filled with color or not:

def triangle(x,y,size,filled):
    '''draw an equilateral triangle'''
    #if the number isn't a multiple of 2, color it in.
    if filled % 2 != 0: 
        pygame.draw.polygon(screen,VIOLET,[[x,y],
                                       [x-size/2.,y+1.732*size/2.],
                                       [x+size/2.,y+1.732*size/2.]],
                                        0)
    else: thick = 0 #leave multiples of 2 empty

The "gasket" function will draw the triangles at the right spots:

def gasket(rows):
    size = 400./rows #resize depending on the number of rows
    for i in range(rows):
        for j in range(i+1):
            y = 1.732*i*size/2. #height of equilateral triangle
            x = -i*size/2. + j * size + WIDTH/2. #width of triangle
            triangle(x,y,size,ncr(i,j)) #draw the triangle


Now running "gasket(50)" will get you 50 rows of Pascal's triangle, with the multiples of 2 left empty:



Look familiar? You can change one digit in the "triangle" function to leave out the multiples of 3 instead:


Or the multiples of 8. The pattern of empty triangles changes every time. Try other numbers!


This blog entry has been brought to you by O'Reilly books.:-)

2 comments:

  1. does not work for me python error is trown because WIDITH is not incialized.

    How to fix it?

    ---------------------------------------------------------------------------
    NameError Traceback (most recent call last)
    in ()
    ----> 1 gasket(50)

    in gasket(rows)
    14 for j in range(i+1):
    15 y = 1.732*i*size/2. #height of equilateral triangle
    ---> 16 x = -i*size/2. + j * size + WIDTH/2. #width of triangle
    17 triangle(x,y,size,ncr(i,j)) #draw the triangle

    NameError: name 'WIDTH' is not defined

    ReplyDelete
  2. If I put some value for WIDITH there is another error for pygame

    gasket(50)
    ---------------------------------------------------------------------------
    NameError Traceback (most recent call last)
    in ()
    ----> 1 gasket(50)

    in gasket(rows)
    16 y = 1.732*i*size/2. #height of equilateral triangle
    17 x = -i*size/2. + j * size + WIDTH/2. #width of triangle
    ---> 18 triangle(x,y,size,ncr(i,j)) #draw the triangle

    in triangle(x, y, size, filled)
    3 #if the number isn't a multiple of 2, color it in.
    4 if filled % 2 != 0:
    ----> 5 pygame.draw.polygon(screen,VIOLET,[[x,y],
    6 [x-size/2.,y+1.732*size/2.],
    7 [x+size/2.,y+1.732*size/2.]],

    NameError: name 'pygame' is not defined

    ReplyDelete