当前位置:   article > 正文

手把手教你用Python绘制圣诞树 | Manim实践

watermark.construct

来源:Python数据之道 (ID:PyDataLab)

作者:阳哥

01写在前面

大家好,我是阳哥。

前几天,我跟大家介绍了视频制作工具 Manim,不少同学都觉得不错,想进一步学习。

刚好圣诞节快要到了,我之前在视频号「Python数据之道」上发了圣诞树制作的视频。

今天,我来介绍下视频中的效果是怎么制作的。

02Manim 基础知识

如何运行 Manim

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质量;

  • christmas-intro.py 是py代码文件;

  • DemoSquare 是 py代码文件中的一个类;

命令行中,还有其他许多参数可以设置,可以通过社区版的支持文档来进一步了解:

https://docs.manim.community/en/stable/tutorials/configuration.html#command-line-arguments

圣诞树中的元素

绘制圣诞树,有不少同学是用 Python 的 Turtle 包来绘制的,在这里我用 Manim 进行了绘制,效果如下:

在视频中,一共绘制了 3 颗不同的圣诞树,这里以下面的这棵树为例,来介绍 Manim 的一些用法。

e09a65c7987d055c518817b69139d9f4.gif

对于圣诞树中的细分元素,通过观察,只涉及到正方形和圆形,以及填充不同的颜色。

绘制正方形

Manim 绘制正方形,Manim 提供了类对象 Square 来绘制,如下:

  1. class DemoSquare(Scene):
  2.     def construct(self):
  3.         # WaterMark.construct(self)
  4.         r = 1
  5.         sq = Square(
  6.             side_length=2*r,
  7.             color=GREEN_LEMON,
  8.             )
  9.         self.add(sq)

通过下面的命令来运行:

manim -pql christmas-intro.py DemoSquare
8b71d299d8c0dd5292ecf4c5089670da.gif

绘制效果如下:

65ae3c2c1703dde4a3764619f423b0ec.png

如果需要对正方形进行填充,则可以设置参数 fill_colorfill_opacity ,代码和效果如下:

  1. class DemoSquare(Scene):
  2.     def construct(self):
  3.         # WaterMark.construct(self)
  4.         r = 1
  5.         sq = Square(
  6.             side_length=2*r,
  7.             color=GREEN_LEMON,
  8.             fill_color=GREEN_LEMON, 
  9.             fill_opacity=1,
  10.             )
  11.         self.add(sq)
831947ccda0ef0bb6ec34994810e647e.png

绘制圆形

对于绘制圆形,与绘制正方形类似,Manim 提供了类对象 Circle 来绘制,如下:

  1. class DemoCircle(Scene):
  2.     def construct(self):
  3.         # WaterMark.construct(self)
  4.         r = 1
  5.         circle = Circle(
  6.             radius=r,
  7.             color=GREEN_LEMON,
  8.             fill_color=GREEN_LEMON,
  9.             fill_opacity=1,
  10.         )
  11.         self.play(Create(circle))
  12.         self.wait()

运行的命令行如下:

manim -pql --disable_caching christmas-intro.py DemoCircle --format=gif

其中:

  • --format=gif 表示保存为 .gif

  • --disable_caching 一般是视频帧数很多的时候,加上这个参数,可以加快渲染的速度,一般情况下,可以不加这个参数。

效果如下:

0ac9f733db6a4fbc73a4d7731c7a9184.gif

在代码中添加了 playCreate 因此会有动态的效果了。

03绘制圣诞树

对于这个圣诞树,在用 Manim 制作时,对于形状部分可以分为三个部分,如下:

b56e382c988b98077a8a46cc3b61fcc5.png

第 1 部分和 第 3 部分,相对而言要简单些,第2部分,看起来复杂些,但其实也是由三个类似的单元组合起来的。

93967982ac2b5129a38e99b2db0f9120.png

绘制第2部分

