Manim文档及源码笔记-CE教程BE03英文笔记速览版
Animation Deep Dive _ Mathematical Animations WITH EASE
前言
这次筛选到了Behackl博士的教程,逐步拆解,更为细腻~
参考视频在此【或请自行搜索上面的英文标题】;
本页中文版传送门【建设中】;
更新【new】:
- 8月23日
-
- 文首标注了“前言”与“正文”;
-
- 本系列暂告段落,升级了系列目录,放到了文尾;
- 后续更新备忘:文中“【建设中】”的内容;欢迎朋友们催更~
首先,国际通则:Just GET STARTED~ JUST DO IT~
然后,让我们行动起来~
注:
1、代码实践过程根据运行环境及笔记需要略有改动;
2、经过实践理解,加入了一些自己的注释;
3、常见问题及大概率解决方案:
- Python相关:注意缩进、冒号,中英文字符、大小写;
- Manim相关:安装与运行环境;
- Coding相关:检查拼写;
正文
Animation Keyword Arguments
- moject- every animation is tied to a mobject
- run_time- the animaion’s play duration
- rate_func- a function r: [0, 1] →[0,1] controlling animation progression
- and some more technical, less relevant ones
%%manim -v WARNING -qm BasicAnimations
from manim import *
from colour import Color
class BasicAnimations(Scene):
def construct(self):
polys=VGroup(
*[RegularPolygon(5,radius=1,fill_opacity=0.5,
color=Color(hue=j/5,saturation=1,luminance=0.5))for j in range(5)]
).arrange(RIGHT)
self.play(DrawBorderThenFill(polys),run_time=2)
self.play(
Rotate(polys[0],PI,rate_func=lambda t:t), # rate_func=linear
Rotate(polys[1],PI,rate_func=smooth), # default behavior
Rotate(polys[2],PI,rate_func=lambda t:np.sin(t*PI)), #
Rotate(polys[3],PI,rate_func=there_and_back), #
Rotate(polys[4],PI,rate_func=lambda t:1-abs(1-2*t)), #
run_time=2
)
self.wait()
BasicAnimations
看一下控制因子的图像,顺便练习一下第一章的作图:
Manim文档及源码笔记-CE教程BE01英文笔记速览版
%%manim -v WARNING -qm polys
class polys(Scene):
def construct(self):
ax=Axes(x_range=(-10,10),y_range=(-10,10))
curve0=ax.plot(lambda t: t, color=RED)
curve2=ax.plot(lambda t: np.sin(t*PI), color=GREEN)
curve4=ax.plot(lambda t: 1-abs(1-2*t), color=PURPLE)
self.add(ax, curve0,curve2,curve4)
Basic Mechanisms of Scene.play
- Multiple animations can be passed as positional arguments:
self.play(anim1, anim2, …) - Passing animation keyword arguments propagates them to all animations:
self.play(anim1, anim2, run_time=3) - Manim will not error if you try to pass conflicting animations, it cannot combine different animations for you!
self.play(Rotate( s, PI), Rotate( s, -PI))
%%manim -v WARNING -qm ConflictingAnimations
class ConflictingAnimations(Scene):
def construct(self):
s=Square()
self.add(s)
self.play(Rotate(s,PI),Rotate(s,-PI),run_time=3)
ConflictingAnimations
Overlapping Animations
- Useful for grouping animations: AnimationGroup
- Like Group or VGroup, but for animations
- Keyword argument lag_ratio, number between 0 and 1,
- controls whether animations are played simultaneously (0), successively (1), or anything in between
- Finer control possible by editing list in anims_with_timings attribute
%%manim -v WARNING -qm LaggingGroup
class LaggingGroup(Scene):
def construct(self):
squares=VGroup(*[Square(color=Color(hue=j/20,saturation=1,luminance=0.5),
fill_opacity=0.5) for j in range(20)]).arrange_in_grid(4,5).scale(0.75)
self.play(AnimationGroup(*[FadeIn(s) for s in squares],lag_ratio=1))
LaggingGroup
Part 2 Animations from Method calls and Functions
The .animate syntax
- Problem: mobj.method() usually returns a mobject, mobjects cannot be passed to play
- Solution: Mobjects have a .animate property which can bulid animations!
def construct(self):
s=Square()
self.play(s.animate.shift(RIGHT)) # animation of shift by RIGHT
self.play(s.animate(run_time=2).scale(3)) # supports animation keywords
self.play(s.animate.scale(1/2).shift(2*LEFT)) # supports chaining
Note:
.animate is not aware of how your mobjects change knows only start& end and interpolates linearly between them!
%%manim -v WARNING -qm AnimateSyntax
class AnimateSyntax(Scene):
def construct(self):
s=Square(color=GREEN,fill_opacity=0.5)
c=Circle(color=RED,fill_opacity=0.5)
self.add(s,c)
self.play(s.animate.shift(UP),c.animate.shift(DOWN))
AnimateSyntax
%%manim -v WARNING -qm AnimateSyntax1
class AnimateSyntax1(Scene):
def construct(self):
s=Square(color=GREEN,fill_opacity=0.5)
c=Circle(color=RED,fill_opacity=0.5)
self.add(s,c)
self.play(s.animate.shift(UP),c.animate.shift(DOWN))
self.play(VGroup(s,c).animate.arrange(RIGHT,buff=1))
AnimateSyntax1
%%manim -v WARNING -qm AnimateSyntax2
class AnimateSyntax2(Scene):
def construct(self):
s=Square(color=GREEN,fill_opacity=0.5)
c=Circle(color=RED,fill_opacity=0.5)
self.add(s,c)
self.play(s.animate.shift(UP),c.animate.shift(DOWN))
self.play(VGroup(s,c).animate.arrange(RIGHT,buff=1))
self.play(c.animate(rate_func=linear).shift(RIGHT).scale(2))
AnimateSyntax2
%%manim -v WARNING -qm AnimateProblem
class AnimateProblem(Scene):
def construct(self):
left_square=Square()
right_square=Square()
VGroup(left_square,right_square).arrange(RIGHT,buff=1)
self.add(left_square,right_square)
self.play(left_square.animate.rotate(PI),
Rotate(right_square,PI),run_time=2)
self.wait()
AnimateProblem
Related animations: MoveToTarget and Restore
- General philosophy: create a copy of a mobject, modify it however you like, then transform between original mobject and modified copy
- MoveToTarget: call mob.generate_target(), then modify mob.target- animate with MoveToTarget(mob)
- Restore: call mob.save_state(), then keep modifying mob- animate return to saved state with Restore(mob)
%%manim -v WARNING -qm Animationchanisms
class Animationchanisms(Scene):
def construct(self):
c=Circle()
c.generate_target()
c.target.set_fill(color=GREEN,opacity=0.5)
c.target.shift(2*RIGHT+UP).scale(0.5)
self.add(c)
self.wait()
self.play(MoveToTarget(c))
Animationchanisms
%%manim -v WARNING -qm Animationchanisms1
class Animationchanisms1(Scene):
def construct(self):
c=Circle()
c.generate_target()
c.target.set_fill(color=GREEN,opacity=0.5)
c.target.shift(2*RIGHT+UP).scale(0.5)
self.add(c)
self.wait()
self.play(MoveToTarget(c))
s=Square()
s.save_state()
self.play(FadeIn(s))
self.play(s.animate.set_color(PURPLE).set_opacity(0.5).shift(2*LEFT).scale(3))
self.play(s.animate.shift(5*DOWN).rotate(PI/4))
self.wait()
self.play(Restore(s),run_time=2)
self.wait()
Animationchanisms1
Custom Animations via Functions
- Abstraction: an animation is a function mapping( mobject, completion ratio) to a mobject
def move_somewhere(mobj,alpha):
mobj.move_to(alpha*RIGHT+alpha**2*2*UP)
- Given such a function, UpdateFromAlphaFunc constructs the corresponding animation!
- Tip: store initial state in custom mobject attribute to allow for more flexibility!
mobj.initial_position=mobj.get_center() … then mobj.initial_position is available in the animation function- or write custom animation
%%manim -v WARNING -qm SimpleCustomAnimation
from colour import Color
class SimpleCustomAnimation(Scene):
def construct(self):
def spiral_out(mobject,t):
radius=4*t
angle=2*5*2*PI
mobject.move_to(radius*(np.cos(angle)*RIGHT+np.sin(angle)*UP))
mobject.set_color(Color(hue=t,saturation=1,luminance=0.5))
mobject.set_opacity(1-t)
d=Dot(color=WHITE)
self.add(d)
self.play(UpdateFromAlphaFunc(d,spiral_out,run_time=3))
SimpleCustomAnimation
Part 3 Anatomy of Animations
Animation methods called in a lifecycle
- begin() - prepares first animation frame; stores mobject copy in self.starting_mobject
- interpolate_mobject(alpha) - brings self.mobject to the state of α \alpha α% of animation completed; default: delegates to submobjects
- interpolate_submobject(sub, sub_start, alpha) - same as above, but for a specific submobjedt( in argument)
- finish() - finishes animation, produces last frame
- clean_up_from_scend(scene)- all remaining mobject and scene cleanup( e.g., removing mobjects)
Implementing your own Animatin class
- Basically: inherit from Animation, override
interpolate_mobject or interpolate_submobject - Compare how other Animations in the library are
- Good luck~
%%manim -v WARNING -qm CustomAnimationExample
from colour import Color
class Disperse(Animation):
def __init__(self,mobject,dot_radius=0.05,dot_number=100, **kwargs):
super().__init__(mobject,**kwargs)
self.dot_radius=dot_radius
self.dot_number=dot_number
def begin(self):
dots=VGroup(
*[Dot(radius=self.dot_radius).move_to(self.mobject.point_from_proportion(p))
for p in np.linspace(0,1,self.dot_number)]
)
for dot in dots:
dot.initial_position=dot.get_center()
dot.shift_vector=2*(dot.get_center()-self.mobject.get_center())
dots.set_opacity(0)
self.mobject.add(dots)
self.dots=dots
super().begin()
def clean_up_from_scene(self,scene:"Scene") -> None:
super().clean_up_from_scene(scene)
scene.remove(self.dots)
def interpolate_mobject(self,alpha):
if alpha<=0.5:
self.mobject.set_opacity(1-2*alpha,family=False)
self.dots.set_opacity(2*alpha)
else:
self.mobject.set_opacity(0)
self.dots.set_opacity(2*(1-alpha))
for dot in self.dots:
dot.move_to(dot.initial_position+2*(alpha-0.5)*dot.shift_vector)
class CustomAnimationExample(Scene):
def construct(self):
st=Star(color=YELLOW, fill_opacity=1).scale(3)
self.add(st)
self.wait()
self.play(Disperse(st,dot_number=200,run_time=4))
CustomAnimationExample
本系列目录【new】
Manim文档及源码笔记-CE教程BE01英文笔记速览版
Manim文档及源码笔记-CE教程BE02英文笔记速览版
Manim文档及源码笔记-CE教程BE03英文笔记速览版【本文】
Manim文档及源码笔记-CE教程BE04英文笔记速览版
Manim文档及源码笔记-CE教程BE05英文笔记速览版