大家好,欢送来到 Crossin 的编程教室!
前几天,后盾老有小伙伴留言“爱心代码”。这不是 Crossin 很早之前发过的内容嘛,怎么最近忽然又被人翻出来了?起初才晓得,原来是一部无关程序员的青春偶像剧《点燃我, 和煦你》在热播,而剧中有一段对于期中考试要用程序画一个爱心的桥段。
于是出于好奇,Crossin 就去看了这一集(第 5 集,不用谢)。这一看不要紧,差点把刚吃的鸡腿给喷出来 – 槽点切实太多了!
忍不住做了个欢畅吐槽向的代码解读视频,在某平台上被顶到了 20 个 w 的浏览,也算蹭了一波人家电视剧的热度吧……
https://www.bilibili.com/vide…
上面是图文版,给大家剖析下剧中呈现的“爱心”代码,并且来复刻一下最初男主实现的酷炫跳动爱心。
剧中代码赏析
- 首先是路人同学的代码:
尽管剧中说是“C 语言期中考试”,但这位同学的代码名叫 draw2.py,一个典型的 Python 文件,再联合截图中的 pen.forward、pen.setpos 等办法来看,应该是用 turtle 海龟作图库来画爱心。那成果通常是这样的:
import turtle as t
t.color('red')
t.setheading(50)
t.begin_fill()
t.circle(-100, 170)
t.circle(-300, 40)
t.right(38)
t.circle(-300, 40)
t.circle(-100, 170)
t.end_fill()
t.done()
而不是剧中那个命令行下用 1 组成的不规则的图形。
- 而后是课代表向路人同学展现的优良代码:
及所谓的成果:
这的确是 C 语言代码了,但文件仍然是以 .py 为后缀,并且 include 后面没有加上 #,这显然是没法运行的。
外面的内容是能够画出爱心的,用是这个爱心曲线公式:
而后遍历一个 1517 的方阵,计算每个坐标是在曲线内还是曲线外,在外部就输入 #或 ,内部就是 -
用 python 改写一下是这样的:
for y in range(9, -6, -1):
for x in range(-8, 9):
print('*##*'[(x+10)%4] if (x*x+y*y-25)**3 < 25*x*x*y*y*y else '-', end=' ')
print()
成果:
略微改一下输入,还能做出后面那个全是 1 的成果:
for y in range(9, -6, -1):
for x in range(-8, 9):
print('1' if (x*x+y*y-25)**3 < 25*x*x*y*y*y else '', end=' ')
print()
但跟剧中所谓的成果相去甚远。
- 最初是配角狂拽酷炫 D 炸天的跳动爱心:
代码有两个片段:
但这两个片段也不 C 语言,而是 C ++,且两段并不是同一个程序,用的办法也齐全不一样。
第一段代码跟后面一种思路差不多,只不过没有间接用一条曲线,而是上半部用两个圆形,下半部用两条直线,围出一个爱心。
改写成 Python 代码:
size = 10
for x in range(size):
for y in range(4*size+1):
dist1 = ((x-size)**2 + (y-size)**2) ** 0.5
dist2 = ((x-size)**2 + (y-3*size)**2) ** 0.5
if dist1 < size + 0.5 or dist2 < size + 0.5:
print('V', end=' ')
else:
print('', end=' ')
print()
for x in range(1, 2*size):
for y in range(x):
print('', end=' ')
for y in range(4*size+1-2*x):
print('V', end=' ')
print()
运行成果:
第二段代码用的是基于极坐标的爱心曲线,是遍历角度来计算点的地位。公式是:
计算出不同角度对应的点坐标,而后把它们连起来,就是一个爱心。
from math import pi, sin, cos
import matplotlib.pyplot as plt
no_pieces = 100
dt = 2*pi/no_pieces
t = 0
vx = []
vy = []
while t <= 2*pi:
vx.append(16*sin(t)**3)
vy.append(13*cos(t)-5*cos(2*t)-2*cos(3*t)-cos(4*t))
t += dt
plt.plot(vx, vy)
plt.show()
成果:
代码中循环时用到的 2π 是为了保障曲线长度足够绕一个圈,但其实长一点也无所谓,即便 π=100 也不影响显示成果,只是相当于同一条曲线画了很多遍。所以剧中代码里写下 35 位小数的 π,还被女主用纸笔一字不落地缮写下来,切实是让程序员无奈了解的蛊惑行为。
但不论写再多位的 π,上述两段代码都和最终那个跳动的成果差了五百只羊了个羊。
跳动爱心实现
作为一个总是在写一些没什么乱用的代码的编程博主,Crossin 当然也不会放过这个机会,上面就来挑战一下用 Python 实现最终的那个成果。
- 想要绘制动静的成果,必然要借助一些库的帮忙,不然代码量必定会让你打动得想哭。这里咱们将应用之前 羊了个羊游戏 里用过的 pgzero 库。而后联合最初那个极坐标爱心曲线代码,先绘制出曲线上离散的点。
import pgzrun
from math import pi, sin, cos
no_p = 100
dt = 2*3/no_p
t = 0
x = []
y = []
while t <= 2*3:
x.append(16*sin(t)**3)
y.append(13*cos(t)-5*cos(2*t)-2*cos(3*t)-cos(4*t))
t += dt
def draw():
screen.clear()
for i in range(len(x)):
screen.draw.filled_rect(Rect((x[i]*10+400, -y[i]*10+300), (4, 4)), 'pink')
pgzrun.go()
- 把点的数量减少,同时沿着原点到每个点的径向加一个随机数,并且这个随机数是依照正态分布来的(半个正态分布),大概率分布在曲线上,向曲线外部递加。这样,就失去这样一个随机散布的爱心成果。
...
no_p = 20000
...
while t <= 2*pi:
l = 10 - abs(random.gauss(10, 2) - 10)
x.append(l*16*sin(t)**3)
y.append(l*(13*cos(t)-5*cos(2*t)-2*cos(3*t)-cos(4*t)))
t += dt
...
- 上面就是让点动起来,这步是要害,也有一点点简单。为了不便对于每个点进行管制,这里将每个点自定义成了一个 Particle 类的实例。
从原理上来说,就是给每个点加一个缩放系数,这个系数是依据工夫变动的正弦函数,看起来就会像呼吸的节律一样。
class Particle():
def __init__(self, pos, size, f):
self.pos = pos
self.pos0 = pos
self.size = size
self.f = f
def draw(self):
screen.draw.filled_rect(Rect((10*self.f*self.pos[0] + 400, -10*self.f*self.pos[1] + 300), self.size), 'hot pink')
def update(self, t):
df = 1 + (2 - 1.5) * sin(t * 3) / 8
self.pos = self.pos0[0] * df, self.pos0[1] * df
...
t = 0
def draw():
screen.clear()
for p in particles:
p.draw()
def update(dt):
global t
t += dt
for p in particles:
p.update(t)
- 剧中爱心跳动时,靠两头的点稳定的幅度更大,有一种扩张的成果。所以再依据每个点间隔原点的远近,再加上一个系数,离得越近,系数越大。
class Particle():
...
def update(self, t):
df = 1 + (2 - 1.5 * self.f) * sin(t * 3) / 8
self.pos = self.pos0[0] * df, self.pos0[1] * df
- 最初再用同样的办法画一个更大一点的爱心,这个爱心不须要跳动,只有每一帧随机绘制就能够了。
def draw():
...
t = 0
while t < 2*pi:
f = random.gauss(1.1, 0.1)
x = 16*sin(t)**3
y = 13*cos(t)-5*cos(2*t)-2*cos(3*t)-cos(4*t)
size = (random.uniform(0.5,2.5), random.uniform(0.5,2.5))
screen.draw.filled_rect(Rect((10*f*x + 400, -10*f*y + 300), size), 'hot pink')
t += dt * 3
合在一起,搞定!
总结一下,就是在本来的根底爱心曲线上加上一个正态分布的随机量、一个随工夫变动的正弦函数和一个跟间隔成反比的系数,里面再套一层更大的随机爱心,就失去相似剧中的跳动爱心成果。
但话说回来,真有人会在考场上这么干吗?
除非真的是超级大学霸,不然就是食堂伙食太好 –
吃太饱撑的……
代码已开源:python666.cn/c/9
如二创公布请注明代码起源:Crossin 的编程教室