# hilbert.py # Walker M. White (wmw2) # Based on an module write by Dexter Kozen # October 14, 2015 """Module to draw cool shapes with the TkTurtle.""" import tkturtle import math #################### Hilbert Curves #################### def hilbert(w, side, d, sp=0): """Draws a Hilbert curve of side-length side and depth d on Window w This function clears the window and makes a new turtle t. This starts at the bottom left corner of the Hilbert curve, where the curve is centered at (0,0). It draws by calling the function hilbert_helper(t, side, d). The turtle is visible during drawing and is hidden at the end. Parameter w: The window to draw upon. Precondition: w is a tkturtle Window object. Parameter side: The width and height of the (square) Hilbert curve Precondition: side is a number > 0 Parameter d: The recursive depth of the Hilbert curve Precondition: n is a valid depth (int >= 0) Parameter sp: The turtle speed. Precondition: sp is a valid turtle speed.""" assert type(w) == tkturtle.Window, str(w)+' is not a window object' assert type(side) in [int, float] and side > 0, str(side)+' is not a valid length' assert type(d) == int and d >= 0, str(n)+' is not a valid depth' assert type(sp) == int and 0 <= sp <= 10, str(sp)+' is not a valid Turtle speed' # Create the turtle w.clear() t = tkturtle.Turtle(w) # Set the color and speed of the turtle t.color = 'magenta' t.speed = sp # Get the turtle in position w/o drawing t.move(-side/2.0,-side/2.0) t.right(90) # Draw the Hilbert curve hilbert_helper(t, side, d); t.visible = False # Hide the turtle at the end def hilbert_helper(t, side, d, reverse=False): """Draws a Hilbert curve of side-length side and depth d with Turtle t The curve is draw right to left if reverse is True. Otherwise, it is drawn left to right. This feature is necessary to smoothly draw the recursive Hilbert curves. If drawing in reverse, the Turtle starts on the right side. Otherwise, it start on the left. Parameter t: The drawing Turtle Precondition: t is a Turtle with drawmode True. Parameter side: The width and height of the (square) Hilbert curve Precondition: side is a number > 0 Parameter d: The recursive depth of the Hilbert curve Precondition: n is a valid depth (int >= 0) Parameter reverse: Whether or not to draw in reverse Precondition: reverse is a bool""" # WE WILL NOT WORRY ABOUT ENFORCING PRECONDITIONS IN THE HELPER # Turning directions depend on whether or not we are in reverse if reverse: angle = -90 else: angle = 90 # BASE CASE: d = 0 if d == 0: t.forward(side) t.right(angle) t.forward(side) t.right(angle) t.forward(side) return # RECURSIVE CASE: d > 0 # Compute what the grid looks like dotsize = (2**(d+1))-1 # Number of dots in grid subsize = (2**d)-1 # Number of dots for each sub-hilbert # Compute the new edge sizes. factor = (subsize)/float(dotsize) leng = side*factor edge = side-2*leng # Recursively draw the curve. t.right(angle) hilbert_helper(t,leng,d-1,not reverse) t.right(angle) t.forward(edge) hilbert_helper(t,leng,d-1,reverse) t.left(angle) t.forward(edge) t.left(angle) hilbert_helper(t,leng,d-1,reverse) t.forward(edge) t.right(angle) hilbert_helper(t,leng,d-1,not reverse) t.right(angle) ################ Sierpinski Carpet ################# # This is a fun additional example to play with. def carpet(w, side, d): """Draws a blue Sierpinski carpet with side length and depth d This function clears the window and makes a new graphics pen p. This pen starts in the middle of the canvas at (0,0). It draws by calling the function carpet(p, 0, 0, side, d). The pen is NOT visible during drawing and should remain hidden at the end. The pen should have both a 'blue' fill color and a 'blue' line color. Parameter w: The window to draw upon. Precondition: w is a tkturtle Window object. Parameter side: The side length of the t-square Precondition: side is a valid side length (number >= 0) Parameter d: The recursive depth of the t-square Precondition: n is a valid depth (int >= 0)""" assert type(w) == tkturtle.Window, str(w)+' is not a window object' assert type(side) in [int, float] and side > 0, str(side)+' is not a valid length' assert type(d) == int and d >= 0, str(n)+' is not a valid depth' w.clear() p = tkturtle.Pen(w) # initially at (0,0) p.visible = False p.fillcolor = 'blue' p.pencolor = 'blue' # Draw the initial solid square at (0,0) fill_rect(p,0,0,side,side) # Fill in white squares p.fillcolor = 'white' carpet_helper(p, 0, 0, side, d); def carpet_helper(p, x, y, side, d): """Fills in white squares for a carpet of depth d centered at (x,y) This is one way to draw a Seirpinski shape; draw a basic shape and then draw white squares as a way to "erase". However, DO NOT use this approach in Assignment 4. It will not help you. That assignment is meant to be done without erasing. The carpet is draw with the current pen color and visibility attribute. Parameter p: The graphics pen Precondition: p is a Pen with fill attribute False. Parameter x: The x-coordinate of the carpet center Precondition: x is a number Parameter y: The y-coordinate of the carpet center Precondition: y is a number Parameter side: The side-length of the Sierpinski carpet Precondition: side is a valid side length (number >= 0) Parameter d: The recursive depth of the carpet Precondition: d is a valid depth (int >= 0)""" # WE WILL NOT WORRY ABOUT ENFORCING PRECONDITIONS IN THE HELPER if d == 0: return # Fill in the center squares fill_rect(p, x, y, round(side/3.0), round(side/3.0)) # Round prevents artifacts # Fill in the 8 squares with d-1-level carpets # They are -1, 1 0, 1 1, 1 # -1, 0 1, 0 # -1,-1 0,-1 1,-1 sides = [-1, 0, 1] for a in sides: for b in sides: if not (a==0 and b ==0): carpet_helper(p, x+a*side/3.0, y+b*side/3.0, side/3.0, d-1); # Useful helper function def fill_rect(p, x, y, side, hght): """Fill a rectangle of lengths side, hght with center (x, y) using pen p. Parameter p: The graphics pen Precondition: p is a Pen with fill attribute False. Parameter x: The x-coordinate of the rectangle center Precondition: x is a number Parameter y: The y-coordinate of the rectangle center Precondition: y is a number Parameter side: The width of the rectangle Precondition: side is a valid side length (number >= 0) Parameter hght: The height of the rectangle Precondition: hght is a valid side length (number >= 0)""" # WE WILL NOT WORRY ABOUT ENFORCING PRECONDITIONS IN THE HELPER # Move to the center and draw p.move(x - side/2.0, y - hght/2.0) p.fill = True p.drawLine( 0, hght) p.drawLine( side, 0) p.drawLine( 0, -hght) p.drawLine(-side, 0) p.fill = False p.move(x - side/2.0, y - hght/2.0) ################ Test Function ################# def main(): """Run each of the functions, pausing for a key press at each step""" w = tkturtle.Window() print 'Calling Hilbert n=3' #hilbert(w, 300, 3) raw_input('Hit ') print 'Calling Hilbert n=4' #hilbert(w, 300, 4) raw_input('Hit ') print 'Calling Sierpinski n=3' carpet(w, 300, 3) raw_input('Hit ') print 'Calling Sierpinski n=4' carpet(w, 300, 4) raw_input('Hit ') # Application code if __name__ == '__main__': main()