7

中秋节到了,送你一个Python做的Crossin牌“月饼”

 3 years ago
source link: https://zhuanlan.zhihu.com/p/260768353
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

中秋节到了,送你一个Python做的Crossin牌“月饼”

上海交通大学 计算机应用技术硕士

明天是难得一见的国庆中秋双节合一,在这里除了祝大家节日快乐之外,我们还要送上一个“月饼”——当然这个月饼是不能吃的,因为它是用python做的。先给大家看一下效果图。

v2-a6f39b79de3f7ce752e9aa7f9c4e6780_720w.jpg
图1. 月饼效果图

画这样一个月饼,其实不需要太多的代码,笔者只用了不到50行代码,而且用的是我们经常用到的绘图库matplotlib,这对于我们大部分人来说是很容易上手的,下面笔者就详细介绍一下怎么画这样一个月饼。

首先导入各种需要的库:

import numpy as np
from numpy import sin, cos, pi
import matplotlib.pyplot as plt
import matplotlib.patches as mpatch
from matplotlib.patches import Arc, Circle, Wedge
from matplotlib.collections import PatchCollection

然后设置3个变量,这3个变量的用途到后面会有解释:

length = 20
R = 3**0.5*length/(3**0.5*cos(pi/12)-sin(pi/12))
r = 2*sin(pi/12)*R/3**0.5

接下来我们要画12条弧线,这12条弧线是月饼边缘的花纹:

arc1 = Arc([0, length], width=2*r, height=2*r, angle=0, theta1=30, theta2=150, ec='orange', linewidth=4)
arc2 = Arc([-length/2, length/2*3**0.5], width=2*r, height=2*r, angle=0, theta1=60, theta2=180, ec='orange', linewidth=4)
arc3 = Arc([-length/2*3**0.5, length/2], width=2*r, height=2*r, angle=0, theta1=90, theta2=210, ec='orange', linewidth=4)
arc4 = Arc([-length, 0], width=2*r, height=2*r, angle=0, theta1=120, theta2=240, ec='orange', linewidth=4)
arc5 = Arc([-length/2*3**0.5, -length/2], width=2*r, height=2*r, angle=0, theta1=150, theta2=270, ec='orange', linewidth=4)
arc6 = Arc([-length/2, -length/2*3**0.5], width=2*r, height=2*r, angle=0, theta1=180, theta2=300, ec='orange', linewidth=4)
arc7 = Arc([0, -length], width=2*r, height=2*r, angle=0, theta1=210, theta2=330, ec='orange', linewidth=4)
arc8 = Arc([length/2, -length/2*3**0.5], width=2*r, height=2*r, angle=0, theta1=240, theta2=360, ec='orange', linewidth=4)
arc9 = Arc([length/2*3**0.5, -length/2], width=2*r, height=2*r, angle=0, theta1=270, theta2=390, ec='orange', linewidth=4)
arc10 = Arc([length, 0], width=2*r, height=2*r, angle=0, theta1=300, theta2=420, ec='orange', linewidth=4)
arc11 = Arc([length/2*3**0.5, length/2], width=2*r, height=2*r, angle=0, theta1=330, theta2=450, ec='orange', linewidth=4)
arc12 = Arc([length/2, length/2*3**0.5], width=2*r, height=2*r, angle=0, theta1=0, theta2=120, ec='orange', linewidth=4)

看到这里估计很多人懵逼了,代码是不是有点多了?实际上这些弧线都是来自于同一个Arc类,它们的用法都是一样的,只不过部分参数不同,所以我们只要掌握一个,其他的全都会了。这里笔者就用arc1来说明一下:

arc1 = Arc([0, length], width=2*r, height=2*r, angle=0, theta1=30, theta2=150, ec='orange', linewidth=4)

其中 [0, length] 是这个Arc弧线的圆心,因为这个Arc类是一个椭圆形的一部分,所以其包含圆心、横轴、纵轴等参数,length变量就是arc1圆心的纵坐标,width=2*r height=2*r 就说明这个椭圆的纵轴和横轴长度相等,其是一个圆,而不是椭圆,变量 r 就是这个圆的半径。angle=0 就是我们不需要对这个圆进行旋转,theta1=30 theta2=150 是指明这个弧线的起始角度和终止角度,这里的角度分别是30度150度。而 ec='orange' linewidth=4 分别指边线的颜色和线条宽度,颜色我们设为橙色宽度为4。而这些弧线的效果图如图2所示,在图中笔者标出了arc1、arc5和arc9这3条弧线的位置,所有弧线按照逆时针方向排列。这些位置大同小异,大家只要计算好其各自的位置参数就行,这里就不再赘述了。

