# This example draws what is called the Sierpinski triangle. # First, one black triangle is created. # After that it is removed and in its place three smaller black triangles are created, # which leaves a white hole (also shaped like a triangle, but upside down!) in the middle. # # This can continue for any number of steps - you can split the smaller # triangles into even smaller ones and so on. # We have, however, limited it to 8 to keep our computers from freezing due to # too much calculations! If you change it to a higher number, that will probably happen. # # This code is similar to the code in "Koch snowflake" example so you # could first go through that to understand this example better. import pippy, pygame, sys from pygame.locals import * from math import sin, cos from math import pi as Pi import gtk class Triangle(object): def __init__(self, first_vertex, length, displacement_angle = 0 ): # remember your first vertex self.A = first_vertex # calculate the other two self.B = self.A[0] + length * cos(Pi/3 + displacement_angle), \ self.A[1] - length * sin(Pi/3 + displacement_angle) self.C = self.A[0] + length * cos(displacement_angle), \ self.A[1] - length * sin(displacement_angle) # remember your length self.length = length # calculate the midpoints of each line # m1 for AB, m2 for BC, m3 for CA # m1 and m3 are calculated the same way as points B and C, but with # half the length. self.m1 = self.A[0] + length/2 * cos(Pi/3 + displacement_angle), \ self.A[1] - length/2 * sin(Pi/3 + displacement_angle) self.m3 = self.A[0] + length/2 * cos(displacement_angle), \ self.A[1] - length/2 * sin(displacement_angle) # m2 is 120 degrees (2*Pi/3) from C, half the length. # ... but we don't actually need it for anything. self.m2 = self.C[0] + length/2 * cos(2*Pi/3 + displacement_angle), \ self.C[1] - length/2 * sin(2*Pi/3 + displacement_angle) # create three new triangles from yourself. def split(self): new_triangles = [] new_triangles.append(Triangle(self.A, self.length/2)) new_triangles.append(Triangle(self.m1, self.length/2)) new_triangles.append(Triangle(self.m3, self.length/2)) return new_triangles # This is how a triangle draws itself. def draw(self, screen, color): points = [self.A, self.B, self.C] pygame.draw.polygon(screen, color, points) # always need to init first thing before drawing pygame.init() # XO screen is 1200x900 size = width, height = gtk.gdk.screen_width(), gtk.gdk.screen_height() # create the window and keep track of the surface # for drawing into screen = pygame.display.set_mode(size) # The font we'll use to display the current depth. font_size = 36 font = pygame.font.Font(None, font_size) black = (0, 0, 0) white = (255, 255, 255) # Practice: Could you make this depend on the current resolution? # (hint: variables "height" and "width" could help) starting_point = (200, 750) side_length = 800 t1 = Triangle(starting_point, side_length) depth = 0 all_triangles = [t1] new_triangles = [] recalculate = False while pippy.pygame.next_frame(): for event in pygame.event.get(): if event.type == QUIT: sys.exit() # When R arrow is pressed, go one step further. # This means splitting the existing triangle(s) into new ones. # Note that all the triangles have to be recalculated before redrawn. elif event.type == KEYDOWN and event.key == K_RIGHT and depth < 8: depth += 1 recalculate = True # When L arrow is pressed, go one step back, reducing the number of # triangles. # Note that all the triangles have to be recalculated before redrawn. elif event.type == KEYDOWN and event.key == K_LEFT and depth > 0: depth -= 1 recalculate = True elif event.type == KEYDOWN and event.key == K_q: sys.exit() screen.fill(white) # Display the current step. msg = "Step: " + str(depth) + "/8" text = font.render(msg , True, black) text_box = text.get_rect() text_box.top = height / 10 text_box.left = width / 20 # Display the instructions text2 = font.render("Use left and right arrows; q to quit", True, black) text_box2 = text2.get_rect() text_box2.top = height / 10 - 30 text_box2.left = width / 20 # Write the instructions and the current step on the screen. screen.blit(text, text_box) screen.blit(text2, text_box2) # If the depth was changed (L or R pressed), recalculate everything so we can # draw the change. if recalculate == True: # Delete the existing triangles. all_triangles = [t1] new_triangles = [] # Keep splitting until the new value of "depth" variable is reached. # (it can be one more (R key) or one less (L key) from the last value) for step in range(depth): for triangle in all_triangles: new_triangles += triangle.split() all_triangles = new_triangles new_triangles = [] recalculate = False # Draw the triangle on the screen. for triangle in all_triangles: triangle.draw(screen, black) # Refresh the screen. pygame.display.flip()