-
Notifications
You must be signed in to change notification settings - Fork 0
/
painter.py
350 lines (297 loc) · 11.2 KB
/
painter.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
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
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
from tokens import TokenType, FUNC
import math
import matplotlib.pyplot as plt
# origin_x = 0
# origin_y = 0
# scale_x = 1.5
# scale_y = 0.75
# rot_rad = 1
# 背景颜色只能在开始时设置一次
# 颜色都用元组表示
bg_color = (0.9608, 0.9608, 0.8627)
dot_color = (1, 0.6471, 0)
# 根据start, end, step 生成元组
def GenerateT(fr, to, step):
return tuple(range(int(fr), int(to) + 1, int(step)))
# 调整rgb值为matplotlib支持的格式 0-1
def CacuColor(color):
return tuple(c / 255.0 for c in color)
# 实现具体函数的计算
def CacuFunc(content, func):
# content 是元组时
if isinstance(content, tuple):
if func == FUNC.SIN:
sin_values = tuple(math.sin(rad) for rad in content)
return sin_values
elif func == FUNC.COS:
cos_values = tuple(math.cos(rad) for rad in content)
return cos_values
elif func == FUNC.TAN:
tan_values = tuple(math.tan(rad) for rad in content)
return tan_values
elif func == FUNC.SQRT:
sqrt_values = tuple(math.sqrt(value) for value in content)
return sqrt_values
elif func == FUNC.EXP:
exp_values = tuple(math.exp(value) for value in content)
return exp_values
elif func == FUNC.LN:
ln_values = tuple(math.log(value) for value in content)
return ln_values
# content 不是元组时
else:
if func == FUNC.SIN:
return math.sin(content)
elif func == FUNC.COS:
return math.cos(content)
elif func == FUNC.TAN:
return math.tan(content)
elif func == FUNC.SQRT:
return math.sqrt(content)
elif func == FUNC.EXP:
return math.exp(content)
elif func == FUNC.LN:
return math.log(content)
# 计算语法树
def CacuSyntaxTree(root, temp=None):
# 处理const和temp外都可能存在递归调用
if root is None:
print('Syntax tree is empty')
# const 直接返回值
if root.OpCode == TokenType.CONST_ID:
if root.Content == "PI":
return 3.1415926
if root.Content == "E":
return 2.71828
else:
return float(root.Content)
# T 返回变量(元组类型)
elif root.OpCode == TokenType.T:
return temp
# func 返回函数计算后的结果
elif root.OpCode == TokenType.FUNC:
# content1 必须是值或者元组
content1 = CacuSyntaxTree(root.Content[1], temp)
return CacuFunc(content1, root.Content[0])
elif root.OpCode == TokenType.PLUS:
# 先计算好 content0 1
content0 = CacuSyntaxTree(root.Content[0], temp)
content1 = CacuSyntaxTree(root.Content[1], temp)
# 对于content不同情况需要采取不同的计算方法
if isinstance(content0, tuple) and isinstance(content1, tuple):
return tuple(x + y for x, y in zip(content0, content1))
elif isinstance(content0, tuple) and isinstance(content1, tuple) is False:
return tuple(x + content1 for x in content0)
elif isinstance(content0, tuple) is False and isinstance(content1, tuple):
return tuple(content0 + y for y in content1)
else:
return content0 + content1
elif root.OpCode == TokenType.MINUS:
content0 = CacuSyntaxTree(root.Content[0], temp)
content1 = CacuSyntaxTree(root.Content[1], temp)
if isinstance(content0, tuple) and isinstance(content1, tuple):
return tuple(x - y for x, y in zip(content0, content1))
elif isinstance(content0, tuple) and isinstance(content1, tuple) is False:
return tuple(x - content1 for x in content0)
elif isinstance(content0, tuple) is False and isinstance(content1, tuple):
return tuple(content0 - y for y in content1)
else:
return content0 - content1
elif root.OpCode == TokenType.MUL:
content0 = CacuSyntaxTree(root.Content[0], temp)
content1 = CacuSyntaxTree(root.Content[1], temp)
if isinstance(content0, tuple) and isinstance(content1, tuple):
return tuple(x * y for x, y in zip(content0, content1))
elif isinstance(content0, tuple) and isinstance(content1, tuple) is False:
return tuple(x * content1 for x in content0)
elif isinstance(content0, tuple) is False and isinstance(content1, tuple):
return tuple(content0 * y for y in content1)
else:
return content0 * content1
elif root.OpCode == TokenType.DIV:
content0 = CacuSyntaxTree(root.Content[0], temp)
content1 = CacuSyntaxTree(root.Content[1], temp)
if isinstance(content0, tuple) and isinstance(content1, tuple):
return tuple(x / y for x, y in zip(content0, content1))
elif isinstance(content0, tuple) and isinstance(content1, tuple) is False:
return tuple(x / content1 for x in content0)
elif isinstance(content0, tuple) is False and isinstance(content1, tuple):
return tuple(content0 / y for y in content1)
else:
return content0 / content1
elif root.OpCode == TokenType.POWER:
content0 = CacuSyntaxTree(root.Content[0], temp)
content1 = CacuSyntaxTree(root.Content[1], temp)
if isinstance(content0, tuple) and isinstance(content1, tuple):
return tuple(x ** y for x, y in zip(content0, content1))
elif isinstance(content0, tuple) and isinstance(content1, tuple) is False:
return tuple(x ** content1 for x in content0)
elif isinstance(content0, tuple) is False and isinstance(content1, tuple):
return tuple(content0 ** y for y in content1)
else:
return content0 ** content1
# test CacuSyntaxTree
# root = MakeExprNode(TokenType.FUNC, FUNC.SQRT,
# MakeExprNode(TokenType.PLUS,
# MakeExprNode(TokenType.CONST_ID, 15),
# MakeExprNode(TokenType.T))
# )
# print(CacuSyntaxTree(root))
# 根据绘图语言中设置的参数调整x,y的坐标
def Modify(x_data: tuple, y_data: tuple, origin_x, origin_y, scale_x, scale_y, rot_rad):
x_origin = tuple(x + origin_x for x in x_data)
y_origin = tuple(y + origin_y for y in y_data)
x_scale = tuple(x * scale_x for x in x_origin)
y_scale = tuple(y * scale_y for y in y_origin)
x_rot = tuple(x * math.cos(rot_rad) + y * math.sin(rot_rad) for x, y in zip(x_scale, y_scale))
y_rot = tuple(y * math.cos(rot_rad) - x * math.sin(rot_rad) for x, y in zip(x_scale, y_scale))
return x_rot, y_rot
# main
'''
fig, ax = plt.subplots()
draw ...
plt.show()
'''
# 画出所有点后暂停给定的时间
def drawNodes(ax, x_data, y_data, pause_time, color):
# 使用 plt.scatter() 绘制点
plt.scatter(x_data, y_data, color=color, marker='o')
# 暂停给定时间
plt.pause(pause_time)
# 设置坐标轴范围并隐藏坐标轴
def setAx(x_min, x_max, y_min, y_max, ax):
# 反转y轴方向
ax.invert_yaxis()
# 设置坐标轴范围,保证不会缩小原先的坐标轴范围
# 使得非delete的情况下能展示出所有的绘图结果
xmin, xmax, ymin, ymax = plt.axis()
if xmin < x_min:
x_min = xmin
if xmax > x_max:
x_max = xmax
if ymin < y_min:
y_min = ymin
if ymax > y_max:
y_max = ymax
t = (x_min, x_max, y_min, y_max)
ax_min = min(t)
ax_max = max(t)
ax.set_xlim(ax_min, ax_max)
ax.set_ylim(ax_max, ax_min)
# 隐藏坐标轴
ax.axis('off')
# 不删除的单循环绘图
def singleLoopNoDelete(ax, x_data, y_data, color):
# 获得X,Y轴的范围
x_max = max(x_data) + 2
x_min = min(x_data) - 2
y_max = max(y_data) + 2
y_min = min(y_data) - 2
# 设置坐标轴
setAx(x_min, x_max, y_min, y_max, ax)
# 使用 plt.scatter() 绘制点
plt.scatter(x_data, y_data, color=color, marker='o')
# 暂停观看效果
plt.pause(2)
# 删除效果的单循环绘图
def singleLoopDelete(ax, x_data, y_data, color, title=None):
# 获得X,Y轴的范围
x_max = max(x_data) + 2
x_min = min(x_data) - 2
y_max = max(y_data) + 2
y_min = min(y_data) - 2
# 设置坐标轴
setAx(x_min, x_max, y_min, y_max, ax)
# 使用 plt.scatter() 绘制点
plt.scatter(x_data, y_data, color=color, marker='o')
plt.pause(2)
pause_time = 3.0 / len(x_data)
length = len(x_data)
for i in range(length):
# 准备好当前帧需要绘制的点,即少画一个点
x_data.pop()
y_data.pop()
# 绘制当前帧
drawNodes(ax, x_data, y_data, pause_time, color)
# 清空当前帧
ax.cla()
# 设置题目
plt.title(title)
# 设置坐标轴
setAx(x_min, x_max, y_min, y_max, ax)
# 无delete的双重循环绘图
def doubleLoopNoDelete(ax, x_move, y_move, x_data, y_data, color):
# 获得X,Y轴的范围
x_max = max(x_data) + max(x_move) + 2
x_min = min(x_data) - min(x_move) - 2
y_max = max(y_data) + max(y_move) + 2
y_min = min(y_data) + min(y_move) - 2
setAx(x_min, x_max, y_min, y_max, ax)
pause_time = 3.0 / len(x_move)
length = len(x_move)
# 双重循环绘图即每次都将初始x,y坐标移动一定偏移量
for i in range(length):
x_draw = [x + x_move[i] for x in x_data]
y_draw = [y + y_move[i] for y in y_data]
drawNodes(ax, x_draw, y_draw, pause_time, color)
plt.pause(2)
# delete效果的双重循环绘图
def doubleLoopDelete(ax, x_move, y_move, x_data, y_data, color, title=None):
# 获得X,Y轴的范围
x_max = max(x_data) + max(x_move) + 2
x_min = min(x_data) - min(x_move) - 2
y_max = max(y_data) + max(y_move) + 2
y_min = min(y_data) + min(y_move) - 2
setAx(x_min, x_max, y_min, y_max, ax)
length = len(x_move)
x_all = []
y_all = []
# 获得要绘制的所有点
for i in range(length):
x_draw = [x + x_move[i] for x in x_data]
y_draw = [y + y_move[i] for y in y_data]
x_all.extend(x_draw)
y_all.extend(y_draw)
# 绘制所有点
plt.scatter(x_all, y_all, color=color, marker='o')
plt.pause(2)
pause_time = 3.0 / len(x_all)
length = len(x_all)
# 每次弹出一个点
for i in range(length):
x_all.pop()
y_all.pop()
drawNodes(ax, x_all, y_all, pause_time, color)
ax.cla()
plt.title(title)
setAx(x_min, x_max, y_min, y_max, ax)
# 测试painter功能
def test():
# 正式使用的时候需要将元组转化成列表
x = []
y = []
x2 = []
y2 = []
fig, ax = plt.subplots()
# 设置背景颜色
fig.set_facecolor(bg_color)
for i in range(100):
x.append(i)
y.append(i)
for i in range(5):
x2.append(i * 10)
y2.append((5 - i) * 10)
singleLoopNoDelete(ax, x, y, dot_color)
singleLoopDelete(ax, x, y, dot_color)
for i in range(100):
x.append(i)
y.append(i)
# doubleLoopNoDelete(ax, x2, y2, x, y, color)
doubleLoopDelete(ax, x2, y2, x, y, dot_color)
for i in range(100):
x.append(i)
y.append(i)
singleLoopNoDelete(ax, x, y, dot_color)
plt.show()
if __name__ == "__main__":
test()