Python基础图形绘图教学3
- 绘画蜡烛
- 绘画椭圆
- 绘画圆柱
- 绘画波浪线
- 绘画正多边形
- 绘画奇数正多边形内邻点连线图形
- 绘画正多边形内邻点连线图形
- 部分图案组合图
- 结束语
绘画蜡烛
先从之前的蛋糕案例中搞几个已经写好的图形。
根据蜡烛底部的中心坐标、蜡烛的高度宽度绘制蜡烛,以蜡烛的宽度作为烛焰的直径:
def draw_candle(location, width, height):
"""
画蜡烛。
:param location: 蜡烛底部中心坐标
:param width: 蜡烛宽度
:param height: 蜡烛高度
"""
color = turtle.color()
angle = turtle.heading()
turtle.penup()
turtle.color('skyblue')
turtle.setheading(0)
turtle.goto(*location)
turtle.pendown()
turtle.begin_fill()
turtle.forward(width / 2)
turtle.setheading(90)
turtle.forward(height)
turtle.setheading(180)
turtle.forward(width)
turtle.setheading(270)
turtle.forward(height)
turtle.setheading(0)
turtle.forward(width / 2)
turtle.end_fill()
turtle.penup()
turtle.setheading(90)
turtle.forward(height)
turtle.setheading(20)
turtle.color('red')
turtle.pendown()
turtle.begin_fill()
turtle.circle(width / 2, 180)
turtle.setheading(20)
turtle.circle(-width / 4, 180)
turtle.setheading(200)
turtle.circle(width / 4, 180)
turtle.end_fill()
turtle.penup()
turtle.color(*color)
turtle.goto(*location)
turtle.setheading(angle)
这个还是很简单,一个长方形和一个半太极就完成了。
绘画椭圆
这里就是通过椭圆中心坐标进行绘图的,比从边开始绘图方便了点,易于管理:
def draw_oval(location, width, height, fill=True, color='#000000'):
"""
画椭圆。
:param location: 椭圆中心坐标
:param width: 椭圆长轴(横向)
:param height: 椭圆短轴(纵向)
:param fill: 是否填充
:param color: 椭圆颜色
"""
assert width >= height, ValueError('目前只能长轴是 x 轴,即 width >= height (width == height 时是圆)!')
a = width / 2
b = height / 2
color_old = turtle.color()
angle = turtle.heading()
turtle.color(color)
turtle.penup()
turtle.goto(*location)
turtle.setheading(0)
turtle.forward(width / 2)
turtle.setheading(90)
turtle.pendown()
if fill:
turtle.begin_fill()
if width < 180: # 通过椭圆直角坐标方程计算描点绘画
for x in range(width // 2, width // -2, -1): # 以 y = location[1] 的单位上半部分描点
y = location[1] + (((a ** 2 - x ** 2) * b ** 2) / a ** 2) ** 0.5
turtle.goto(x, y)
for x in range(width // -2, width // 2 + 1): # 以 y = location[1] 的单位下半部分描点
y = location[1] - (((a ** 2 - x ** 2) * b ** 2) / a ** 2) ** 0.5
turtle.goto(x, y)
else: # 通过椭圆极坐标方程计算描点绘画
for ang in range(360):
turtle.goto(width / 2 * math.cos(math.radians(ang + 1)), location[1] + height / 2 * math.sin(math.radians(ang + 1)))
if fill:
turtle.end_fill()
turtle.penup()
turtle.color(*color_old)
turtle.goto(*location)
turtle.setheading(angle)
绘画圆柱
这里以底部椭圆的中心坐标作为起始点,比椭圆多两个参数,多一个圆柱高度和圆柱侧面填充颜色,操作也就是先画底部椭圆,在画侧边长方形,最后上方再盖一层一模一样的椭圆:
def draw_cylinder(location, width, height, altitude, fill=True, color1='#000000', color2='#000000'):
"""
画椭圆柱。
:param location: 底部椭圆中心坐标
:param width: 底部椭圆长轴(横向)
:param height: 底部椭圆短轴(纵向)
:param altitude: 椭圆柱高度
:param fill: 是否填充
:param color1: 椭圆柱面绘图颜色
:param color2: 椭圆柱顶绘图颜色
"""
color_old = turtle.color()
angle = turtle.heading()
draw_oval(location, width, height, fill=fill, color=color1)
turtle.penup()
turtle.color(color1)
turtle.goto(*location)
turtle.setheading(0)
turtle.forward(width / 2)
turtle.pendown()
if fill:
turtle.begin_fill()
turtle.setheading(90)
turtle.forward(altitude)
turtle.setheading(180)
turtle.forward(width)
turtle.setheading(270)
turtle.forward(altitude)
turtle.setheading(0)
turtle.forward(width)
turtle.end_fill()
turtle.penup()
location = (location[0], location[1] + altitude)
draw_oval(location, width, height, fill=fill, color=color2)
turtle.penup()
turtle.color(*color_old)
turtle.goto(*location)
turtle.setheading(angle)
绘画波浪线
其实就是在画三角函数,设置一下画笔宽度和起伏程度,计算波浪形状改变比例:
def draw_wave(location, width, height, space, pensize=10, color='#000000', towards='right'):
"""
画波浪线。
:param location: 波浪线起点(中心点)
:param width: 波浪线总宽度
:param height: 波浪线高度
:param space: 波浪间距
:param pensize: 波浪粗细
:param color: 波浪颜色
:param towards: 破浪线朝向
"""
assert towards.lower() in ['left', 'right'], '方向只能是 left向左 或 right向右 !'
color_old = turtle.color()
angle = turtle.heading()
thickness = turtle.pensize()
turtle.color(color)
turtle.penup()
turtle.goto(*location)
turtle.pensize(pensize)
turtle.pendown()
if towards.lower() == 'right':
turtle.setheading(0)
else:
turtle.setheading(180)
for x in range(width):
turtle.goto(location[0] + x, location[1] + height / 2 * math.sin(math.radians(x * 180 / space)))
turtle.penup()
turtle.color(*color_old)
turtle.pensize(thickness)
turtle.goto(*location)
turtle.setheading(angle)
用在蛋糕上有点简陋,毕竟蛋糕上圆柱侧面所显示的是个扇形弧面,应该让图像两边的y值按比例扩大一下,接下来就是经过修改后的函数:
def draw_wave(location, width, height, space, pensize=10, color='#000000', towards='right'):
"""
画波浪线。
:param location: 波浪线起点(中心点)
:param width: 波浪线总宽度
:param height: 波浪线高度
:param space: 波浪间距
:param pensize: 波浪粗细
:param color: 波浪颜色
:param towards: 破浪线朝向
"""
assert towards.lower() in ['left', 'right'], '方向只能是 left向左 或 right向右 !'
color_old = turtle.color()
angle = turtle.heading()
thickness = turtle.pensize()
turtle.color(color)
turtle.penup()
turtle.goto(location[0], location[1] + height)
turtle.pensize(pensize)
turtle.pendown()
if towards.lower() == 'right':
turtle.setheading(0)
else:
turtle.setheading(180)
for x in range(width):
turtle.goto(location[0] + x, location[1] + height / 2 * math.sin(math.radians(x * 180 / space)) + height * abs(x - width / 2) / width * 2)
turtle.penup()
turtle.color(*color_old)
turtle.pensize(thickness)
turtle.goto(*location)
turtle.setheading(angle)
这是原先位置的,是不是都超出去了?在不改变函数传入参数的情况下,加上微调:
两端就这样拐过去了~这样蛋糕上就能容纳更多的 波浪线
绘画正多边形
虽然使用 turtle.circle(radius, extent, steps)
也能画正多边形,但是他是按外接圆半径计算的,这里用边长来绘画:
def draw_polygon_by_point(point, radius, rotate, side, fill=True):
"""
按起始顶点和边长绘制圆内正多边形。
:param point: 顶点位置
:param radius: 边长
:param rotate: 中心顺时针旋转
:param side: 边数
:param fill: 是否填充
"""
assert side > 2 and isinstance(side, int), ValueError('边数必须是大于2的正整数!')
turtle.penup()
turtle.setheading((180 * (side - 2) / side) / 2 - 90) # 绝对数值对称
turtle.right(rotate) # 初始中心旋转
turtle.goto(point[0], point[1])
turtle.pendown()
if fill:
turtle.begin_fill()
for angle in range(side):
turtle.forward(radius)
turtle.right(180 - (180 * (side - 2) / side))
if fill:
turtle.end_fill()
turtle.penup()
turtle.setheading(0) # 恢复到水平向右的方向
这个包含的初始旋转和中心顺时针旋转,能自动让顶点位于正上方(在不使用中心旋转的情况下)
绘画奇数正多边形内邻点连线图形
上次绘画的方式是从边上某点就开始绘画了,这次统一从中心点来看待这些图形:
def draw_odd_shape(location, radius, rotate, side, fill=True):
"""
按外接圆圆心和半径绘制圆内正多边形顶点连线图(奇数边数)。
:param location: 圆心位置
:param radius: 半径长度
:param rotate: 中心顺时针旋转
:param side: 边数
:param fill: 是否填充
"""
length = 2 * radius * math.sin(math.radians(180 * (side - 2) / side + (45 if side == 4 else 0))) # 计算任意两间隔顶点间的距离
turtle.penup()
turtle.setheading(90) # 先将初始顶点设置于正上方
turtle.right(rotate) # 初始中心旋转
turtle.goto(location[0], location[1])
turtle.forward(radius) # 此处到达最上方顶点
turtle.right(90 + 2 * 180 / (side if side != 4 else 8)) # 倾斜方便绘制第一条直线
turtle.pendown()
if fill:
turtle.begin_fill()
for angle in range(side):
turtle.forward(length)
turtle.right(4 * 180 / (side if side != 4 else 8))
if fill:
turtle.end_fill()
turtle.penup()
turtle.setheading(90) # 恢复到垂直向上的方向
turtle.goto(location[0], location[1])
绘画正多边形内邻点连线图形
def draw_polygon_by_center(location, radius, rotate, side, fill=True):
"""
按外接圆圆心和半径绘制圆内正多边形最近间隔顶点连线图。
:param location: 圆心位置
:param radius: 半径长度
:param rotate: 中心顺时针旋转
:param side: 边数
:param fill: 是否填充
"""
assert side > 2 and isinstance(side, int), ValueError('边数必须是大于2的正整数!')
if side < 5 or side % 2: # 边数小于5,或者是奇数边数时可以一次性画完
draw_odd_shape(location, radius, rotate, side, fill)
else: # 偶数边数时要分两个小形状变换角度画两次
'''
正多边形内间最远隔点连线图:
draw_polygon_by_center(location, radius, rotate, side // 2, fill)
draw_polygon_by_center(location, radius, rotate + 360 / side, side // 2, fill)
'''
location = (location[0], location[1] + radius)
draw_polygon_by_point(location, 2 * radius * math.sin(math.radians(180 * (side - 2) / side + (45 if side == 4 else 0))), rotate, side // 2, fill)
turtle.goto(location[0], location[1] - radius) # 回到中心
turtle.setheading(90 - (rotate + 360 / side)) # 向右上角出发
turtle.forward(radius) # 前进到右上角顶点
location = turtle.position() # 获取右上角顶点坐标
turtle.back(radius) # 返回中心
draw_polygon_by_point(location, 2 * radius * math.sin(math.radians(180 * (side - 2) / side + (45 if side == 4 else 0))), rotate + 360 / side, side // 2, fill)
在这函数中,会自动判断边数是不是奇数,奇数或者边过少,则直接调用画奇数的函数,否则自动绘画两个正多边形并自动旋转。
部分图案组合图
首先上场的是正三边形到正十八边形内邻点连线图形,之前也发过一个有更多边形的红色组合,这里用黄色的显示:
把他们拆开后就是这样一堆图形:
好看吧~python绘图就是这样动动手指,绘制自己用手绘画不出来的东西。