对于第2部分中的每一个细分单元,可以用通过一个自定义函数来实现:

  1. def draw_tree(n, buff=1):
  2.   shapes = Group()
  3.   for i in range(4):
  4.       sq_lst = [
  5.           Square(
  6.               side_length=2 * r,
  7.               color=GREEN_LEMON,
  8.               fill_color=GREEN_LEMON,
  9.               fill_opacity=1,
  10.           )
  11.           for j in range(2 * (i + n) + 1)
  12.       ]
  13.       sq_group = Group(*sq_lst).arrange(RIGHT)
  14.       sq_group.to_edge(UP, buff=buff)
  15.       if i == 2:
  16.           sq_side_lst = [
  17.               Square(
  18.                   side_length=2 * r,
  19.                   color=YELLOW,
  20.                   fill_color=YELLOW,
  21.                   fill_opacity=1,
  22.               )
  23.               for j in range(2)
  24.           ]
  25.           sq_side_group = Group(*sq_side_lst)
  26.           sq_side_group[0].next_to(sq_group, LEFT)
  27.           sq_side_group[1].next_to(sq_group, RIGHT)
  28.           self.add(sq_side_group[0])
  29.           self.wait(time_wait)
  30.           for sq in sq_group:
  31.               self.add(sq)
  32.               self.wait(time_wait)
  33.           self.add(sq_side_group[1])
  34.           self.wait(time_wait)
  35.           shapes.add(sq_side_group[0])
  36.           shapes.add(sq_group)
  37.           shapes.add(sq_side_group[1])
  38.       elif i == 3:
  39.           circle_side_lst = [
  40.               Circle(
  41.                   radius=r,
  42.                   color=RED_LEMON,
  43.                   fill_color=RED_LEMON,
  44.                   fill_opacity=1,
  45.               )
  46.               for j in range(2)
  47.           ]
  48.           circle_side_group = Group(*circle_side_lst)
  49.           circle_side_group[0].next_to(sq_group, LEFT)
  50.           circle_side_group[1].next_to(sq_group, RIGHT)
  51.           self.add(circle_side_group[0])
  52.           self.wait(time_wait)
  53.           for sq in sq_group:
  54.               self.add(sq)
  55.               self.wait(time_wait)
  56.           self.add(circle_side_group[1])
  57.           self.wait(time_wait)
  58.           shapes.add(circle_side_group[0])
  59.           shapes.add(sq_group)
  60.           shapes.add(circle_side_group[1])
  61.       else:
  62.           shapes.add(sq_group)
  63.           for sq in sq_group:
  64.               self.add(sq)
  65.               self.wait(time_wait)
  66.       buff += buff_gap
  67.   return shapes

上面这个代码片段,看起来很长,其实逻辑还是不复杂的。主要是根据图形之间的布局关系来编写代码的。有兴趣的同学可以先自行研究下。

通过上面的这个自定义函数,咱们可以将第二部分绘制出来,关键代码如下:

  1. tree_caps = Group()
  2.         for n in range(3):
  3.             tree_cap = draw_tree(n, buff=buff_gap * 2 + buff_gap * n * 4)
  4.             tree_caps.add(tree_cap)

绘制第3部分

第3部分,在代码中,我称之为 tree_root ,主要是绘制正方形组成的矩形方阵,然后控制视频演示的顺序。

代码如下:

  1. tree_root = Group()
  2.   for i in range(3):
  3.       sq_lst = [
  4.           Square(
  5.               side_length=2 * r,
  6.               color=RED_LEMON,
  7.               fill_color=RED_LEMON,
  8.               fill_opacity=1,
  9.           )
  10.           for j in range(5)
  11.       ]
  12.       sq_group = Group(*sq_lst).arrange(RIGHT)
  13.       tree_root.add(sq_group)
  14.   for i in range(1len(tree_root)):
  15.       tree_root[i].next_to(tree_root[i - 1], DOWN, buff=buff_gap / 2)
  16.   tree_root.next_to(tree_caps, DOWN, buff=buff_gap / 2)
  17.   for i in range(len(tree_root)):
  18.       for j in range(len(tree_root[i])):
  19.           self.add(tree_root[i][j])
  20.           self.wait(time_wait)
  21.   self.wait()

缩小放大效果

对于图形的所辖和放大效果,在  Manim 中,可以通过 animate 以及 scale 的设置来实现:

  1. tree = Group(circle, tree_caps, tree_root)
  2. self.play(tree.animate.move_to(ORIGIN).scale(0.5), run_time=4)
  3. 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 中,还包括 Tree01Tree03,大家也可以运行看看。

对于代码运行过程中的一些问题,欢迎一起交流,也祝大家圣诞快乐~~

相关阅读

大家读完顺手点下右下角的  “在看” ,就是最大的鼓励和支持了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/325923
推荐阅读
相关标签
  

闽ICP备14008679号