相信很多人都知道3Blue1Brown,这是一个由斯坦福大学的数学系学生Grant Sanderson 创建的YouTube 频道。该频道从独特的视觉角度解说高等数学,内容包括线性代数微积分神经网络黎曼猜想傅里叶变换以及四元数等等。

本人通过该视频频道获得了很多启发,同时也对其精良的视频制作技术产生了浓厚的兴趣。偶然的机会,得知其在Github上有专门开设了一个动画制作引擎:manim,地址在:

https://github.com/leekunhwee/manimhttps://github.com/leekunhwee/manim

3blue1brown制作的数学解析动画观点高,起点低,把非常复杂的数学原理讲述的非常生动形象,让没有太多数学基础的人也能够感受到数学的美感,今天我们就在UBUNTU18.04上,尝试安装一下3B1B的动画制作环境,说不定某天会用到。

安装环境:

  • Ubuntu 18.04.5 LTS
  • Anaconda Python 3.8.5

安装过程:

下载manim

git clone https://github.com/leekunhwee/manim.git

安装依赖,ffmpeg

这一步通过apt-get安装FFMPEG预编译包或者从源码开始编译都可以,从源码安装可以参考

ubuntu18.04编译FFMPEG_tugouxp的专栏-CSDN博客https://blog.csdn.net/tugouxp/article/details/115491843安装依赖,miktex

Getting MiKTeX

下载地址页面有安装说明,按照说明安装即可。

最后执行miktexsetup finish

验证安装依赖包的版本信息

安装python依赖包:

python -m pip install -r requirements.txt

(base) caozilong@caozilong-Vostro-3268:~/mimal/manim$ python -m pip install -r requirements.txt
Ignoring pycairo: markers 'sys_platform == "win32"' don't match your environment
Ignoring pyreadline: markers 'sys_platform == "win32"' don't match your environment
Collecting argparseDownloading argparse-1.4.0-py2.py3-none-any.whl (23 kB)
Collecting colourDownloading colour-0.1.5-py2.py3-none-any.whl (23 kB)
Requirement already satisfied: numpy in /home/caozilong/anaconda3/lib/python3.8/site-packages (from -r requirements.txt (line 3)) (1.19.2)
Requirement already satisfied: Pillow in /home/caozilong/anaconda3/lib/python3.8/site-packages (from -r requirements.txt (line 4)) (8.0.1)
Collecting progressbarDownloading progressbar-2.5.tar.gz (10 kB)
Requirement already satisfied: scipy in /home/caozilong/anaconda3/lib/python3.8/site-packages (from -r requirements.txt (line 6)) (1.5.2)
Requirement already satisfied: tqdm in /home/caozilong/anaconda3/lib/python3.8/site-packages (from -r requirements.txt (line 7)) (4.50.2)
Requirement already satisfied: opencv-python in /home/caozilong/anaconda3/lib/python3.8/site-packages (from -r requirements.txt (line 8)) (4.5.3.56)
Collecting pycairo==1.17.1Downloading pycairo-1.17.1.tar.gz (194 kB)|████████████████████████████████| 194 kB 453 kB/s
Collecting pydub==0.23.0Downloading pydub-0.23.0-py2.py3-none-any.whl (28 kB)
Building wheels for collected packages: progressbar, pycairoBuilding wheel for progressbar (setup.py) ... doneCreated wheel for progressbar: filename=progressbar-2.5-py3-none-any.whl size=12074 sha256=11707eec90e81c753d7d715ef81831bd0c497ff6a0322fd68fda3ce62789b021Stored in directory: /home/caozilong/.cache/pip/wheels/2c/67/ed/d84123843c937d7e7f5ba88a270d11036473144143355e2747Building wheel for pycairo (setup.py) ... doneCreated wheel for pycairo: filename=pycairo-1.17.1-cp38-cp38-linux_x86_64.whl size=258204 sha256=a72ee395c86fa713bad8c6df805187eaee0019fb47831de54b3bcbceeb37987fStored in directory: /home/caozilong/.cache/pip/wheels/92/a5/7c/b88429bb8e47045f531dd3b5dedf5c4202c2750b502c29fb29
Successfully built progressbar pycairo
Installing collected packages: argparse, colour, progressbar, pycairo, pydub
Successfully installed argparse-1.4.0 colour-0.1.5 progressbar-2.5 pycairo-1.17.1 pydub-0.23.0
(base) caozilong@caozilong-Vostro-3268:~/mimal/manim$ 

conda install pycairo 

