-
Notifications
You must be signed in to change notification settings - Fork 0
/
l-turtles.py
150 lines (114 loc) · 4.34 KB
/
l-turtles.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import turtle
def l_system(V, w, P, n):
"""Generates an L-system run for n rounds.
They are defined as
G = (V, w, P)
V = The alphabet (tuple, not actually used, can be specified as None)
w = The start (string)
P = The production rules (dictionary for replacement)
"""
# Make sure all production rules are in alphabet
if V:
assert(all(key in V for key in P))
current = w
for i in range(n):
current = [P[x] if x in P else x for x in list(current)]
current = ''.join(current)
return current
def run_turtle(var, start, rules, iters, angle, startdir=0):
"""Var, start, rules and iters, correspond to (V, w, P, n) of the
l-system function. The distance moved is scaled down from size.
The turtle starts facing startdir.
Instructions are defined as the following:
F, G: Draw forward
M, N: Move forward (don't draw)
[, ]: Push and pop angle and location
+, -: Turn left and right by angle degrees
Variables not described can be used as constants.
"""
# Initialization
terry = turtle.Turtle()
turtle.mode("world") # Coordinate system
terry.pensize(1)
terry.pencolor("blue")
terry.speed(0) # Instant speed
turtle.tracer(0, 0) # Don't draw anything yet (could change in future)
turtle.setup(width=900, height=900, startx=None, starty=None) # Square pixels
terry.hideturtle()
dist = 1
positions = []
angles = []
bounds = [0, 0, 0, 0] # llx, lly, urx, ury
instructions = l_system(var, start, rules, iters)
print("First 50 instructions:\n", instructions[:50])
def update_bounds(bounds):
coords = terry.position()
bounds[0] = min(bounds[0], coords[0])
bounds[1] = min(bounds[1], coords[1])
bounds[2] = max(bounds[2], coords[0])
bounds[3] = max(bounds[3], coords[1])
# Run turtle
terry.left(startdir) # Starting direction
for instr in instructions:
if instr in ('F', 'G'):
terry.forward(dist)
update_bounds(bounds)
elif instr in ('M', 'N'):
terry.penup()
terry.forward(dist)
terry.pendown()
update_bounds(bounds)
elif instr == '[':
positions.append(terry.pos())
angles.append(terry.heading())
elif instr == ']':
terry.penup()
terry.goto(positions.pop())
terry.setheading(angles.pop())
terry.pendown()
elif instr == '+':
terry.left(angle)
elif instr == '-':
terry.right(angle)
llx, lly, urx, ury = bounds
width = urx - llx
height = ury - lly
if width > height:
y_center = (ury + lly)/2
ury = y_center + width/2
lly = y_center - width/2
else:
x_center = (urx + llx)/2
urx = x_center + height/2
llx = x_center - height/2
print("Bounds:", bounds)
turtle.setworldcoordinates(llx, lly, urx, ury) # Redraw
turtle.update() # Draw everything
turtle.exitonclick()
def right_koch(iters):
run_turtle(('F',), 'F', {'F':'F+F-F-F+F'}, iters, 90)
def dragon_curve(iters):
run_turtle(('X', 'Y'), 'FX', {'X':'X+YF', 'Y':'FX-Y'}, iters, 90)
def sierpinski(iters):
run_turtle(('F', 'G'), 'F', {'F':'G-F-G', 'G':'F+G+F'}, iters, 60)
def plant_1(iters):
run_turtle(('F', 'G'), 'F', {'G':'GG', 'F':'G[+F]-F'}, iters, 45, startdir=90)
def plant_2(iters):
run_turtle(('X', 'F'), 'X', {'X':'F-[[X]+X]+F[+FX]-X', 'F':'FF'},
iters=iters, angle=360-25, startdir=70)
def hilbert_curve(iters):
run_turtle(('A', 'B'), 'A', {'A':'-BF+AFA+FB-', 'B':'+AF-BFB-FA+'},
iters=iters, angle=90)
def koch_island(iters):
run_turtle(('F',), 'F-F-F-F', {'F':'F+FF-FF-F-F+F+FF-F-F+F+FF+FF-F'},
iters=iters, angle=90)
def square_koch(iters):
run_turtle(('F',), 'F-F-F-F', {'F':'FF-F-F-F-FF'}, iters, 90)
def plant_3(iters):
run_turtle(('F', 'X'), 'X', {'X':'F[+X]F[-X]+X', 'F':'FF'}, iters,
20, startdir=90)
def koch_burst(iters):
# Own design
run_turtle(('F'), 'F++F++F++F++F', {'F':'F+F--FF++F-F'}, iters, 72,
startdir=180)
koch_burst(4)