目录
引言
正多边形是指二维平面内各边相等,各角也相等的多边形。这些平面图形往往给人平衡之美。本文将简单介绍一种绘制正多边形的python方法。
正文
§1 绘画部分
§1.1 核心内容
要想绘制一个正多边形,首先要知道它的边数nos(number of side)、半径r(radius)和边长ls(lenth of side)。由于在实际绘画场景中,绘图器使用者往往会自定义边数、半径或边长,所以这三个量应当成为本程序函数里的自变量。
经过研究,这三者存在内部联系,其中以边长和半径之间联系最为紧密,在这两个量之间存在函数关系,而因为半径r比边长ls更能在几何上给人直观的大小关系和空间感,所以选取半径r作为自变量,边长ls作为因变量。
一般地,在同一个正多边形中,边长ls和半径r存在如下函数关系,其中边数nos作为参数参与计算:
ls与r的函数关系式:
而这个函数关系式用python表达式表示出来就是:
ls = m.sin(m.radians(180 / nos)) * 2 * r
#标识字母m标识了math库
(介于此公式推导较为简单,就不作赘述)
§1.2 基本程序
基本程序功能应包含指定边数的正多边形绘制,在此基础上,可增设交互环节、绘画样板和可调节参数(如画笔颜色、画笔速度、画布颜色等)等。笔者在这里提供基础功能函数、交互环节、绘画样板和可调节参数选项,后期也可以根据需要安排基础功能函数,绘制更精美的图形。
(a)基础功能函数
基础功能函数:
def _init_(angle, radius, pencolor = 'black', bgcolor = 'white', pensize = 25, speed = 0): an = angle; r = radius t.hideturtle() t.pencolor(pencolor) t.bgcolor(bgcolor) t.pensize(pensize) t.speed(speed) t.pu() t.goto(0, r) t.seth(180) t.left(an / 2) t.pd() def get_side_lenth(radius, number_of_side): nos = number_of_side; r = radius ls = m.sin(m.radians(180 / nos)) * 2 * r return ls def get_exterior_angle(nos): an = 360 / nos return an def custom_regular_polygon(number_of_side, lenth_of_side, angle, changer = changer): nos = number_of_side; ls = lenth_of_side; an = angle for i in range(nos): t.forward(ls) t.left(an) print(f'正{nos}边形绘画完毕。\nDone.') #标识字母指向:t -> turtle; m -> math
其中,包含了几个重要变量:
序号 | 变量名 | 表示意义 |
1 | nos; number_of_side | 正多边形边数 |
2 | ls; lenth_of_side | 正多边形边长 |
3 | r; radius | 正多边形半径 |
4 | an; angle | 正多边形外角度数 |
拆分来看,这四个基础函数的功能指向明确:
序号 | 函数名 | 参数及其默认值 |
1 | _init_ | angle, radius, pencolor = 'black', bgcolor = 'white', pensize = 25, speed = 0 |
2 | get_side_lenth | radius, number_of_side |
3 | get_exterior_angle | nos |
4 | custom_regular_polygon | number_of_side, lenth_of_side, angle, changer = changer |
注:custom_regular_polygon函数参数changer是将阿拉伯数字替换为汉字的字典,仅为了增强输出时文字的美观性,不是必须的。
函数名 | 功能 | 返回值 |
_init_ | 初始化turtle,做好画图准备 | 无 |
get_side_lenth | 通过给定的半径长度计算边长 | 边长ls |
get_exterior_angle | 通过给定的半径长度计算外角度数 | 外角度数an |
custom_regular_polygon | 在_init_函数运行的基础上,运用get_side_lenth函数、get_exterior_angle函数的返回值绘制指定边数的正多边形。 | 无 |
运行程序时,应将四者先后一并使用:
def custom_mode(nos, radius, pensize, speed, pencolor, bgcolor):
ls = get_side_lenth(radius, nos) #计算边长
an = get_exterior_angle(nos) #计算外角度数
_init_(radius = radius, angle = an, pencolor = pencolor, bgcolor = bgcolor, pensize = pensize, speed = speed) #初始化turtle画笔、画布
custom_regular_polygon(nos, ls, an) #绘制图形
可以像笔者一样将其统一为函数。
(b)样板模式
在基础功能函数的基础上,可以设计几个绘制样板作为绘制实例,提供给使用者以便快速得到图形。
笔者准备了从正三角形到正十边形,外加正一百边形共9个普通样板:
class classic:
#初始化turtle画笔和画布,这里给定了除了angle和radius外的诸多参数的默认值(初始化不需要nos参与,故未提及)
def _init_(angle, radius, pencolor = 'black', bgcolor = 'white', pensize = 2, speed = 0):
an = angle; r = radius
t.hideturtle()
t.pencolor(pencolor)
t.bgcolor(bgcolor)
t.pensize(pensize)
t.speed(speed)
t.pu()
t.goto(0, r)
t.seth(180)
t.left(an / 2)
t.pd()
#计算边长
def get_side_lenth(radius, number_of_side):
nos = number_of_side; r = radius
ls = m.sin(m.radians(180 / nos)) * 2 * r
return ls
#计算外角度数
def get_exterior_angle(nos):
an = 360 / nos
return an
#正多边形样板
def regular_triangle(ls, an):
for i in range(3):
t.forward(ls)
t.left(an)
print('正三角形(等边三角形)绘画完毕。\nDone.')
def square(ls, an):
for i in range(4):
t.forward(ls)
t.left(an)
print('正四边形(正方形)绘画完毕。\nDone.')
def regular_pentagon(ls, an):
for i in range(5):
t.forward(ls)
t.left(an)
print('正五边形绘画完毕。\nDone.')
def regular_hexagon(ls, an):
for i in range(6):
t.forward(ls)
t.left(an)
print('正六边形绘画完毕。\nDone.')
def regular_heptagon(ls, an):
for i in range(7):
t.forward(ls)
t.left(an)
print('正七边形绘画完毕。\nDone.')
def regular_octagon(ls, an):
for i in range(8):
t.forward(ls)
t.left(an)
print('正八边形绘画完毕。\nDone.')
def regular_nonagon(ls, an):
for i in range(9):
t.forward(ls)
t.left(an)
print('正九边形绘画完毕。\nDone.')
def regular_decagon(ls, an):
for i in range(10):
t.forward(ls)
t.left(an)
print('正十边形绘画完毕。\nDone.')
def regular_100_polygon(ls, an):
for i in range(100):
t.forward(ls)
t.left(an)
print('正一百边形绘画完毕。\nDone.')
§2 其他程序设计
一个较为完善的交互式、提供计算服务和绘图功能的程序不仅要有计算核心(算法等)、绘图功能(基础功能函数、样板函数等),还要有通俗强大的交互能力、一定的容错能力和排错功能。所以,笔者在此写下自己的交互设计和容错、排错设计。
§2.1 交互
本程序中交互的主要作用就是获取使用者提供的绘图需求,笔者将交互设计为函数。
def inter():
choice = input('请选择绘画模式:\n(1)样板模式\n(2)自定义模式\n\nPlease select drawing mode:\n(1)default mode\n(2)DIY mode\n\n>>[')
if '1' in choice:
nos = input('''在此模式(样板模式)下,您可以选择预先设置好的正多边形绘图作为参考案例,下面是菜单:
In this mode (default mode), you can select the pre-set regular polygon drawing as a reference case. The following is the menu:
(1)正三角形(等边三角形)//regular triangle\n(2)正四边形(正方形)//square\n(3)正五边形//regular pentagon
(4)正六边形//regular hexagon\n(5)正七边形//regular heptagon\n(6)正八边形//regular octagon
(7)正九边形//regular nonagon\n(8)正十边形//regular decagon\n(9)正一百边形//regular polygon of 100 sides
请填写边数。\nPlease input the number of sides.\n>>[''').strip()
try:
nos = int(nos)
except:
print('请正确输入边数。\nPlease input the number correctly.')
sys.exit()
sample_mode(nos)
else:
nos = eval(input('请输入正多边形边数\nnumber of sides\n>>['))
choice = input(f'边数nos:{nos}\n半径r:200\n画笔pen:5,black\n画布paper:white\n速度v:0\n请问是否需要更改参数?(Y/N)>>[').strip().upper()
if 'Y' in choice:
nos = eval(input('请输入正多边形边数\nnumber of sides\n>>['))
radius = eval(input('请输入半径长度\nradius\n>>['))
pensize = eval(input('请输入画笔粗细\npen size\n>>['))
speed = int(input('请输入画笔速度\npen speed\n>>['))
pencolor = input('请输入画笔颜色\npen colour\n>>[').strip().lower()
bgcolor = input('请输入画布颜色\nbackground colour\n>>[').strip().lower()
else:
radius = 200; pensize = 5; speed = 0; pencolor = 'black'; bgcolor = 'white'
custom_mode(nos, radius, pensize, speed, pencolor, bgcolor)
注:sample_mode函数就是样板功能的集合函数,custom_mode函数就是基础功能函数。
在主干分支上也有交互:
if __name__ == '__main__':
choice = input('欢迎使用正多边形绘图器,使用主功能请输入1,查看绘图范例请输入2。\n>>[')
if '1' in choice:
draw.inter()
else:
print('\n\n下面是调用示例:')
for i in range(8):
draw.example(10 - i, 200 - i * 20, 4, 10, 'white', 'gold')
注:draw.example函数是另一个样板,组合了多个正多边形。
§2.2 容错和排错设计
笔者为程序设计的排错功能主要在于排除不规范输入对程序流程的干扰。
如下:
def _error_raiser_(nos, radius, pensize, speed, pencolor, bgcolor):
if type(nos) != int:
print('下一次请输入一个整数。\nPlease input an integer next time.')
sys.exit()
if nos < 3:
print('下一次请输入一个大于2的整数。\nPlease input an integer bigger than two next time.')
sys.exit()
if type(radius) != int:
if type(radius) != float:
print('下一次请输入一个数值作为半径。\nPlease input a number as the radius next time.')
sys.exit()
if type(pensize) != int:
if type(radius) != float:
print('下一次请输入一个数值作为画笔粗细。\nPlease input a number as the pen size next time.')
sys.exit()
if type(speed) != int:
print('下一次请输入一个整数值作为画笔速度。\nPlease input an integer as the pen speed next time.')
sys.exit()
if type(pencolor) != str:
print('下一次请输入一个有效字符串作为画笔颜色。\nPlease input a useful number as the pen color next time.')
sys.exit()
if type(bgcolor) != str:
print('下一次请输入一个有效字符串作为画布颜色。\nPlease input a useful number as the background color next time.')
sys.exit()
#若半径为0或负数,则使用默认值180。
if radius <= 0:
radius = 180
if pensize <= 0:
pensize = 5
if speed < 0:
speed = 0
if pencolor == 'default':
pencolor = 'black'
if bgcolor == 'default':
bgcolor = 'white'
return radius, pensize, speed, pencolor, bgcolor
注:输入种类甚繁,请诸位过目,笔者不作赘述。
PS:虽然名字叫“error_raiser”,但笔者选择直接用sys.exit(),当然实操时也可以换为raise Exception()。
笔者还在小规模输入处安插了如try...except...等排错设置。
至于容错能力,其实一般般,没有怎么用心设计,主要在于多分支结构强大的“顺流而下”能力。
§3 完整程序代码
本程序的完整程序代码如下所示。
import turtle as t
import random as ran
import math as m
import sys
import time as tm
class classic:
def _init_(angle, radius, pencolor = 'black', bgcolor = 'white', pensize = 2, speed = 0):
an = angle; r = radius
t.hideturtle()
t.pencolor(pencolor)
t.bgcolor(bgcolor)
t.pensize(pensize)
t.speed(speed)
t.pu()
t.goto(0, r)
t.seth(180)
t.left(an / 2)
t.pd()
def get_side_lenth(radius, number_of_side):
nos = number_of_side; r = radius
ls = m.sin(m.radians(180 / nos)) * 2 * r
return ls
def get_exterior_angle(nos):
an = 360 / nos
return an
def regular_triangle(ls, an):
for i in range(3):
t.forward(ls)
t.left(an)
print('正三角形(等边三角形)绘画完毕。\nDone.')
def square(ls, an):
for i in range(4):
t.forward(ls)
t.left(an)
print('正四边形(正方形)绘画完毕。\nDone.')
def regular_pentagon(ls, an):
for i in range(5):
t.forward(ls)
t.left(an)
print('正五边形绘画完毕。\nDone.')
def regular_hexagon(ls, an):
for i in range(6):
t.forward(ls)
t.left(an)
print('正六边形绘画完毕。\nDone.')
def regular_heptagon(ls, an):
for i in range(7):
t.forward(ls)
t.left(an)
print('正七边形绘画完毕。\nDone.')
def regular_octagon(ls, an):
for i in range(8):
t.forward(ls)
t.left(an)
print('正八边形绘画完毕。\nDone.')
def regular_nonagon(ls, an):
for i in range(9):
t.forward(ls)
t.left(an)
print('正九边形绘画完毕。\nDone.')
def regular_decagon(ls, an):
for i in range(10):
t.forward(ls)
t.left(an)
print('正十边形绘画完毕。\nDone.')
def regular_100_polygon(ls, an):
for i in range(100):
t.forward(ls)
t.left(an)
print('正一百边形绘画完毕。\nDone.')
class custom:
changer = {3 : '三' , 4 : '四' , 5 : '五' , 6 : '六' , 7 : '七' ,
8 : '八' , 9 : '九' , 10 : '十' , 100 : '一百'}
def _error_raiser_(nos, radius, pensize, speed, pencolor, bgcolor):
if type(nos) != int:
print('下一次请输入一个整数。\nPlease input an integer next time.')
sys.exit()
if nos < 3:
print('下一次请输入一个大于2的整数。\nPlease input an integer bigger than two next time.')
sys.exit()
if type(radius) != int:
if type(radius) != float:
print('下一次请输入一个数值作为半径。\nPlease input a number as the radius next time.')
sys.exit()
if type(pensize) != int:
if type(radius) != float:
print('下一次请输入一个数值作为画笔粗细。\nPlease input a number as the pen size next time.')
sys.exit()
if type(speed) != int:
print('下一次请输入一个整数值作为画笔速度。\nPlease input an integer as the pen speed next time.')
sys.exit()
if type(pencolor) != str:
print('下一次请输入一个有效字符串作为画笔颜色。\nPlease input a useful number as the pen color next time.')
sys.exit()
if type(bgcolor) != str:
print('下一次请输入一个有效字符串作为画布颜色。\nPlease input a useful number as the background color next time.')
sys.exit()
#若半径为0或负数,则使用默认值180。
if radius <= 0:
radius = 180
if pensize <= 0:
pensize = 5
if speed < 0:
speed = 0
if pencolor == 'default':
pencolor = 'black'
if bgcolor == 'default':
bgcolor = 'white'
return radius, pensize, speed, pencolor, bgcolor
def _init_(angle, radius, pencolor = 'black', bgcolor = 'white', pensize = 25, speed = 0):
an = angle; r = radius
t.hideturtle()
t.pencolor(pencolor)
t.bgcolor(bgcolor)
t.pensize(pensize)
t.speed(speed)
t.pu()
t.goto(0, r)
t.seth(180)
t.left(an / 2)
t.pd()
def get_side_lenth(radius, number_of_side):
nos = number_of_side; r = radius
ls = m.sin(m.radians(180 / nos)) * 2 * r
return ls
def get_exterior_angle(nos):
an = 360 / nos
return an
def custom_regular_polygon(number_of_side, lenth_of_side, angle, changer = changer):
nos = number_of_side; ls = lenth_of_side; an = angle
for i in range(nos):
t.forward(ls)
t.left(an)
print(f'正{nos}边形绘画完毕。\nDone.')
class draw:
class main_:
def sample_mode(nos):
if nos not in [3,4,5,6,7,8,9,10,100]:
print('请检查您的输入。此边数不在样板之内。\nPlease check your inputs.It\'s not in samples.')
sys.exit()
ls = classic.get_side_lenth(180, nos)
an = classic.get_exterior_angle(nos)
classic._init_(angle = an, radius = 180)
if nos == 3:
classic.regular_triangle(ls, an)
elif nos == 4:
classic.square(ls, an)
elif nos == 5:
classic.regular_pentagon(ls, an)
elif nos == 6:
classic.regular_hexagon(ls, an)
elif nos == 7:
classic.regular_heptagon(ls, an)
elif nos == 8:
classic.regular_octagon(ls, an)
elif nos == 9:
classic.regular_nonagon(ls, an)
elif nos == 10:
classic.regular_decagon(ls, an)
else:
classic.regular_100_polygon(ls, an)
def custom_mode(nos, radius, pensize, speed, pencolor, bgcolor):
radius, pensize, speed, pencolor, bgcolor = custom._error_raiser_(nos, radius, pensize, speed, pencolor, bgcolor)
ls = custom.get_side_lenth(radius, nos)
an = custom.get_exterior_angle(nos)
custom._init_(radius = radius, angle = an, pencolor = pencolor, bgcolor = bgcolor, pensize = pensize, speed = speed)
custom.custom_regular_polygon(nos, ls, an)
def example(nos, radius, pensize, speed, pencolor, bgcolor):
custom._error_raiser_(nos, radius, pensize, speed, pencolor, bgcolor)
ls = custom.get_side_lenth(radius, nos)
an = custom.get_exterior_angle(nos)
custom._init_(radius = radius, angle = an, pencolor = pencolor, bgcolor = bgcolor, pensize = pensize, speed = speed)
custom.custom_regular_polygon(nos, ls, an)
def inter():
choice = input('请选择绘画模式:\n(1)样板模式\n(2)自定义模式\n\nPlease select drawing mode:\n(1)default mode\n(2)DIY mode\n\n>>[')
if '1' in choice:
nos = input('''在此模式(样板模式)下,您可以选择预先设置好的正多边形绘图作为参考案例,下面是菜单:
In this mode (default mode), you can select the pre-set regular polygon drawing as a reference case. The following is the menu:
(1)正三角形(等边三角形)//regular triangle\n(2)正四边形(正方形)//square\n(3)正五边形//regular pentagon
(4)正六边形//regular hexagon\n(5)正七边形//regular heptagon\n(6)正八边形//regular octagon
(7)正九边形//regular nonagon\n(8)正十边形//regular decagon\n(9)正一百边形//regular polygon of 100 sides
请填写边数。\nPlease input the number of sides.\n>>[''').strip()
try:
nos = int(nos)
except:
print('请正确输入边数。\nPlease input the number correctly.')
sys.exit()
draw.main_.sample_mode(nos)
else:
nos = eval(input('请输入正多边形边数\nnumber of sides\n>>['))
choice = input(f'边数nos:{nos}\n半径r:200\n画笔pen:5,black\n画布paper:white\n速度v:0\n请问是否需要更改参数?(Y/N)>>[').strip().upper()
if 'Y' in choice:
nos = eval(input('请输入正多边形边数\nnumber of sides\n>>['))
radius = eval(input('请输入半径长度\nradius\n>>['))
pensize = eval(input('请输入画笔粗细\npen size\n>>['))
speed = int(input('请输入画笔速度\npen speed\n>>['))
pencolor = input('请输入画笔颜色\npen colour\n>>[').strip().lower()
bgcolor = input('请输入画布颜色\nbackground colour\n>>[').strip().lower()
else:
radius = 200; pensize = 5; speed = 0; pencolor = 'black'; bgcolor = 'white'
draw.main_.custom_mode(nos, radius, pensize, speed, pencolor, bgcolor)
if __name__ == '__main__':
choice = input('欢迎使用正多边形绘图器,使用主功能请输入1,查看绘图范例请输入2。\n>>[')
if '1' in choice:
draw.inter()
else:
print('\n\n下面是调用示例:')
for i in range(8):
draw.example(10 - i, 200 - i * 20, 4, 10, 'white', 'gold')
t.exitonclick()
#正多边形画图器 V3.5
#创建日期 3 Oct, 2022
#Author starlight_2007
总结一下,流程图如下 :
(程序导出的原图水印很烦人,只能先用截屏)
后记
本程序功能简单,结构明晰,笔者也一步步分析了设计理念。
希望读者遇见新的知识!
PS:题外话,说真的,要是闲的没事,这套流程还可以再接一个数据库,各个报错端口都能接,还可以再多加几个,记录访客等。要是喜欢,自己分析一下数据也很好玩。不过这些并不适合用在这样一个功能性极强且简单的程序上。