(base) caozilong@caozilong-Vostro-3268:~/mimal/manim$ conda install pycairo
Collecting package metadata (current_repodata.json): done
Solving environment: -
The environment is inconsistent, please check the package plan carefully
The following packages are causing the inconsistency:- defaults/linux-64::anaconda==2020.11=py38_0- defaults/linux-64::spyder==4.1.5=py38_0- defaults/linux-64::astroid==2.4.2=py38_0- defaults/noarch::python-language-server==0.35.1=py_0- defaults/linux-64::pylint==2.6.0=py38\
done## Package Plan ##environment location: /home/caozilong/anaconda3added / updated specs:- pycairoThe following packages will be downloaded:package                    |            build---------------------------|-----------------_anaconda_depends-2020.07  |           py38_0           6 KBanaconda-custom            |           py38_1          35 KBastroid-2.5                |   py38h06a4308_1         284 KBca-certificates-2021.7.5   |       h06a4308_1         113 KBcertifi-2021.5.30          |   py38h06a4308_0         138 KBconda-4.10.3               |   py38h06a4308_0         2.9 MBlibllvm9-9.0.1             |       h4a3c616_1        21.0 MBopenssl-1.1.1l             |       h7f8727e_0         2.5 MBpycairo-1.19.1             |   py38h708ec4a_0          73 KBsnappy-1.1.8               |       he6710b0_0          40 KBwrapt-1.12.1               |   py38h7b6447c_1          50 KB------------------------------------------------------------Total:        27.1 MBThe following NEW packages will be INSTALLED:_anaconda_depends  pkgs/main/linux-64::_anaconda_depends-2020.07-py38_0h5py               pkgs/main/linux-64::h5py-2.10.0-py38h7918eee_0libllvm9           pkgs/main/linux-64::libllvm9-9.0.1-h4a3c616_1pycairo            pkgs/main/linux-64::pycairo-1.19.1-py38h708ec4a_0snappy             pkgs/main/linux-64::snappy-1.1.8-he6710b0_0wrapt              pkgs/main/linux-64::wrapt-1.12.1-py38h7b6447c_1The following packages will be UPDATED:astroid                                      2.4.2-py38_0 --> 2.5-py38h06a4308_1ca-certificates                              2020.10.14-0 --> 2021.7.5-h06a4308_1certifi            pkgs/main/noarch::certifi-2020.6.20-p~ --> pkgs/main/linux-64::certifi-2021.5.30-py38h06a4308_0conda                                4.9.2-py38h06a4308_0 --> 4.10.3-py38h06a4308_0openssl                                 1.1.1h-h7b6447c_0 --> 1.1.1l-h7f8727e_0The following packages will be DOWNGRADED:anaconda                                   2020.11-py38_0 --> custom-py38_1Proceed ([y]/n)? yDownloading and Extracting Packages
anaconda-custom      | 35 KB     | ##################################### | 100%
openssl-1.1.1l       | 2.5 MB    | ##################################### | 100%
wrapt-1.12.1         | 50 KB     | ##################################### | 100%
certifi-2021.5.30    | 138 KB    | ##################################### | 100%
pycairo-1.19.1       | 73 KB     | ##################################### | 100%
ca-certificates-2021 | 113 KB    | ##################################### | 100%
astroid-2.5          | 284 KB    | ##################################### | 100%
_anaconda_depends-20 | 6 KB      | ##################################### | 100%
snappy-1.1.8         | 40 KB     | ##################################### | 100%
libllvm9-9.0.1       | 21.0 MB   | ##################################### | 100%
conda-4.10.3         | 2.9 MB    | ##################################### | 100%
Preparing transaction: done
Verifying transaction: done
Executing transaction: done
(base) caozilong@caozilong-Vostro-3268:~/mimal/manim$

验证用例:

python -m manim example_scenes.py SquareToCircle -pl

python -m manim example_scenes.py WarpSquare -pl

以一个例子说明3B1B的动画制作原理