图2. 弧线效果图

然后我们再画一个圆,这个圆是月饼的主体部分,其圆心和整个月饼的中心是重合的,圆心位置我们选在了坐标原点,代码如下:

circle = Circle((0,0), R, ec='orange', fc='white', linewidth=4)

这里变量R就是这个大圆的半径,边线颜色设置为橙色,表面颜色设为白色。其效果如图3所示。

图3. 大圆效果图

接下来我们再画上一些花纹,这些花纹主要由8个扇形组成,其中有4个大的扇形和4个小的扇形,代码如下:

wedge1 = Wedge([-2, 2], R-5, 90, 180, ec='orange', fc=r'white', linewidth=4)
wedge2 = Wedge([-5, 5], R-12, 90, 180, ec='orange', fc=r'white', linewidth=4)
wedge3 = Wedge([-2, -2], R-5, 180, 270, ec='orange', fc=r'white', linewidth=4)
wedge4 = Wedge([-5, -5], R-12, 180, 270, ec='orange', fc=r'white', linewidth=4)
wedge5 = Wedge([2, -2], R-5, 270, 360, ec='orange', fc=r'white', linewidth=4)
wedge6 = Wedge([5, -5], R-12, 270, 360, ec='orange', fc=r'white', linewidth=4)
wedge7 = Wedge([2, 2], R-5, 0, 90, ec='orange', fc=r'white', linewidth=4)
wedge8 = Wedge([5, 5], R-12, 0, 90, ec='orange', fc=r'white', linewidth=4)

这里wedge1、wedge3、wedge5、wedge7是大扇形,wedge2、wedge4、wedge6、wedge8是小扇形,一个大扇形嵌套一个小扇形,其中wedge1嵌套wedge2,wedge3嵌套wedge4,以此类推。Wedge这个类和前面Arc有些类似,不过Wedge是圆的一部分,以wedge1为例:

wedge1 = Wedge([-2, 2], R-5, 90, 180, ec='orange', fc=r'white', linewidth=4)

其中[-2, 2]是这个Wedge所代表的圆的圆心,R-5 是其半径,90 180分别代表起始和终止角度,ecfclinewidth这些都和前面Arc类的用法相似,这8个扇形的效果图如图4所示。在图中笔者标出了wedge1、wedge2、wedge5和wedge6的位置,所有扇形按照逆时针方向放置。

图4. 扇形效果图

最后我们再把公众号“CROSSIN”的名称加上,因为代码不多,所以把剩余所有代码都写在这里:

art_list = [arc1, arc2, arc3, arc4, arc5, arc6, arc7, arc8, arc9, arc10, arc11, arc12]
art_list.extend([circle, wedge1, wedge2, wedge3, wedge4, wedge5, wedge6, wedge7, wedge8])
fig, ax = plt.subplots(figsize=(8,8))
ax.set_aspect('equal')
for a in art_list:
    ax.add_patch(a)

plt.text(-18, -2.5, 'CROSSIN', fontfamily=r'Times New Man', bbox=dict(boxstyle='square', fc="w", ec='orange', linewidth=4),  fontsize=50, color='orange')

plt.ylim([-35, 35])
plt.xlim([-35, 35])

plt.show()

art_list是我们设置的一个list变量,里面放有arc1到arc12这12个图形,然后再把circle以及8个扇形都加进去。ax.set_aspect('equal')是设置整个图形x轴和y轴同比例,for a in art_list: ax.add_patch(a) 是把art_list中所有图形加入到画布当中。而plt.text这行代码中,-18和-2.5是这个text的左下角的坐标,fontfamily fontsize 分别是文字的字体和大小,bbox 是设置text边框的格式,这些参数也都和前面讲过的很多参数差不多,这里不再赘述。plt.ylim plt.xlim是设置整个画布的坐标范围。最终效果如图1所示。

当然我们也可以改变图片的颜色,比如设置成蓝色的,如图5所示,这个完全可以根据个人爱好而定。

图5. 蓝色月饼图

笔者在这里用了不到50行代码就画出了一个月饼,其中还有不少复用的,所以核心只有几个绘图函数,理解起来并不难。你也可以自己动手试一试,定制一块你自己的月饼。

最后再次祝大家,中秋快乐!国庆快乐!

作者:tyran
完整代码:https://gitee.com/crossin/snippet/tree/master/mooncake

获取更多教程和案例,

欢迎搜索及关注:Crossin的编程教室

这里还有更多精彩。一起学,走得远!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK