ManimCE文档及源码教程笔记--(一)Manim的组成部分

ManimCE文档及源码教程笔记--(一)Manim的组成部分

Manim教程admin2022-08-25 16:09:4173A+A-

Manim的组成部分

文档解释了manim的组成部分,并将为您提供开始制作自己的视频所需的所有工具。

基本上,manim提供了三个不同的概念供您使用,您可以将它们编排在一起以生成数学动画:数学对象(简称mobject)、动画和场景。正如我们将在以下几节中看到的,这三个概念中的每一个都作为一个单独的类在manim中实现:MobjectAnimationScene类。

笔记
在阅读本页之前,建议您阅读教程Quickstart和Manim的输出设置。

移动对象,Mobjects

mobject是所有manim动画的基本构建块。从Mobject派生的每个类表示一个可以在屏幕上显示的对象。例如,圆Circle、箭头Arrow和矩形Rectangle等简单形状都是mobject。更复杂的结构,如轴Axes函数FunctionGraph或条形图BarChart也是mobject。

如果试图在屏幕上显示Mobject的实例,则只能看到一个空帧。原因是Mobject类是所有其他Mobject的抽象基类,即它没有任何可以在屏幕上显示的预定义视觉形状。它只是一个可以展示的东西的骨架。因此,您很少需要使用Mobject的普通实例;相反,您很可能会创建其派生类的实例。其中一个派生类是VMobject。V代表矢量化Mobject。本质上,vmobject是使用矢量图形vector graphics进行显示的mobject。大多数时候,您将处理vmobjects,尽管我们将继续使用术语“mobject”来指代可以在屏幕上显示的形状类别,因为它更为通用。

笔记
任何可以在屏幕上显示的对象都是mobject,即使它不一定是数学性质的。

提示
要查看从Mobject派生的类的示例,请参阅几何体geometry模块。其中大多数事实上也是从VMobject派生的。

创建和显示移动对象

如Quickstart中所述,通常manim脚本中的所有代码都放在场景类的construct()方法中。要在屏幕上显示mobject,请调用包含场景Sceneadd()方法。这是在屏幕上显示未设置动画的移动对象的主要方式。要从屏幕上删除mobject,只需从包含的场景中调用remove()方法。

from manim import *
class CreatingMobjects(Scene):
    def construct(self):
        circle = Circle()
        self.add(circle)
        self.wait(1)
        self.remove(circle)
        self.wait(1)

放置移动对象

让我们定义一个名为Shapes的新场景Scene,并在其中添加add()一些mobject。该脚本生成一个静态图片,其中显示一个圆、一个正方形和一个三角形:

from manim import *
class Shapes(Scene):
    def construct(self):
        circle = Circle()
        square = Square()
        triangle = Triangle()
        circle.shift(LEFT)
        square.shift(UP)
        triangle.shift(RIGHT)
        self.add(circle, square, triangle)
        self.wait(1)

默认情况下,首次创建移动对象时,将其放置在坐标中心或原点。它们还提供了一些默认颜色。此外,形状场景使用shift()方法放置移动对象。正方形从原点向上移动一个单位,而圆和三角形分别左移LEFT和右移RIGHT一个单位。

注意
与其他图形软件不同,manim将坐标中心放置在屏幕的中心。垂直正方向向上,水平正方向向右。另请参阅常数constants模块中定义的常数原点、上UP、下DOWN、左LEFT、右RIGHT和其他。

有许多其他可能的方法可以将mobject放置在屏幕上,例如move_to()next_to()align_to()。下一个场景MobjectPlacement使用了这三种方法。

from manim import *
class MobjectPlacement(Scene):
    def construct(self):
        circle = Circle()
        square = Square()
        triangle = Triangle()
        # place the circle two units left from the origin
        circle.move_to(LEFT \        # place the square to the left of the circle
        square.next_to(circle, LEFT)
        # align the left border of the triangle to the left border of the circle
        triangle.align_to(circle, LEFT)
        self.add(circle, square, triangle)
        self.wait(1)

move_to()方法使用绝对单位(相对于原点测量),而next_to()使用相对单位(从作为第一个参数传递的mobject测量)。align_to()不使用LEFT作为测量单位,而是作为确定用于对齐的边界的方法。移动对象边界的坐标是使用其周围的假想边界框确定的。

提示
manim中的许多方法可以链接在一起。例如,两条线

square = Square()
square.shift(LEFT)

可以替换为

square = Square().shift(LEFT)

从技术上讲,这是可能的,因为大多数方法调用返回修改后的mobject。

设置移动对象的样式,Styling mobjects

以下场景更改了移动对象的默认美学。

from manim import *
class MobjectStyling(Scene):
    def construct(self):
        circle = Circle().shift(LEFT)
        square = Square().shift(UP)
        triangle = Triangle().shift(RIGHT)
        circle.set_stroke(color=GREEN, width=20)
        square.set_fill(YELLOW, opacity=1.0)
        triangle.set_fill(PINK, opacity=0.5)
        self.add(circle, square, triangle)
        self.wait(1)

此场景使用两个主要函数来更改移动对象的视觉样式:set_stroke()set_fill()。前者改变了主体边界的视觉风格,而后者则改变了室内的风格。默认情况下,大多数移动对象具有完全透明的内部,因此必须指定不透明度参数以显示颜色。不透明度为1.0表示完全不透明,而0.0表示完全透明。

只有VMobject的实例实现set_stroke()set_fill()Mobject的实例实现set_color()。绝大多数预定义类都是从VMobject派生的,因此通常可以安全地假设您可以访问set_stroke()set_fill()

移动对象屏幕顺序

下一个场景与上一节中的MobjectStyleing场景完全相同,只是只有一行。

from manim import *
class MobjectZOrder(Scene):
    def construct(self):
        circle = Circle().shift(LEFT)
        square = Square().shift(UP)
        triangle = Triangle().shift(RIGHT)
        circle.set_stroke(color=GREEN, width=20)
        square.set_fill(YELLOW, opacity=1.0)
        triangle.set_fill(PINK, opacity=0.5)
        self.add(triangle, square, circle)
        self.wait(1)

这里唯一的区别(除了场景名称)是将移动对象添加到场景的顺序。在MobjectStyleing中,我们将它们添加为add(circle, square, triangle),而在MobjectZorder中,我们添加它们为add(triangle, square, circle)

如您所见,add()参数的顺序决定了mobject在屏幕上的显示顺序,最左边的参数放在后面。

动画,Animations

manim的核心是动画。通常,可以通过调用play()方法将动画添加到场景中。

from manim import *
class SomeAnimations(Scene):
    def construct(self):
        square = Square()
        # some animations display mobjects, ...
        self.play(FadeIn(square))
        # ... some move or rotate mobjects around...
        self.play(Rotate(square, PI/4))
        # some animations remove mobjects from the screen
        self.play(FadeOut(square))
        self.wait(1)

简而言之,动画是在两个运动对象之间插值的过程。例如,FadeIn(square)以完全透明的square版本开始,以完全不透明的版本结束,通过逐渐增加不透明度在它们之间进行插值。淡出FadeOut的工作方式相反:它从完全不透明插值到完全透明。另一个例子是,Rotate从作为参数传递给它的mobject开始,以相同的对象结束,但旋转了一定量,这次是插值mobject的角度,而不是其不透明度。

动画制作方法,Animating methods

mobject的任何可以更改的属性都可以设置动画。事实上,通过使用animate(),任何更改mobject属性的方法都可以用作动画。

from manim import *
class AnimateExample(Scene):
    def construct(self):
        square = Square().set_fill(RED, opacity=1.0)
        self.add(square)
        # animate the change of color
        self.play(square.animate.set_fill(WHITE))
        self.wait(1)
        # animate the change of position and the rotation at the same time
        self.play(square.animate.shift(UP).rotate(PI / 3))
        self.wait(1)

参考:Animation

·animate()·是所有mobjects的属性,用于为后面的方法设置动画。例如,square.set_fill(WHITE)设置正方形的填充颜色,而square.animate.set_fill(WHITE)设置此动作的动画。

动画运行时,Animation run time

默认情况下,传递给play()的任何动画都只持续一秒钟。使用run_time参数控制持续时间

from manim import *
class RunTime(Scene):
    def construct(self):
        square = Square()
        self.add(square)
        self.play(square.animate.shift(UP), run_time=3)
        self.wait(1)

创建自定义动画,Creating a custom animation

尽管Manim有许多内置动画,但有时需要从一个Mobject的一个状态平滑地设置到另一个状态。如果您发现自己处于这种情况,则可以定义自己的自定义动画。首先扩展Animation类并覆盖其interpolate_mobject()interpolate_mobject()方法将alpha作为参数接收,该参数从0开始,并在整个动画中更改。所以,你只需要操纵自己。根据其interpolate_mobject()方法中的alpha值,在动画内部移动对象。然后,您可以获得动画Animation的所有好处,例如为不同的运行时间播放动画或使用不同的速率函数。

假设您从一个数字开始,并希望创建将其转换Transform为目标数字的变换动画。您可以使用FadeTransform进行转换,它将淡出起始编号,淡入目标编号。但是,当我们考虑将一个数字转换为另一个数字时,一种直观的方法是平滑地增加或减少它。Manim有一个功能,允许您通过定义自己的自定义动画来自定义此行为。

您可以先创建自己的Count类来扩展动画Animation。该类可以有一个具有三个参数的构造函数,一个小数DecimalNumber Mobject、start和end。构造函数将小数DecimalNumberMobject传递给超级构造函数(在本例中为动画构造函数),并设置start和end。

你需要做的唯一一件事就是定义你希望它在动画的每一步中的表现。Manim根据视频的帧速率、速率函数和播放的动画的运行时间,在interpolate_mobject()方法中为您提供alpha值。alpha参数的值介于0和1之间,表示当前播放动画的步长。例如,0表示动画开始,0.5表示动画完成一半,1表示动画结束。

Count动画的情况下,您只需找出一种方法来确定在给定alpha值下显示的数字,然后在Count动漫的interpolate_mobject()方法中设置该值。假设从50开始递增,直到动画结束时小数DecimalNumber达到100。

通常,从起始数字开始,只添加要根据alpha值递增的值的一部分。因此,计算每个步骤中要显示的数字的逻辑将是50+alpha*(100-50)。设置小数DecimalNumber的计算值后,就完成了。

定义了计数Count动画后,可以在场景Scene中以任意速率函数播放任意小数DecimalNumber的任意持续时间。

from manim import *
class Count(Animation):
    def __init__(self, number: DecimalNumber, start: float, end: float, **kwargs) -> None:
        # Pass number as the mobject of the animation
        super().__init__(number,  **kwargs)
        # Set start and end
        self.start = start
        self.end = end
    def interpolate_mobject(self, alpha: float) -> None:
        # Set value of DecimalNumber according to alpha
        value = self.start + (alpha \        self.mobject.set_value(value)

class CountingScene(Scene):
    def construct(self):
        # Create Decimal Number and add it to scene
        number = DecimalNumber().set_color(WHITE).scale(5)
        # Add an updater to keep the DecimalNumber centered as its value changes
        number.add_updater(lambda number: number.move_to(ORIGIN))
        self.add(number)
        self.wait()
        # Play the Count Animation to count from 0 to 100 in 4 seconds
        self.play(Count(number, 0, 100), run_time=4, rate_func=linear)
        self.wait()

参考:
Animation
DecimalNumber
interpolate_mobject()
play()

使用移动对象的坐标,Using coordinates of a mobject

移动对象包含定义其边界的点。这些点可以分别用于将其他mobject相互添加,例如通过get_center()get_top()get_start()等方法。以下是一些重要坐标的示例:
示例:MobjectExample

from manim import *
class MobjectExample(Scene):
    def construct(self):
        p1= [-1,-1,0]
        p2= [1,-1,0]
        p3= [1,1,0]
        p4= [-1,1,0]
        a = Line(p1,p2).append_points(Line(p2,p3).points).append_points(Line(p3,p4).points)
        point_start= a.get_start()
        point_end  = a.get_end()
        point_center = a.get_center()
        self.add(Text(f"a.get_start() = {np.round(point_start,2).tolist()}", font_size=24).to_edge(UR).set_color(YELLOW))
        self.add(Text(f"a.get_end() = {np.round(point_end,2).tolist()}", font_size=24).next_to(self.mobjects[-1],DOWN).set_color(RED))
        self.add(Text(f"a.get_center() = {np.round(point_center,2).tolist()}", font_size=24).next_to(self.mobjects[-1],DOWN).set_color(BLUE))
        self.add(Dot(a.get_start()).set_color(YELLOW).scale(2))
        self.add(Dot(a.get_end()).set_color(RED).scale(2))
        self.add(Dot(a.get_top()).set_color(GREEN_A).scale(2))
        self.add(Dot(a.get_bottom()).set_color(GREEN_D).scale(2))
        self.add(Dot(a.get_center()).set_color(BLUE).scale(2))
        self.add(Dot(a.point_from_proportion(0.5)).set_color(ORANGE).scale(2))
        self.add(*[Dot(x) for x in a.points])
        self.add(a)

将移动对象转换为其他移动对象,Transforming mobjects into other mobjects

也可以将一个mobject转换为另一个mob,如下所示:

from manim import *
class ExampleTransform(Scene):
    def construct(self):
        self.camera.background_color = WHITE
        m1 = Square().set_color(RED)
        m2 = Rectangle().set_color(RED).rotate(0.2)
        self.play(Transform(m1,m2))

变换函数将前一个mobject的点映射到下一个mob的点。这可能会导致奇怪的行为,例如,当一个对象的点按顺时针方向排列,而其他点按逆时针方向排列时。在这里,使用翻转功能并通过numpy的滚动功能重新定位点可能会有所帮助:

from manim import *
class ExampleRotation(Scene):
    def construct(self):
        self.camera.background_color = WHITE
        m1a = Square().set_color(RED).shift(LEFT)
        m1b = Circle().set_color(RED).shift(LEFT)
        m2a= Square().set_color(BLUE).shift(RIGHT)
        m2b= Circle().set_color(BLUE).shift(RIGHT)
        points = m2a.points
        points = np.roll(points, int(len(points)/4), axis=0)
        m2a.points = points
        self.play(Transform(m1a,m1b),Transform(m2a,m2b), run_time=1)

场景,Scenes

场景Scene类是manim的结缔组织。每个移动对象都必须添加added到要显示的场景中,或者从中删除removed以停止显示。每个动画都必须由一个场景播放played,并且没有动画发生的每个时间间隔都由对wait()的调用决定。视频的所有代码都必须包含在从场景派生的类的construct()方法中。最后,如果要同时渲染多个场景Scene,则单个文件可能包含多个场景Scene子类。

点击这里复制本文地址 以上内容来源于互联网,由爱搜问收集整理,如有侵权请联系站长,核实后立即删除,转载分享时注明本文地址!

支持Ctrl+Enter提交
qrcode

爱搜问 ©2021-2022 吉ICP备2022002266号 Aisouwen.COM.
站点地图1 站点地图2 站点地图3