from manimlib.imports import *
import os
import pyclbrclass Shapes(Scene):#A few simple shapes#Python 2.7 version runs in Python 3.7 without changesdef construct(self):circle = Circle()square = Square()line=Line(np.array([3,0,0]),np.array([5,0,0]))triangle=Polygon(np.array([0,0,0]),np.array([1,1,0]),np.array([1,-1,0]))self.play(ShowCreation(circle))self.play(FadeOut(circle))self.play(GrowFromCenter(square))self.play(Transform(square,triangle))self.add(line)class MoreShapes(Scene):#A few more simple shapes#2.7 version runs in 3.7 without any changes#Note: I fixed my 'play command not found' issue by installing soxdef construct(self):circle = Circle(color=PURPLE_A)square = Square(fill_color=GOLD_B, fill_opacity=1, color=GOLD_A)square.move_to(UP+LEFT)circle.surround(square)rectangle = Rectangle(height=2, width=3)ellipse=Ellipse(width=3, height=1, color=RED)ellipse.shift(2*DOWN+2*RIGHT)pointer = CurvedArrow(2*RIGHT,5*RIGHT,color=MAROON_C)arrow = Arrow(LEFT,UP)arrow.next_to(circle,DOWN+LEFT)rectangle.next_to(arrow,DOWN+LEFT)ring=Annulus(inner_radius=.5, outer_radius=1, color=BLUE)ring.next_to(ellipse, RIGHT)self.add(pointer)self.play(FadeIn(square))self.play(Rotating(square),FadeIn(circle))self.play(GrowArrow(arrow))self.play(GrowFromCenter(rectangle), GrowFromCenter(ellipse), GrowFromCenter(ring))class MovingShapes(Scene):#Show the difference between .shift() and .move_todef construct(self):circle=Circle(color=TEAL_A)circle.move_to(LEFT)square=Circle()square.move_to(LEFT+3*DOWN)self.play(GrowFromCenter(circle), GrowFromCenter(square), rate=5)self.play(ApplyMethod(circle.move_to,RIGHT), ApplyMethod(square.shift,RIGHT))self.play(ApplyMethod(circle.move_to,RIGHT+UP), ApplyMethod(square.shift,RIGHT+UP))self.play(ApplyMethod(circle.move_to,LEFT+UP), ApplyMethod(square.shift,LEFT+UP))class AddingText(Scene):#Adding text on the screendef construct(self):my_first_text=TextMobject("Writing with manim is fun")second_line=TextMobject("and easy to do!")second_line.next_to(my_first_text,DOWN)third_line=TextMobject("for me and you!")third_line.next_to(my_first_text,DOWN)self.add(my_first_text, second_line)self.wait(2)self.play(Transform(second_line,third_line))self.wait(2)second_line.shift(3*DOWN)self.play(ApplyMethod(my_first_text.shift,3*UP))###Try uncommenting the following####self.play(ApplyMethod(second_line.move_to, LEFT_SIDE-2*LEFT))#self.play(ApplyMethod(my_first_text.next_to,second_line))class AddingMoreText(Scene):#Playing around with text propertiesdef construct(self):quote = TextMobject("Imagination is more important than knowledge")quote.set_color(RED)quote.to_edge(UP)quote2 = TextMobject("A person who never made a mistake never tried anything new")quote2.set_color(YELLOW)author=TextMobject("-Albert Einstein")author.scale(0.75)author.next_to(quote.get_corner(DOWN+RIGHT),DOWN)self.add(quote)self.add(author)self.wait(2)self.play(Transform(quote,quote2),ApplyMethod(author.move_to,quote2.get_corner(DOWN+RIGHT)+DOWN+2*LEFT))self.play(ApplyMethod(author.scale,1.5))author.match_color(quote2)self.play(FadeOut(quote))class RotateAndHighlight(Scene):#Rotation of text and highlighting with surrounding geometriesdef construct(self):square=Square(side_length=5,fill_color=YELLOW, fill_opacity=1)label=TextMobject("Text at an angle")label.bg=BackgroundRectangle(label,fill_opacity=1)label_group=VGroup(label.bg,label)  #Order matterslabel_group.rotate(TAU/8)label2=TextMobject("Boxed text",color=BLACK)label2.bg=SurroundingRectangle(label2,color=BLUE,fill_color=RED, fill_opacity=.5)label2_group=VGroup(label2,label2.bg)label2_group.next_to(label_group,DOWN)label3=TextMobject("Rainbow")label3.scale(2)label3.set_color_by_gradient(RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE)label3.to_edge(DOWN)self.add(square)self.play(FadeIn(label_group))self.play(FadeIn(label2_group))self.play(FadeIn(label3))class BasicEquations(Scene):#A short script showing how to use Latex commandsdef construct(self):eq1=TextMobject("$\\vec{X}_0 \\cdot \\vec{Y}_1 = 3$")eq1.shift(2*UP)eq2=TexMobject(r"\vec{F}_{net} = \sum_i \vec{F}_i")eq2.shift(2*DOWN)self.play(Write(eq1))self.play(Write(eq2))class ColoringEquations(Scene):#Grouping and coloring parts of equationsdef construct(self):line1=TexMobject(r"\text{The vector } \vec{F}_{net} \text{ is the net }",r"\text{force }",r"\text{on object of mass }")line1.set_color_by_tex("force", BLUE)line2=TexMobject("m", "\\text{ and acceleration }", "\\vec{a}", ".  ")line2.set_color_by_tex_to_color_map({"m": YELLOW,"{a}": RED})sentence=VGroup(line1,line2)sentence.arrange_submobjects(DOWN, buff=MED_LARGE_BUFF)self.play(Write(sentence))class UsingBraces(Scene):#Using braces to group text togetherdef construct(self):eq1A = TextMobject("4x + 3y")eq1B = TextMobject("=")eq1C = TextMobject("0")eq2A = TextMobject("5x -2y")eq2B = TextMobject("=")eq2C = TextMobject("3")eq1B.next_to(eq1A,RIGHT)eq1C.next_to(eq1B,RIGHT)eq2A.shift(DOWN)eq2B.shift(DOWN)eq2C.shift(DOWN)eq2A.align_to(eq1A,LEFT)eq2B.align_to(eq1B,LEFT)eq2C.align_to(eq1C,LEFT)eq_group=VGroup(eq1A,eq2A)braces=Brace(eq_group,LEFT)eq_text = braces.get_text("A pair of equations")self.add(eq1A, eq1B, eq1C)self.add(eq2A, eq2B, eq2C)self.play(GrowFromCenter(braces),Write(eq_text))class UsingBracesConcise(Scene):#A more concise block of code with all columns aligneddef construct(self):eq1_text=["4","x","+","3","y","=","0"]eq2_text=["5","x","-","2","y","=","3"]eq1_mob=TexMobject(*eq1_text)eq2_mob=TexMobject(*eq2_text)eq1_mob.set_color_by_tex_to_color_map({"x":RED_B,"y":GREEN_C})eq2_mob.set_color_by_tex_to_color_map({"x":RED_B,"y":GREEN_C})for i,item in enumerate(eq2_mob):item.align_to(eq1_mob[i],LEFT)eq1=VGroup(*eq1_mob)eq2=VGroup(*eq2_mob)eq2.shift(DOWN)eq_group=VGroup(eq1,eq2)braces=Brace(eq_group,LEFT)eq_text = braces.get_text("A pair of equations")self.play(Write(eq1),Write(eq2))self.play(GrowFromCenter(braces),Write(eq_text))class PlotFunctions(GraphScene):CONFIG = {"x_min" : -10,"x_max" : 10.3,"y_min" : -1.5,"y_max" : 1.5,"graph_origin" : ORIGIN ,"function_color" : RED ,"axes_color" : GREEN,"x_labeled_nums" :range(-10,12,2),}   def construct(self):self.setup_axes(animate=True)func_graph=self.get_graph(self.func_to_graph,self.function_color)func_graph2=self.get_graph(self.func_to_graph2)vert_line = self.get_vertical_line_to_graph(TAU,func_graph,color=YELLOW)graph_lab = self.get_graph_label(func_graph, label = "\\cos(x)")graph_lab2=self.get_graph_label(func_graph2,label = "\\sin(x)", x_val=-10, direction=UP/2)two_pi = TexMobject("x = 2 \\pi")label_coord = self.input_to_graph_point(TAU,func_graph)two_pi.next_to(label_coord,RIGHT+UP)self.play(ShowCreation(func_graph),ShowCreation(func_graph2))self.play(ShowCreation(vert_line), ShowCreation(graph_lab), ShowCreation(graph_lab2),ShowCreation(two_pi))def func_to_graph(self,x):return np.cos(x)def func_to_graph2(self,x):return np.sin(x)class ExampleApproximation(GraphScene):CONFIG = {"function" : lambda x : np.cos(x), "function_color" : BLUE,"taylor" : [lambda x: 1, lambda x: 1-x**2/2, lambda x: 1-x**2/math.factorial(2)+x**4/math.factorial(4), lambda x: 1-x**2/2+x**4/math.factorial(4)-x**6/math.factorial(6),lambda x: 1-x**2/math.factorial(2)+x**4/math.factorial(4)-x**6/math.factorial(6)+x**8/math.factorial(8), lambda x: 1-x**2/math.factorial(2)+x**4/math.factorial(4)-x**6/math.factorial(6)+x**8/math.factorial(8) - x**10/math.factorial(10)],"center_point" : 0,"approximation_color" : GREEN,"x_min" : -10,"x_max" : 10,"y_min" : -1,"y_max" : 1,"graph_origin" : ORIGIN ,"x_labeled_nums" :range(-10,12,2),}def construct(self):self.setup_axes(animate=True)func_graph = self.get_graph(self.function,self.function_color,)approx_graphs = [self.get_graph(f,self.approximation_color)for f in self.taylor]term_num = [TexMobject("n = " + str(n),aligned_edge=TOP)for n in range(0,8)]#[t.to_edge(BOTTOM,buff=SMALL_BUFF) for t in term_num]#term = TexMobject("")#term.to_edge(BOTTOM,buff=SMALL_BUFF)term = VectorizedPoint(3*DOWN)approx_graph = VectorizedPoint(self.input_to_graph_point(self.center_point, func_graph))self.play(ShowCreation(func_graph),)for n,graph in enumerate(approx_graphs):self.play(Transform(approx_graph, graph, run_time = 2),Transform(term,term_num[n]))self.wait()class DrawAnAxis(Scene):CONFIG = { "plane_kwargs" : { "x_line_frequency" : 2,"y_line_frequency" :2}}def construct(self):my_plane = NumberPlane(**self.plane_kwargs)my_plane.add(my_plane.get_axis_labels())self.add(my_plane)#self.wait()class SimpleField(Scene):CONFIG = {"plane_kwargs" : {"color" : RED},}def construct(self):plane = NumberPlane(**self.plane_kwargs) #Create axes and gridplane.add(plane.get_axis_labels())  #add x and y labelself.add(plane)  #Place grid on screenpoints = [x*RIGHT+y*UPfor x in np.arange(-5,5,1)for y in np.arange(-5,5,1)]     #List of vectors pointing to each grid pointvec_field = []  #Empty list to use in for loopfor point in points:field = 0.5*RIGHT + 0.5*UP   #Constant field up and to rightresult = Vector(field).shift(point)   #Create vector and shift it to grid pointvec_field.append(result)   #Append to listdraw_field = VGroup(*vec_field)   #Pass list of vectors to create a VGroupself.play(ShowCreation(draw_field))   #Draw VGroup on screenclass FieldWithAxes(Scene):CONFIG = {"plane_kwargs" : {"color" : RED_B},"point_charge_loc" : 0.5*RIGHT-1.5*UP,}def construct(self):plane = NumberPlane(**self.plane_kwargs)#plane.main_lines.fade(.9)  #doesn't work in most recent commitplane.add(plane.get_axis_labels())self.add(plane)field = VGroup(*[self.calc_field(x*RIGHT+y*UP)for x in np.arange(-9,9,1)for y in np.arange(-5,5,1)])self.play(ShowCreation(field))def calc_field(self,point):#This calculates the field at a single point.x,y = point[:2]Rx,Ry = self.point_charge_loc[:2]r = math.sqrt((x-Rx)**2 + (y-Ry)**2)efield = (point - self.point_charge_loc)/r**3#efield = np.array((-y,x,0))/math.sqrt(x**2+y**2)  #Try one of these two fields#efield = np.array(( -2*(y%2)+1 , -2*(x%2)+1 , 0 ))/3  #Try one of these two fieldsreturn Vector(efield).shift(point)class ExampleThreeD(ThreeDScene):CONFIG = {"plane_kwargs" : {"color" : RED_B},"point_charge_loc" : 0.5*RIGHT-1.5*UP,}def construct(self):plane = NumberPlane(**self.plane_kwargs)#plane.main_lines.fade(.9)   #Doesn't work in most recent commitplane.add(plane.get_axis_labels())self.add(plane)field2D = VGroup(*[self.calc_field2D(x*RIGHT+y*UP)for x in np.arange(-9,9,1)for y in np.arange(-5,5,1)])self.set_camera_orientation(phi=PI/3,gamma=PI/5)self.play(ShowCreation(field2D))self.wait()#self.move_camera(gamma=0,run_time=1)   #Doesn't work in most recent commit#self.move_camera(phi=3/4*PI, theta=-PI/2)   #Doesn't work in most recent commitself.begin_ambient_camera_rotation(rate=0.1)self.wait(6)def calc_field2D(self,point):x,y = point[:2]Rx,Ry = self.point_charge_loc[:2]r = math.sqrt((x-Rx)**2 + (y-Ry)**2)efield = (point - self.point_charge_loc)/r**3return Vector(efield).shift(point)class EFieldInThreeD(ThreeDScene):CONFIG = {"plane_kwargs" : {"color" : RED_B},"point_charge_loc" : 0.5*RIGHT-1.5*UP,}def construct(self):plane = NumberPlane(**self.plane_kwargs)#plane.main_lines.fade(.9)  #Doesn't work in most recent commitplane.add(plane.get_axis_labels())self.add(plane)field2D = VGroup(*[self.calc_field2D(x*RIGHT+y*UP)for x in np.arange(-9,9,1)for y in np.arange(-5,5,1)])field3D = VGroup(*[self.calc_field3D(x*RIGHT+y*UP+z*OUT)for x in np.arange(-9,9,1)for y in np.arange(-5,5,1)for z in np.arange(-5,5,1)])self.play(ShowCreation(field3D))self.wait()#self.move_camera(0.8*np.pi/2, -0.45*np.pi)   #Doesn't work in most recent commitself.begin_ambient_camera_rotation()self.wait(6)def calc_field2D(self,point):x,y = point[:2]Rx,Ry = self.point_charge_loc[:2]r = math.sqrt((x-Rx)**2 + (y-Ry)**2)efield = (point - self.point_charge_loc)/r**3return Vector(efield).shift(point)def calc_field3D(self,point):x,y,z = pointRx,Ry,Rz = self.point_charge_locr = math.sqrt((x-Rx)**2 + (y-Ry)**2+(z-Rz)**2)efield = (point - self.point_charge_loc)/r**3#efield = np.array((-y,x,z))/math.sqrt(x**2+y**2+z**2)return Vector(efield).shift(point)class MovingCharges(Scene):CONFIG = {"plane_kwargs" : {"color" : RED_B},"point_charge_loc" : 0.5*RIGHT-1.5*UP,}def construct(self):plane = NumberPlane(**self.plane_kwargs)#plane.main_lines.fade(.9)  #Doesn't work in most recent commitplane.add(plane.get_axis_labels())self.add(plane)field = VGroup(*[self.calc_field(x*RIGHT+y*UP)for x in np.arange(-9,9,1)for y in np.arange(-5,5,1)])self.field=fieldsource_charge = self.Positron().move_to(self.point_charge_loc)self.play(FadeIn(source_charge))self.play(ShowCreation(field))self.moving_charge()def calc_field(self,point):x,y = point[:2]Rx,Ry = self.point_charge_loc[:2]r = math.sqrt((x-Rx)**2 + (y-Ry)**2)efield = (point - self.point_charge_loc)/r**3return Vector(efield).shift(point)def moving_charge(self):numb_charges=4possible_points = [v.get_start() for v in self.field]points = random.sample(possible_points, numb_charges)particles = VGroup(*[self.Positron().move_to(point)for point in points])for particle in particles:particle.velocity = np.array((0,0,0))self.play(FadeIn(particles))self.moving_particles = particlesself.add_foreground_mobjects(self.moving_particles )self.always_continually_update = Trueself.wait(10)def field_at_point(self,point):x,y = point[:2]Rx,Ry = self.point_charge_loc[:2]r = math.sqrt((x-Rx)**2 + (y-Ry)**2)efield = (point - self.point_charge_loc)/r**3return efielddef continual_update(self, *args, **kwargs):if hasattr(self, "moving_particles"):dt = self.frame_durationfor p in self.moving_particles:accel = self.field_at_point(p.get_center())p.velocity = p.velocity + accel*dtp.shift(p.velocity*dt)class Positron(Circle):CONFIG = {"radius" : 0.2,"stroke_width" : 3,"color" : RED,"fill_color" : RED,"fill_opacity" : 0.5,}def __init__(self, **kwargs):Circle.__init__(self, **kwargs)plus = TexMobject("+")plus.scale(0.7)plus.move_to(self)self.add(plus)class FieldOfMovingCharge(Scene):CONFIG = {"plane_kwargs" : {"color" : RED_B},"point_charge_start_loc" : 5.5*LEFT-1.5*UP,}def construct(self):plane = NumberPlane(**self.plane_kwargs)#plane.main_lines.fade(.9)   #Doesn't work in most recent commitplane.add(plane.get_axis_labels())self.add(plane)field = VGroup(*[self.create_vect_field(self.point_charge_start_loc,x*RIGHT+y*UP)for x in np.arange(-9,9,1)for y in np.arange(-5,5,1)])self.field=fieldself.source_charge = self.Positron().move_to(self.point_charge_start_loc)self.source_charge.velocity = np.array((1,0,0))self.play(FadeIn(self.source_charge))self.play(ShowCreation(field))self.moving_charge()def create_vect_field(self,source_charge,observation_point):return Vector(self.calc_field(source_charge,observation_point)).shift(observation_point)def calc_field(self,source_point,observation_point):x,y,z = observation_pointRx,Ry,Rz = source_pointr = math.sqrt((x-Rx)**2 + (y-Ry)**2 + (z-Rz)**2)if r<0.0000001:   #Prevent divide by zeroefield = np.array((0,0,0))  else:efield = (observation_point - source_point)/r**3return efielddef moving_charge(self):numb_charges=3possible_points = [v.get_start() for v in self.field]points = random.sample(possible_points, numb_charges)particles = VGroup(self.source_charge, *[self.Positron().move_to(point)for point in points])for particle in particles[1:]:particle.velocity = np.array((0,0,0))self.play(FadeIn(particles[1:]))self.moving_particles = particlesself.add_foreground_mobjects(self.moving_particles )self.always_continually_update = Trueself.wait(10)def continual_update(self, *args, **kwargs):Scene.continual_update(self, *args, **kwargs)if hasattr(self, "moving_particles"):dt = self.frame_durationfor v in self.field:field_vect=np.zeros(3)for p in self.moving_particles:field_vect = field_vect + self.calc_field(p.get_center(), v.get_start())v.put_start_and_end_on(v.get_start(), field_vect+v.get_start())for p in self.moving_particles:accel = np.zeros(3)p.velocity = p.velocity + accel*dtp.shift(p.velocity*dt)class Positron(Circle):CONFIG = {"radius" : 0.2,"stroke_width" : 3,"color" : RED,"fill_color" : RED,"fill_opacity" : 0.5,}def __init__(self, **kwargs):Circle.__init__(self, **kwargs)plus = TexMobject("+")plus.scale(0.7)plus.move_to(self)self.add(plus)HEAD_INDEX   = 0
BODY_INDEX   = 1
ARMS_INDEX   = 2
LEGS_INDEX   = 3class StickMan(SVGMobject):CONFIG = {"color" : BLUE_E,"file_name_prefix": "stick_man","stroke_width" : 2,"stroke_color" : WHITE,"fill_opacity" : 1.0,"height" : 3,}def __init__(self, mode = "plain", **kwargs):digest_config(self, kwargs)self.mode = modeself.parts_named = Falsetry:svg_file = os.path.join(SVG_IMAGE_DIR,"%s_%s.svg" % (self.file_name_prefix, mode))SVGMobject.__init__(self, file_name=svg_file, **kwargs)except:warnings.warn("No %s design with mode %s" %(self.file_name_prefix, mode))svg_file = os.path.join(SVG_IMAGE_DIR,"stick_man_plain.svg",)SVGMobject.__init__(self, mode="plain", file_name=svg_file, **kwargs)def name_parts(self):self.head = self.submobjects[HEAD_INDEX]self.body = self.submobjects[BODY_INDEX]self.arms = self.submobjects[ARMS_INDEX]self.legs = self.submobjects[LEGS_INDEX]self.parts_named = Truedef init_colors(self):SVGMobject.init_colors(self)if not self.parts_named:self.name_parts()self.head.set_fill(self.color, opacity = 1)self.body.set_fill(RED, opacity = 1)self.arms.set_fill(YELLOW, opacity = 1)self.legs.set_fill(BLUE, opacity = 1)return selfclass Waving(Scene):def construct(self):start_man = StickMan()plain_man = StickMan()waving_man = StickMan("wave")self.add(start_man)self.wait()self.play(Transform(start_man,waving_man))self.play(Transform(start_man,plain_man))self.wait()class CirclesAndSquares(SVGMobject):CONFIG = {"color" : BLUE_E,"file_name_prefix": "circles_and_squares","stroke_width" : 2,"stroke_color" : WHITE,"fill_opacity" : 1.0,"height" : 3,"start_corner" : None,"circle_index" : 0,"line1_index" :1,"line2_index" : 2,"square1_index" : 3,"square2_index" : 4,}def __init__(self, mode = "plain", **kwargs):digest_config(self, kwargs)self.mode = modeself.parts_named = Falsetry:svg_file = os.path.join(SVG_IMAGE_DIR,"%s_%s.svg" % (self.file_name_prefix, mode))SVGMobject.__init__(self, file_name=svg_file, **kwargs)except:warnings.warn("No %s design with mode %s" %(self.file_name_prefix, mode))svg_file = os.path.join(SVG_IMAGE_DIR,"circles_and_squares_plain.svg",)SVGMobject.__init__(self, mode="plain", file_name=svg_file, **kwargs)def name_parts(self):self.circle = self.submobjects[self.circle_index]self.line1 = self.submobjects[self.line1_index]self.line2 = self.submobjects[self.line2_index]self.square1 = self.submobjects[self.square1_index]self.square2 = self.submobjects[self.square2_index]self.parts_named = Truedef init_colors(self):SVGMobject.init_colors(self)self.name_parts()self.circle.set_fill(RED, opacity = 1)self.line1.set_fill(self.color, opacity = 0)self.line2.set_fill(self.color, opacity = 0)self.square1.set_fill(GREEN, opacity = 1)self.square2.set_fill(BLUE, opacity = 1)return selfclass SVGCircleAndSquare(Scene):def construct(self):thingy = CirclesAndSquares()self.add(thingy)self.wait()if __name__ == "__main__":# Call this file at command line to make sure all scenes work with version of manim# type "python manim_tutorial_P37.py" at command line to run all scenes in this file#Must have "import os" and  "import pyclbr" at start of file to use this###Using Python class browser to determine which classes are defined in this filemodule_name = 'manim_tutorial_P37'   #Name of current filemodule_info = pyclbr.readmodule(module_name)for item in module_info.values():if item.module==module_name:print(item.name)os.system("python -m manim manim_tutorial_P37.py %s -l" % item.name)  #Does not play files

