#!/usr/bin/env python # coding: UTF-8 # ## @package _16_sierpinski # # Draws a Sierpinski Gasket. # # Usage: _16_sierpinski [number_of_divisions] # # @author Paulo Roma Cavalcanti # @since 09/02/2016 # @see http://paulbourke.net/fractals/gasket/ # @see https://en.wikipedia.org/wiki/Sierpinski_triangle # @see http://ecademy.agnesscott.edu/~lriddle/ifs/siertri/siertri.htm # @see http://www.oftenpaper.net/sierpinski.htm import sys try: from tkinter import * # python 3 except ImportError: from Tkinter import * # python 2 ## Creates a Sierpinski Gasket, by recursively partitioning # an initial triangle (a,b,c) into three or four new triangles. # # - There will be: # - @f$3^{count}@f$ red triangles or # - @f$4^{count}@f$ if the fourth triangle is drawn. # # - The number of white triangles is a Geometric Progression, starting at 1 and with ratio 3, given by: # - P(0) = 0 # - P(n) = 3 * P(n-1) + 1 # - P(n) = @f$\frac{(3^n-1)}{2}.@f$ # # @param a first vertex coordinates. # @param b second vertex coordinates. # @param c third vertex coordinates. # @param n number of subdivisions on each edge (depth of recursion). # @param fourth whether to add the fourth triangle. # def Sierpinski(a, b, c, n, fourth=False): ## list of points (a set of triangles). points = [] ## Pushes three vertices to the list of points. def triangle( a, b, c ): points.append ( a ) points.append ( b ) points.append ( c ) ## Returns a point on segment p1-p2 corresponding to parameter s. def mix(p1,p2,s): return [(p1[0]+p2[0])*s, (p1[1]+p2[1])*s] ## Divides triangle (a,b,c), by recursively subdividing # each edge at its middle point, and connecting the new points. def divideTriangle( a, b, c, count ): # check for end of recursion if ( count == 0 ): triangle( a, b, c ) else: # bisect the sides ab = mix( a, b, 0.5 ) ac = mix( a, c, 0.5 ) bc = mix( b, c, 0.5 ) count -= 1 # three new triangles divideTriangle( a, ab, ac, count ) divideTriangle( c, ac, bc, count ) divideTriangle( b, bc, ab, count ) # add the middle triangle if ( fourth ): divideTriangle( ac, bc, ab, count ) divideTriangle (a, b, c, n) return points ## Draw triangles, given in points, on canvas c. def draw(c, points, contour=False): w = c.winfo_width()//2 h = c.winfo_height()//2 centerx = w centery = h c.delete(ALL) for i in range(0,len(points),3): if ( not contour ): c.create_polygon(centerx+w*points[i] [0],centery-h*points[i ][1], centerx+w*points[i+1][0],centery-h*points[i+1][1], centerx+w*points[i+2][0],centery-h*points[i+2][1], fill='red', outline='black') else: c.create_line(centerx+w*points[i] [0],centery-h*points[i ][1], centerx+w*points[i+1][0],centery-h*points[i+1][1], centerx+w*points[i+2][0],centery-h*points[i+2][1], centerx+w*points[i] [0],centery-h*points[i ][1], fill='black', width=2) ## Main program. def main(argv=None): ## Resize the graphics when the window changes. def resize(event=None): global pts draw(canvas, pts, cntVar.get()=='ON') ## Redraw the graphics when the scale changes def redraw(event=None): global pts # Initialize the corners of the gasket with three points. vertices = [ [ -1, -1 ], [ 0, 1 ], [ 1, -1 ] ] pg = lambda n: (3**n-1)//2 ndiv = slider.get() pts = Sierpinski( vertices[0], vertices[1], vertices[2], ndiv, fillVar.get()=='ON' ) lr.configure(text="Red Triangles = %d "%(len(pts)//3)) la.configure(text=" White Triangles = %d"%(pg(ndiv))) resize(event) ndiv = 2 if argv is None: argv = sys.argv if len(argv) > 1: try: ndiv = abs(int(argv[1])) except: ndiv = 3 root = Tk() root.title("Sierpinski Gasket") bot = Frame(root) bot.pack(side=BOTTOM) top = Frame(root) top.pack(side=TOP) canvas = Canvas(root,width=400,height=400); canvas.bind("", resize) canvas.pack(expand=YES,fill=BOTH) slider = Scale(bot, from_=0, to=8, command=redraw, orient=HORIZONTAL) slider.set(ndiv) slider.pack(side=LEFT, anchor = W) cntVar = StringVar() # create a checkbutton for the drawing style cntVar.set ( "OFF" ) c = Checkbutton (bot, text="Contour", variable=cntVar, onvalue="ON", offvalue="OFF", command=resize) c.pack(side=BOTTOM) fillVar = StringVar() # create another checkbutton for the number of triangles fillVar.set ( "OFF" ) c = Checkbutton (bot, text="Fill", variable=fillVar, onvalue="ON", offvalue="OFF", command=redraw) c.pack(side=LEFT) lr = Label(top, text="") lr.pack(side=LEFT,anchor = W) la = Label(top, text="") la.pack(side=RIGHT,anchor = E) redraw(None) mainloop() if __name__=='__main__': sys.exit(main())