-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathui.py
274 lines (236 loc) · 9 KB
/
ui.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
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
"""This file implements the UI, using the helper modules."""
import sys
import traceback
import command_parser
import sonifier
import sympy
from sympy.utilities.lambdify import lambdify, lambdastr
from sympy.parsing import sympy_parser
class Ui(command_parser.CommandParserBase):
prompt = "y = "
def __init__(self):
super().__init__()
self.min_x = 0
self.max_x = 10
self.min_y = 0
self.max_y = 10
self.duration = 5.0
self.hrtf = False
self.x_ticks = None
self.y_ticks = None
self.zero_ticks = False
self.x_symbol, self.y_symbol = sympy.symbols("x, y")
self.current_graph = None
self.debug = False
def parse(self, equation):
return sympy_parser.parse_expr(equation, transformations =
sympy_parser.standard_transformations + (sympy_parser.implicit_multiplication,
sympy_parser.function_exponentiation))
def make_graph(self, equation):
sym = self.parse(equation)
if len(sym.free_symbols) > 1 or (len(sym.free_symbols) == 1 and self.x_symbol not in sym.free_symbols):
symbols = set(sym.free_symbols)-{self.x_symbol}
print("It appears you have attempted to use the following additional constants and/or functions:")
print(" ".join((str(i) for i in sorted(list(symbols)))))
print("Expressions must only use the variable x.")
return
f = lambdify((self.x_symbol, ), sym)
return sonifier.Sonifier(f = f, duration = self.duration, min_x = self.min_x,
max_x = self.max_x, min_y = self.min_y, max_y = self.max_y,
hrtf = self.hrtf, x_ticks = self.x_ticks, y_ticks = self.y_ticks, zero_ticks = self.zero_ticks)
def do_default(self, argument):
try:
print("Graphing ", argument)
if self.current_graph is not None:
self.current_graph.shutdown()
self.current_graph = self.make_graph(argument)
if self.current_graph is None:
# We couldn't parse it.
return
self.current_graph.to_audio_device()
except Exception as e:
if self.debug:
exc_type, exc_value, exc_traceback = sys.exc_info()
traceback.print_exception(exc_type, exc_value, exc_traceback)
else:
print("I don't recognize your input")
def quit_hook(self):
if self.current_graph:
self.current_graph.shutdown()
def do_xrange(self, argument):
"""Set the range for x.
Syntax:
.xrange: Show the current range for x.
.xrange <min> <max>: configure us to evaluate the graph from x=min to x=max,.
Floating point arguments are allowed."""
if len(argument) == 0:
print("Range is {} <= x <= {}".format(self.min_x, self.max_x))
return
words = argument.split()
try:
min = float(words[0])
max = float(words[1])
except:
print("Couldn't parse. See .help xrange for syntax.")
return
if min >= max:
print("Error: min must be strictly less than max.")
return
self.min_x = min
self.max_x = max
def do_yrange(self, argument):
"""Set the range for y.
Syntax:
.yrange: Show the current range for y.
.yrange <min> <max>: configure us to show values of the function between y=min and y=max.
Floating point arguments are allowed."""
if len(argument) == 0:
print("Range is {} <= y <= {}".format(self.min_y, self.max_y))
return
words = argument.split()
try:
min = float(words[0])
max = float(words[1])
except:
print("Couldn't parse. See .help yrange for syntax.")
return
if min >= max:
print("Error: min must be strictly less than max.")
return
self.min_y = min
self.max_y = max
def do_duration(self, argument):
"""Set the duration of the graph.
syntax:
.duration: Show the duration.
.duration <seconds>: Set the duration."""
if len(argument) == 0:
print("Current duration:", self.duration)
return
try:
new_dur = float(argument)
except:
print("Couldn't parse duration. See .help duration for syntax.")
return
if new_dur <= 1.0:
print("Duration must be at least 1 second.")
return
self.duration = new_dur
def do_file(self, argument):
"""Graph an equation to a file.
syntax:
.file <name> <equation>: Graph equation to file name.
The file name must not contain spaces and must end in .wav or .ogg. It will be written to the current working directory."""
fname, sep, equation = argument.partition(" ")
if len(fname) == 0 or len(equation) == 0:
print("Invalid syntax. See .help file.")
graph = self.make_graph(equation)
graph.write_file(fname)
graph.shutdown()
def do_eval(self, argument):
"""Evaluate the argument with sympy and display.
syntax:
.eval <expression>: Evaluate the expression."""
if len(argument) == 0:
print("Invalid syntax. See .help eval for details.")
return
try:
sym = self.parse(argument)
sympy.simplify(sym)
except:
print("Couldn't parse expression.")
return
try:
print(sym.evalf())
except:
print(sym)
def do_xticks(self, argument):
"""Turn on/off ticks for the x axis.
syntax:
.xticks: Show if x ticks are on or off.
.xticks off: Turn x ticks off.
.xticks <number>: Tick when we cross a x value that is a multiple of <number>.
This command is equivalent to setting how axises are shown on a sighted graphing calculator."""
if argument == "off":
self.x_ticks = None
elif len(argument) == 0 and self.x_ticks is None:
print("x ticks are off.")
elif len(argument) == 0:
print("We are ticking when x is a multiple of", self.x_ticks)
else:
try:
x_ticks = float(argument)
except:
print("Invalid syntax. See .help xticks for details.")
return
self.x_ticks = x_ticks
def do_yticks(self, argument):
"""Turn on/off ticks for the y axis.
syntax:
.yticks: Show if y ticks are on or off.
.yticks off: Turn y ticks off.
.yticks <number>: Tick when we cross a y value that is a multiple of <number>.
This command is equivalent to setting how axises are shown on a sighted graphing calculator."""
if argument == "off":
self.y_ticks = None
elif len(argument) == 0 and self.y_ticks is None:
print("y ticks are off.")
elif len(argument) == 0:
print("We are ticking when y is a multiple of", self.y_ticks)
else:
try:
y_ticks = float(argument)
except:
print("Invalid syntax. See .help yticks for details.")
return
self.y_ticks = y_ticks
def do_0ticks(self, argument):
"""Turn on/off ticks for y = 0.
Syntax:
.0ticks: Show if 0 crossing ticks are on or off.
.0ticks on: set ticks on when y crosses 0.
.0ticks off: set ticks off when y crosses 0.
Finding 0 values can be very important in optimisation. This should help."""
if argument == "off":
self.zero_ticks = False
elif argument == "on":
self.zero_ticks = True
elif len(argument) == 0:
if self.zero_ticks == True: print("Zero crossing ticks are on.")
else: print("Zero crossing ticks are off.")
else:
print("Invalid syntax.")
def do_hrtf(self, argument):
"""Turn on/off HRTF.
Syntax:
.hrtf: Show if HRTF is on or off.
.hrtf on: Turn HRTF on.
.hrtf off: Turn HRTF off.
HRTF is currently experimental. The model is suboptimal."""
if argument == "off":
self.hrtf = False
elif argument == "on":
self.hrtf = True
elif len(argument) == 0:
if self.hrtf:
print("HRTF is on.")
else:
print("HRTF is off.")
else:
print("Invalid syntax.")
def do_debug(self, argument):
""" Enable debug mode.
Syntax:
.debug
.debug <on|off>
Synopsis: With no arguments, shows the status of debug mode.
with one argument of "on off" turns debug mode on or off.
If enabled, tracebacks from the program will be raised. If off, (default), the program will squelch all errors."""
argument = argument.lower()
if argument in {"off", "on"}:
self.debug = (True if argument == "on" else False)
print("Debug mode {}".format(argument))
elif argument:
print (".debug {} is not a valid use of .debug. Please type .help debug to receive help.".format(argument))
else:
print("Debug mode " + ("on" if self.debug else "off"))