执行测试

python -m manim manim_tutorial_P37.py MoreShapes -pl

效果:

还有其他一些例子,比如:

支持字体:

此时的环境不支持字体显示,执行带有字体处理的用例会失败,所以接下来需要支持字体处理

安装字体文件:

  1. physics.sty:https://mirrors.ctan.org/macros/latex/contrib/physics/physics.sty
  2. dsfont.sty:

将这两个文件下载到:

/usr/share/texlive/texmf-dist/tex/latex/physics/physics.sty

/usr/share/texlive/texmf-dist/tex/latex/dsfont/

目录

最后执行sudo mktexlsr,如下图所示:

OK,现在可以支持字体显示了,我们重新运行用例:

python -m manim manim_tutorial_P37.py PlotFunctions  -pl

manim_tutorial_P37.py中有21个用例,安装字体后都可以运行

高端一些的例子:

git clone https://github.com/3b1b/manim.git

pip install -e .

验证命令:

manimgl example_scenes.py OpeningManimExample

或者

manim-render example_scenes.py OpeningManimExample

效果:

总结:

3B1B 动画的制作思路是:根据自己想在场景中展现的内容和效果编写一系列的类,然后通过命令行对每个类进行实例化,前面输入的测试命令其实就包含了类的实例化过程,而每个类被实例化后都将得到一个动画片段,通过视频制作软件将各个片段衔接起来并配音,就能得到大家喜闻乐见的 3B1B 教学动画了。


