手把手教你用Python绘制圣诞树 - Manim实践
阅读量:次 Authors: 阳哥 VIDEOCREATION
Manim ManimCE
阅读量:次 Authors: 阳哥 VIDEOCREATION
Manim ManimCE
Table of Contents
大家好,我是阳哥。
前几天,我跟大家介绍了视频制作工具 Manim
,不少同学都觉得不错,想进一步学习。
刚好圣诞节快要到了,我之前在视频号「Python数据之道」上发了圣诞树制作的视频。
今天,我来介绍下视频中的效果是怎么制作的。
用 Manim
绘制图形,首先需要引入 Manim
库,然后将需要绘制的内容封装到一个 类(class) 里面。
from manim import *
对于 编辑好的程序文件(例如christmas-intro.py
文件),需要在同一个文件夹下运行命令来运行程序,命令格式如下:
manim -pql christmas-intro.py DemoSquare
上面的命令行中:
manim
是运行程序的主要标志用语;p
表示程序运行后会进行预览(图片或视频);ql
表示低质量(quality low), 其他的选项有 -ql
, -qm
, -qh
, -qk
, 分别表示 低质量、正常质量、高质量、4K质量;命令行中,还有其他许多参数可以设置,可以通过社区版的支持文档来进一步了解:
https://docs.manim.community/en/stable/tutorials/configuration.html#command-line-arguments
绘制圣诞树,有不少同学是用 Python 的 Turtle
包来绘制的,在这里我用 Manim
进行了绘制,效果如下:
在视频中,一共绘制了 3 颗不同的圣诞树,这里以下面的这棵树为例,来介绍 Manim
的一些用法。
对于圣诞树中的细分元素,通过观察,只涉及到正方形和圆形,以及填充不同的颜色。
用 Manim
绘制正方形,Manim
提供了类对象 Square
来绘制,如下:
class DemoSquare(Scene):
def construct(self):
# WaterMark.construct(self)
r = 1
sq = Square(
side_length=2*r,
color=GREEN_LEMON,
)
self.add(sq)
通过下面的命令来运行:
manim -pql christmas-intro.py DemoSquare
绘制效果如下:
如果需要对正方形进行填充,则可以设置参数 fill_color
和 fill_opacity
,代码和效果如下:
class DemoSquare(Scene):
def construct(self):
# WaterMark.construct(self)
r = 1
sq = Square(
side_length=2*r,
color=GREEN_LEMON,
fill_color=GREEN_LEMON,
fill_opacity=1,
)
self.add(sq)
对于绘制圆形,与绘制正方形类似,Manim
提供了类对象 Circle
来绘制,如下:
class DemoCircle(Scene):
def construct(self):
# WaterMark.construct(self)
r = 1
circle = Circle(
radius=r,
color=GREEN_LEMON,
fill_color=GREEN_LEMON,
fill_opacity=1,
)
self.play(Create(circle))
self.wait()
运行的命令行如下:
manim -pql --disable_caching christmas-intro.py DemoCircle --format=gif
其中:
--format=gif
表示保存为 .gif
;--disable_caching
一般是视频帧数很多的时候,加上这个参数,可以加快渲染的速度,一般情况下,可以不加这个参数。效果如下:
在代码中添加了 play
、Create
因此会有动态的效果了。
对于这个圣诞树,在用 Manim
制作时,对于形状部分可以分为三个部分,如下:
第 1 部分和 第 3 部分,相对而言要简单些,第2部分,看起来复杂些,但其实也是由三个类似的单元组合起来的。
对于第2部分中的每一个细分单元,可以用通过一个自定义函数来实现:
def draw_tree(n, buff=1):
shapes = Group()
for i in range(4):
sq_lst = [
Square(
side_length=2 * r,
color=GREEN_LEMON,
fill_color=GREEN_LEMON,
fill_opacity=1,
)
for j in range(2 * (i + n) + 1)
]
sq_group = Group(*sq_lst).arrange(RIGHT)
sq_group.to_edge(UP, buff=buff)
if i == 2:
sq_side_lst = [
Square(
side_length=2 * r,
color=YELLOW,
fill_color=YELLOW,
fill_opacity=1,
)
for j in range(2)
]
sq_side_group = Group(*sq_side_lst)
sq_side_group[0].next_to(sq_group, LEFT)
sq_side_group[1].next_to(sq_group, RIGHT)
self.add(sq_side_group[0])
self.wait(time_wait)
for sq in sq_group:
self.add(sq)
self.wait(time_wait)
self.add(sq_side_group[1])
self.wait(time_wait)
shapes.add(sq_side_group[0])
shapes.add(sq_group)
shapes.add(sq_side_group[1])
elif i == 3:
circle_side_lst = [
Circle(
radius=r,
color=RED_LEMON,
fill_color=RED_LEMON,
fill_opacity=1,
)
for j in range(2)
]
circle_side_group = Group(*circle_side_lst)
circle_side_group[0].next_to(sq_group, LEFT)
circle_side_group[1].next_to(sq_group, RIGHT)
self.add(circle_side_group[0])
self.wait(time_wait)
for sq in sq_group:
self.add(sq)
self.wait(time_wait)
self.add(circle_side_group[1])
self.wait(time_wait)
shapes.add(circle_side_group[0])
shapes.add(sq_group)
shapes.add(circle_side_group[1])
else:
shapes.add(sq_group)
for sq in sq_group:
self.add(sq)
self.wait(time_wait)
buff += buff_gap
return shapes
上面这个代码片段,看起来很长,其实逻辑还是不复杂的。主要是根据图形之间的布局关系来编写代码的。有兴趣的同学可以先自行研究下。
通过上面的这个自定义函数,咱们可以将第二部分绘制出来,关键代码如下:
tree_caps = Group()
for n in range(3):
tree_cap = draw_tree(n, buff=buff_gap * 2 + buff_gap * n * 4)
tree_caps.add(tree_cap)
第3部分,在代码中,我称之为 tree_root
,主要是绘制正方形组成的矩形方阵,然后控制视频演示的顺序。
代码如下:
tree_root = Group()
for i in range(3):
sq_lst = [
Square(
side_length=2 * r,
color=RED_LEMON,
fill_color=RED_LEMON,
fill_opacity=1,
)
for j in range(5)
]
sq_group = Group(*sq_lst).arrange(RIGHT)
tree_root.add(sq_group)
for i in range(1, len(tree_root)):
tree_root[i].next_to(tree_root[i - 1], DOWN, buff=buff_gap / 2)
tree_root.next_to(tree_caps, DOWN, buff=buff_gap / 2)
for i in range(len(tree_root)):
for j in range(len(tree_root[i])):
self.add(tree_root[i][j])
self.wait(time_wait)
self.wait()
对于图形的所辖和放大效果,在 Manim
中,可以通过 animate
以及 scale
的设置来实现:
tree = Group(circle, tree_caps, tree_root)
self.play(tree.animate.move_to(ORIGIN).scale(0.5), run_time=4)
self.play(tree.animate.scale(2), run_time=4)
这颗树的完整代码,我已经封装在 christmas-intro.py
文件的 类 Tree02
中,大家可以运行下面的命令来运行代码:
manim -pql christmas-intro.py Tree02
完整的代码文件 christmas-intro.py
,大家可以在公众号「Python数据之道」回复 manim 来获取。
代码文件 christmas-intro.py
中,还包括 Tree01
和 Tree03
,大家也可以运行看看。
对于代码运行过程中的一些问题,欢迎一起交流。
排序算法: 汇总 , 冒泡排序 , 选择排序 , 快速排序 , 归并排序 , 堆排序 , 插入排序 , 希尔排序 , 计数排序 , 桶排序 , 基数排序
对我的文章感兴趣的朋友,可以关注我的微信公众号「Python数据之道」(ID:PyDataLab),接收我的更新通知。