-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhelp.py
More file actions
306 lines (261 loc) · 12.4 KB
/
help.py
File metadata and controls
306 lines (261 loc) · 12.4 KB
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
from fractions import Fraction
import numpy as np
import matplotlib.pyplot as plt
import settings
def inputInt(prompt):
"""
Ask the user to input an integer
"""
while True:
try:
num = int(input(prompt))
break
except ValueError:
print('Error: Please input a valid number')
return num
def inputStr(prompt):
"""
Ask the user to input a string
"""
while True:
try:
num = str(input(prompt))
break
except ValueError:
print('Error: Please input a valid string')
return num
def inputFloat(prompt):
"""
Ask the user to input a float
"""
while True:
try:
num = float(input(prompt))
break
except ValueError:
print('Error: Please input a valid number')
return num
def inputFraction(prompt):
"""
Ask the user to input a fraction or float
"""
while True:
try:
userinput = input(prompt)
num = float(Fraction(userinput))
break
except:
try:
num = float(userinput)
break
except ValueError:
print('Error: Please input a valid number (a simple fraction or float)')
return num
def displayMenu(options, message):
"""
Makes a menu of items that can be selected for.
Input: the options of a menu, as well as the message to be displayed afterwards.
Output: the menu in teh terminal, returns the choice of the user
"""
print('\n') # for prettier format
for i in range(len(options)):
print("{:d}. {:s}".format(i+1, options[i]))
choice = 0
while not(np.any(choice == np.arange(len(options))+1)):
choice = inputInt(message)
return choice
def selfDefinedSystem():
"""
User input to define their own system. Changes settings file.
The user is first asked to input the alphabet of their system.
Then the value they want the line segments to be scaled by after each iteration.
Then the startcondition of the system (axiom).
For each letter of their alphabet, they must then specify the function of that letter, unless it is [ or ], where the function is assumed to be save position and load position respectively.
Also, the specific angle to turn must be specified, if the letter is chosen to represent an angle.
Input: user input for the self defined L-system
Output: none
"""
letteroptions = np.array(['A length', 'An angle', 'Do nothing'])
while True:
alphabet = input('\nInput the alphabet of the system(without spaces), make sure you have no duplicates, and only uppercase letters (or [ or ] for saving/loading position): ')
allowed_chars = np.array(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', ']'])
if np.all([char in allowed_chars for char in alphabet]) != True:
print('\nOnly uppercase letters and [ or ] are allowed')
elif np.any([alphabet[i] in alphabet[:i] + alphabet[i+1:] for i in range(len(alphabet))]):
print('\nDuplicates are not allowed')
else:
break
settings.iteration_scaling = inputFraction('\nInput what value you want the length of segments in the system to be scaled by after each iteration: ')
settings.lettermapping = np.zeros((4,len(alphabet)), dtype = object) # for storing the alphabet(0) along with replacement rules(1), turtle grahping rules(2), and turtle grahping action(3) (length or angle)
while True:
settings.selfdefined_start = input('\nInput the startcondition of the system: ')
# startconditions need to be a part of the alphabet
if all(letters in alphabet for letters in settings.selfdefined_start):
break
else:
print('\nYour startcondition needs to be a part of the alphabet')
for i in range(len(alphabet)):
settings.lettermapping[0,i] = alphabet[i]
# To be able to use [ and ]
if alphabet[i] == '[':
settings.lettermapping[1,i] = '['
settings.lettermapping[2,i] = 'save'
settings.lettermapping[3,i] = 'save'
continue
elif alphabet[i] == ']':
settings.lettermapping[1,i] = ']'
settings.lettermapping[2,i] = 'load'
settings.lettermapping[3,i] = 'load'
continue
while True:
replacement = inputStr('\nWhat should ' + alphabet[i] + ' be replaced with, after each iteration?\n')
if all(letters in alphabet for letters in replacement):
settings.lettermapping[1,i] = replacement
break
else:
print('\nAll letters of the replacement need to be part of your alphabet')
option = displayMenu(letteroptions, 'What should ' + alphabet[i] + ' represent? ')
if option == 1: # A length
settings.lettermapping[2,i] = 'l' # placeholder
settings.lettermapping[3,i] = 'length'
elif option == 2: # An angle
settings.lettermapping[2,i] = np.pi * inputFraction('\nWrite what value you want x to be, for an angle x*Pi. Positive values denote positive rotation. ')
settings.lettermapping[3,i] = 'angle'
elif option == 3: # do nothing
settings.lettermapping[2,i] = 'nothing'
settings.lettermapping[3,i] = 'nothing'
while True:
settings.name = input('\nWhat do you want to name your system?\n')
if np.any(settings.name == settings.SystemsList):
print('\nName cannot be the same as a predefined system (Sierpinski, Koch or User defined)')
else:
break
return
def complexTurtlePlot(turtleCommands):
"""
A function to plot the fractal using a turtle, more complex than the standard turtlePlot function to accompany user defined systems.
This function is implemented to be able to produce complex plots based on turtlecommands that don't neccesarily alternate between angle and length.
Input: turtleCommands: A row vector consisting of alternating length and angle specifications. Doesn't have to be alternating if the system loaded isn't Sierpinski or Koch
Output: Screen output of the plot
"""
saved_positions = []
saved_angles = []
x = np.array([[0,0]])
d = np.array([1,0])
plt.subplots(1, 1, figsize = (15,15))
for i in range(np.size(turtleCommands)):
if settings.turtleAction[i] == 'length':
x = np.vstack((x, x[-1] + turtleCommands[i] * d))
elif turtleCommands[i] == 'save':
# save position
saved_positions.append(x[-1])
saved_angles.append(d)
elif turtleCommands[i] == 'load':
# load position
plt.plot(x[:,0],x[:,1], linewidth = 0.5)
x = np.array([saved_positions[-1]])
d = saved_angles[-1]
saved_angles = saved_angles[:-1]
saved_positions = saved_positions[:-1]
elif turtleCommands[i] == 'nothing':
pass
else:
darray = np.array([[np.cos(turtleCommands[i]), -np.sin(turtleCommands[i])], [np.sin(turtleCommands[i]), np.cos(turtleCommands[i])]])
d = darray.dot(d)
plt.plot(x[:,0],x[:,1], linewidth = 0.5)
plt.axis('equal')
if settings.System == 'User defined':
plt.title(settings.name + ' system with ' + str(settings.N) + ' iterations')
else:
plt.title(settings.System + ' system with ' + str(settings.N) + ' iterations')
plt.show()
def loadUserdefined(turtleCommands, LindenMayerstring):
"""
Loads the user defined system.
Based on the variables in settings, makes turtlecommands and turtleactions.
"""
l = settings.iteration_scaling**settings.N
settings.turtleAction = np.zeros(np.size(turtleCommands), dtype = object)
for i in range(len(LindenMayerstring)):
command = settings.lettermapping[2][LindenMayerstring[i] == settings.lettermapping[0]] # vectorization is cool
if command == 'l':
turtleCommands[i] = l # We have to have this here, we cannot move it to turtlePlot because of the project specifications
settings.turtleAction[i] = 'length'
elif command == 'save':
turtleCommands[i] = 'save'
settings.turtleAction[i] = 'save'
elif command == 'load':
turtleCommands[i] = 'load'
settings.turtleAction[i] = 'load'
elif command == 'nothing':
turtleCommands[i] = 'nothing'
settings.turtleAction[i] = 'nothing'
else:
turtleCommands[i] = float(command[0])
settings.turtleAction[i] = 'other'
return turtleCommands
def factoryReset():
"""
For loading predefined systems into systems.dat using pickle, instead of loading each manually using the interface.
"""
import pickle
from datastorage import system
# Create empty array
predefined_systems = []
# Koch Snowflake
settings.System = 'User defined'
settings.N = 2
settings.name = 'Koch snowflake'
settings.selfdefined_start = 'FLLFLLF'
settings.iteration_scaling = 1/3
settings.lettermapping = np.array([['F','L','R'],['FRFLLFRF','L', 'R'],['l', 1/3*np.pi, -1/3*np.pi],['length','other','other']])
current_system = system(settings.name, settings.lettermapping, settings.selfdefined_start, settings.iteration_scaling) # create system based on system class
predefined_systems.append(current_system)
# Right angled Koch curve
settings.System = 'User defined'
settings.N = 2
settings.name = 'Right angled Koch curve'
settings.selfdefined_start = 'F'
settings.iteration_scaling = 1/3
settings.lettermapping = np.array([['F','L','R'],['FLFRFRFLF','L', 'R'],['l', 1/2*np.pi, -1/2*np.pi],['length','other','other']])
current_system = system(settings.name, settings.lettermapping, settings.selfdefined_start, settings.iteration_scaling) # create system based on system class
predefined_systems.append(current_system)
# Dragon curve
settings.System = 'User defined'
settings.N = 2
settings.name = 'Dragon curve'
settings.selfdefined_start = 'F'
settings.iteration_scaling = 1/2
settings.lettermapping = np.array([['F', 'G', 'L','R'],['FLG','FRG','L', 'R'],['l', 'l', 1/2*np.pi, -1/2*np.pi],['length','length','other','other']])
current_system = system(settings.name, settings.lettermapping, settings.selfdefined_start, settings.iteration_scaling) # create system based on system class
predefined_systems.append(current_system)
# Levy curve
settings.System = 'User defined'
settings.N = 2
settings.name = 'Levy curve'
settings.selfdefined_start = 'F'
settings.iteration_scaling = 1/2
settings.lettermapping = np.array([['F','L','R'],['LFRRFL','L', 'R'],['l', 1/4*np.pi, -1/4*np.pi],['length','other','other']])
current_system = system(settings.name, settings.lettermapping, settings.selfdefined_start, settings.iteration_scaling) # create system based on system class
predefined_systems.append(current_system)
# Fractal tree
settings.System = 'User defined'
settings.N = 2
settings.name = 'Fractal tree'
settings.selfdefined_start = 'X'
settings.iteration_scaling = 1/2
settings.lettermapping = np.array([['X', 'F', 'L', 'R', '[', ']'],['FL[[X]RX]RF[RFX]LX','FF','L', 'R', '[', ']'],['nothing', 'l', 25/180*np.pi, -25/180*np.pi, 'save', 'load'],['other','length','other','other', 'save', 'load']])
current_system = system(settings.name, settings.lettermapping, settings.selfdefined_start, settings.iteration_scaling) # create system based on system class
predefined_systems.append(current_system)
# Fractal bush
settings.System = 'User defined'
settings.N = 2
settings.name = 'Fractal bush'
settings.selfdefined_start = 'F'
settings.iteration_scaling = 1
settings.lettermapping = np.array([[ 'F', 'L', 'R', '[', ']'],['FFl[LFRFRF]R[RFLFLF]','L', 'R', '[', ']'],['l', 45/360*np.pi, -45/360*np.pi, 'save', 'load'],['length','other','other', 'save', 'load']])
current_system = system(settings.name, settings.lettermapping, settings.selfdefined_start, settings.iteration_scaling) # create system based on system class
predefined_systems.append(current_system)
with open('systems.dat', 'wb') as systemsfile:
for s in predefined_systems:
pickle.dump(s, systemsfile)