结束!

直观数学-3blue1brown动画的制作相关推荐

  1. 进化计算-遗传算法之史上最直观交叉算子(动画演示)

    获取更多资讯,赶快关注上面的公众号吧! 文章目录 第二十章 遗传算法-史上最直观交叉算子(动画演示) 20.1 单点交叉(Single-point crossover) 20.2 两点交叉(Two-p ...

  2. android 原生砸金蛋 动画,C4D-砸金蛋动画效果制作

    本篇教程讲C4D-砸金蛋动画效果制作,喜欢的一起来学习吧! 那就开始今天的课程吧. 1.搭建画面中的场景.打开C4D,创建一个圆柱,[半径]为900,[高度]为200,[高度分段]为2,[旋转分段]为 ...

  3. Unity3d中使用自带动画系统制作下雨效果(一)

    之前看了以前版本的unity3d demo AngryBots ,觉得里面的下雨效果不错,刚好前段时间学习了,写出来跟大家分享下,直接开始. 使用自带动画系统制作下雨效果. 先制作下雨的雨滴涟漪. 步 ...

  4. flash动画制作成品_「咻动画」flash动画在制作方面有哪些优势?

    在flash小游戏渐渐被越来越多的线上游戏淹没之后,flash动画制作渐渐地也淡出了我们的生活,不少小伙伴会发出时代变了之类的感叹.其实flash动画的应用范围并不局限于flash小游戏,在网页设计以 ...

  5. 如何将unity3d动画嵌入html,在Unity3D中使用精灵动画引擎制作动画的两种方法

    7月28日消息,如今的游戏玩家对于游戏角色的动作要求越来越高,给开发者提出了众多的要求,工作量也相应上升.那么如何才能简单快速地制作角色动画以提升效率呢?下面就和大家分享两个在Unity3D中使用精灵 ...

  6. flash制作文字笔顺_flash动画课件制作有什么优点

    Flash已经逐渐成为交互性矢量技术的标准,是未来网页制作和网络课件制作的主流.Flash动画课件制作相比传统方式.PPT课件,在教学中发挥着重要的价值,本篇文章,拥有多年制作经验的艺虎动画,总结分享 ...

  7. 完全用计算机制作的三维动画,通过四个步骤告诉你三维动画怎么制作

    三维动画怎么制作?我们在惊叹于三维动画技术的逼真时,常常会思考这个问题.当然,三维动画的制作是需要通过专业的技术学习才能得以实现的.要想完全了解三维动画的制作,是要下一番大功夫才行.今天也只是简单为大 ...

  8. css动画效果制作正方体旋转相册

    以下代码利用css动画效果制作了一个旋转的正方体,给正方体六个面放置好图片就可以当一个炫酷有趣的正方体旋转相框啦!可以将女朋友的照片放进去哦,赶快去试试,给女朋友一个惊喜吧! 下面没有放入背景建议大家 ...

  9. 计算机基础与应用说课ppt课件,广东省“XX杯”说课大赛计算机应用基础类一等奖作品:PPT写字动画的制作现场说课课件.ppt...

    广东省"XX杯"说课大赛计算机应用基础类一等奖作品:PPT写字动画的制作现场说课课件.ppt 文档编号:1054435 文档页数:28 上传时间: 2020-05-30 文档级别: ...

最新文章

  1. android表格布局最后一个组件,Android布局之TableLayout表格布局
  2. NI Measurement Studio 打包问题的解决(原创)
  3. Windows平台下 vscode清理Java工程项目的缓存、相关快捷键设置
  4. UITableView-FDTemplateLayoutCell自动计算UITableView高度的使用
  5. C#中DataGridView控件使用大全
  6. python判断天数_python判断输入日期是该年的第几天
  7. php 正则匹配所有路径,与文件路径匹配的PHP正则表达式
  8. java 索引实现,Java创建ES索引实现
  9. 访问:source.android.com和developer.android.com
  10. 经历一番波折后的fedora以及wireshark
  11. peek java linkedlist_Java LinkedList peek()方法
  12. 对于计算机学科的认识和一些感想
  13. 用C语言求两个数的最大公约数和最小公倍数
  14. ZZULIOJ 1011: 圆柱体表面积(C/C++)
  15. 以设计美学缔造行业软实力 姊芙设计 与您共同见证
  16. 快速掌握 jQuery 操作 HTML 节点
  17. 加密软件的问答-加密软件与密码学有什么关系?
  18. Mac如何设置手写输入?
  19. 战网手机安全令 服务器当前正在维护或已关闭,暴雪安全令一直设定 | 手游网游页游攻略大全...
  20. elastic search index和type

热门文章

  1. Android开发实战《智慧北京》——4.WebView的使用
  2. 如何搭建云进销存-采购管理系统?
  3. Arduino Uno + 光耦继电器 弱电控制强电 实验
  4. 毫米波雷达基本原理,多普勒效应技术,雷达探测模块应用
  5. 校内网黑客入侵案的最新消息,用户日志模块遭挂马“QQ千里眼”
  6. vim的常用命令使用教程
  7. 嵌入式软件--单元测试工具介绍
  8. python实现流媒体,python +OpenCV实现rtmp视频流媒体的播放
  9. 最新零基础C++逆向辅助+过驱动保护+lua
  10. MAC安装向日葵软件 远程协助设置1