├── LICENSE
├── README.md
├── circles_and_squares_plain.svg
├── manim_tutorial_1.py
├── manim_tutorial_P37.py
├── stick_man_plain.svg
└── stick_man_wave.svg
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 zimmermant
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # manim_tutorial
2 | Files to accompany postings at [talkingphysics.wordpress.com](https://talkingphysics.wordpress.com/2018/06/11/learning-how-to-animate-videos-using-manim-series-a-journey/) on how to use manim
3 |
4 |
5 |
--------------------------------------------------------------------------------
/circles_and_squares_plain.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
87 |
--------------------------------------------------------------------------------
/manim_tutorial_1.py:
--------------------------------------------------------------------------------
1 | from big_ol_pile_of_manim_imports import *
2 |
3 | class Shapes(Scene):
4 | #A few simple shapes
5 | #Python 2.7 version runs in Python 3.7 without changes
6 | def construct(self):
7 | circle = Circle()
8 | square = Square()
9 | line=Line(np.array([3,0,0]),np.array([5,0,0]))
10 | triangle=Polygon(np.array([0,0,0]),np.array([1,1,0]),np.array([1,-1,0]))
11 |
12 |
13 | self.play(ShowCreation(circle))
14 | self.play(FadeOut(circle))
15 | self.play(GrowFromCenter(square))
16 | self.play(Transform(square,triangle))
17 | self.add(line)
18 |
19 | class MoreShapes(Scene):
20 | #A few more simple shapes
21 | #2.7 version runs in 3.7 without any changes
22 | #Note: I fixed my 'play command not found' issue by installing sox
23 | def construct(self):
24 | circle = Circle(color=PURPLE_A)
25 | square = Square(fill_color=GOLD_B, fill_opacity=1, color=GOLD_A)
26 | square.move_to(UP+LEFT)
27 | circle.surround(square)
28 | rectangle = Rectangle(height=2, width=3)
29 | ellipse=Ellipse(width=3, height=1, color=RED)
30 | ellipse.shift(2*DOWN+2*RIGHT)
31 | pointer = CurvedArrow(2*RIGHT,5*RIGHT,color=MAROON_C)
32 | arrow = Arrow(LEFT,UP)
33 | arrow.next_to(circle,DOWN+LEFT)
34 | rectangle.next_to(arrow,DOWN+LEFT)
35 | ring=Annulus(inner_radius=.5, outer_radius=1, color=BLUE)
36 | ring.next_to(ellipse, RIGHT)
37 |
38 | self.add(pointer)
39 | self.play(FadeIn(square))
40 | self.play(Rotating(square),FadeIn(circle))
41 | self.play(GrowArrow(arrow))
42 | self.play(GrowFromCenter(rectangle), GrowFromCenter(ellipse), GrowFromCenter(ring))
43 |
44 | class MovingShapes(Scene):
45 | #Show the difference between .shift() and .move_to
46 | def construct(self):
47 | circle=Circle(color=TEAL_A)
48 | circle.move_to(LEFT)
49 | square=Circle()
50 | square.move_to(LEFT+3*DOWN)
51 |
52 | self.play(GrowFromCenter(circle), GrowFromCenter(square), rate=5)
53 | self.play(ApplyMethod(circle.move_to,RIGHT), ApplyMethod(square.shift,RIGHT))
54 | self.play(ApplyMethod(circle.move_to,RIGHT+UP), ApplyMethod(square.shift,RIGHT+UP))
55 | self.play(ApplyMethod(circle.move_to,LEFT+UP), ApplyMethod(square.shift,LEFT+UP))
56 |
57 | class AddingText(Scene):
58 | #Adding text on the screen
59 | def construct(self):
60 | my_first_text=TextMobject("Writing with manim is fun")
61 | second_line=TextMobject("and easy to do!")
62 | second_line.next_to(my_first_text,DOWN)
63 | third_line=TextMobject("for me and you!")
64 | third_line.next_to(my_first_text,DOWN)
65 |
66 | self.add(my_first_text, second_line)
67 | self.wait(2)
68 | self.play(Transform(second_line,third_line))
69 | self.wait(2)
70 | second_line.shift(3*DOWN)
71 | self.play(ApplyMethod(my_first_text.shift,3*UP))
72 |
73 |
74 | class AddingMoreText(Scene):
75 | #Playing around with text properties
76 | def construct(self):
77 | quote = TextMobject("Imagination is more important than knowledge")
78 | quote.set_color(RED)
79 | quote.to_edge(UP)
80 | quote2 = TextMobject("A person who never made a mistake never tried anything new")
81 | quote2.set_color(YELLOW)
82 | author=TextMobject("-Albert Einstein")
83 | author.scale(0.75)
84 | author.next_to(quote.get_corner(DOWN+RIGHT),DOWN)
85 |
86 | self.add(quote)
87 | self.add(author)
88 | self.wait(2)
89 | self.play(Transform(quote,quote2),ApplyMethod(author.move_to,quote2.get_corner(DOWN+RIGHT)+DOWN+2*LEFT))
90 | self.play(ApplyMethod(author.match_color,quote2),Transform(author,author.scale(1)))
91 | self.play(FadeOut(quote))
92 |
93 | class RotateAndHighlight(Scene):
94 | #Rotation of text and highlighting with surrounding geometries
95 | def construct(self):
96 | square=Square(side_length=5,fill_color=YELLOW, fill_opacity=1)
97 | label=TextMobject("Text at an angle")
98 | label.bg=BackgroundRectangle(label,fill_opacity=1)
99 | label_group=VGroup(label.bg,label) #Order matters
100 | label_group.rotate(TAU/8)
101 | label2=TextMobject("Boxed text",color=BLACK)
102 | label2.bg=SurroundingRectangle(label2,color=BLUE,fill_color=RED, fill_opacity=.5)
103 | label2_group=VGroup(label2,label2.bg)
104 | label2_group.next_to(label_group,DOWN)
105 | label3=TextMobject("Rainbow")
106 | label3.scale(2)
107 | label3.set_color_by_gradient(RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE)
108 | label3.to_edge(DOWN)
109 |
110 | self.add(square)
111 | self.play(FadeIn(label_group))
112 | self.play(FadeIn(label2_group))
113 | self.play(FadeIn(label3))
114 |
115 | class BasicEquations(Scene):
116 | #A short script showing how to use Latex commands
117 | def construct(self):
118 | eq1=TextMobject("$\\vec{X}_0 \\cdot \\vec{Y}_1 = 3$")
119 | eq1.shift(2*UP)
120 | eq2=TexMobject("\\vec{F}_{net} = \\sum_i \\vec{F}_i")
121 | eq2.shift(2*DOWN)
122 |
123 | self.play(Write(eq1))
124 | self.play(Write(eq2))
125 |
126 | class ColoringEquations(Scene):
127 | #Grouping and coloring parts of equations
128 | def construct(self):
129 | line1=TexMobject("\\text{The vector }", "\\vec{F}_{net}", "\\text{ is the net force on object of mass }")
130 | line1.set_color_by_tex("force", BLUE)
131 | line2=TexMobject("m", "\\text{ and acceleration }", "\\vec{a}", ". ")
132 | line2.set_color_by_tex_to_color_map({
133 | "m": YELLOW,
134 | "{a}": RED
135 | })
136 | sentence=VGroup(line1,line2)
137 | sentence.arrange_submobjects(DOWN, buff=MED_LARGE_BUFF)
138 | self.play(Write(sentence))
139 |
140 | class UsingBraces(Scene):
141 | #Using braces to group text together
142 | def construct(self):
143 | eq1A = TextMobject("4x + 3y")
144 | eq1B = TextMobject("=")
145 | eq1C = TextMobject("0")
146 | eq2A = TextMobject("5x -2y")
147 | eq2B = TextMobject("=")
148 | eq2C = TextMobject("3")
149 | eq1B.next_to(eq1A,RIGHT)
150 | eq1C.next_to(eq1B,RIGHT)
151 | eq2A.shift(DOWN)
152 | eq2B.shift(DOWN)
153 | eq2C.shift(DOWN)
154 | eq2A.align_to(eq1A,LEFT)
155 | eq2B.align_to(eq1B,LEFT)
156 | eq2C.align_to(eq1C,LEFT)
157 |
158 | eq_group=VGroup(eq1A,eq2A)
159 | braces=Brace(eq_group,LEFT)
160 | eq_text = braces.get_text("A pair of equations")
161 |
162 | self.add(eq1A, eq1B, eq1C)
163 | self.add(eq2A, eq2B, eq2C)
164 | self.play(GrowFromCenter(braces),Write(eq_text))
165 |
166 |
167 | class UsingBracesConcise(Scene):
168 | #A more concise block of code with all columns aligned
169 | def construct(self):
170 | eq1_text=["4","x","+","3","y","=","0"]
171 | eq2_text=["5","x","-","2","y","=","3"]
172 | eq1_mob=TexMobject(*eq1_text)
173 | eq2_mob=TexMobject(*eq2_text)
174 | eq1_mob.set_color_by_tex_to_color_map({
175 | "x":RED_B,
176 | "y":GREEN_C
177 | })
178 | eq2_mob.set_color_by_tex_to_color_map({
179 | "x":RED_B,
180 | "y":GREEN_C
181 | })
182 | for i,item in enumerate(eq2_mob):
183 | item.align_to(eq1_mob[i],LEFT)
184 | eq1=VGroup(*eq1_mob)
185 | eq2=VGroup(*eq2_mob)
186 | eq2.shift(DOWN)
187 | eq_group=VGroup(eq1,eq2)
188 | braces=Brace(eq_group,LEFT)
189 | eq_text = braces.get_text("A pair of equations")
190 |
191 | self.play(Write(eq1),Write(eq2))
192 | self.play(GrowFromCenter(braces),Write(eq_text))
193 |
194 | class PlotFunctions(GraphScene):
195 | CONFIG = {
196 | "x_min" : -10,
197 | "x_max" : 10,
198 | "y_min" : -1.5,
199 | "y_max" : 1.5,
200 | "graph_origin" : ORIGIN ,
201 | "function_color" : RED ,
202 | "axes_color" : GREEN,
203 | "x_labeled_nums" :range(-10,12,2),
204 |
205 | }
206 | def construct(self):
207 | self.setup_axes(animate=True)
208 | func_graph=self.get_graph(self.func_to_graph,self.function_color)
209 | func_graph2=self.get_graph(self.func_to_graph2)
210 | vert_line = self.get_vertical_line_to_graph(TAU,func_graph,color=YELLOW)
211 | graph_lab = self.get_graph_label(func_graph, label = "\\cos(x)")
212 | graph_lab2=self.get_graph_label(func_graph2,label = "\\sin(x)", x_val=-10, direction=UP/2)
213 | two_pi = TexMobject("x = 2 \\pi")
214 | label_coord = self.input_to_graph_point(TAU,func_graph)
215 | two_pi.next_to(label_coord,RIGHT+UP)
216 |
217 |
218 |
219 | self.play(ShowCreation(func_graph),ShowCreation(func_graph2))
220 | self.play(ShowCreation(vert_line), ShowCreation(graph_lab), ShowCreation(graph_lab2),ShowCreation(two_pi))
221 |
222 |
223 | def func_to_graph(self,x):
224 | return np.cos(x)
225 |
226 | def func_to_graph2(self,x):
227 | return np.sin(x)
228 |
229 |
230 | class ExampleApproximation(GraphScene):
231 | CONFIG = {
232 | "function" : lambda x : np.cos(x),
233 | "function_color" : BLUE,
234 | "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),
235 | 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)],
236 | "center_point" : 0,
237 | "approximation_color" : GREEN,
238 | "x_min" : -10,
239 | "x_max" : 10,
240 | "y_min" : -1,
241 | "y_max" : 1,
242 | "graph_origin" : ORIGIN ,
243 | "x_labeled_nums" :range(-10,12,2),
244 |
245 | }
246 | def construct(self):
247 | self.setup_axes(animate=True)
248 | func_graph = self.get_graph(
249 | self.function,
250 | self.function_color,
251 | )
252 | approx_graphs = [
253 | self.get_graph(
254 | f,
255 | self.approximation_color
256 | )
257 | for f in self.taylor
258 | ]
259 |
260 | term_num = [
261 | TexMobject("n = " + str(n),aligned_edge=TOP)
262 | for n in range(0,8)]
263 | #[t.to_edge(BOTTOM,buff=SMALL_BUFF) for t in term_num]
264 |
265 |
266 | #term = TexMobject("")
267 | #term.to_edge(BOTTOM,buff=SMALL_BUFF)
268 | term = VectorizedPoint(3*DOWN)
269 |
270 | approx_graph = VectorizedPoint(
271 | self.input_to_graph_point(self.center_point, func_graph)
272 | )
273 |
274 | self.play(
275 | ShowCreation(func_graph),
276 | )
277 | for n,graph in enumerate(approx_graphs):
278 | self.play(
279 | Transform(approx_graph, graph, run_time = 2),
280 | Transform(term,term_num[n])
281 | )
282 | self.wait()
283 |
284 |
285 | class DrawAnAxis(Scene):
286 | CONFIG = { "plane_kwargs" : {
287 | "x_line_frequency" : 2,
288 | "y_line_frequency" :2
289 | }
290 | }
291 |
292 | def construct(self):
293 | my_plane = NumberPlane(**self.plane_kwargs)
294 | my_plane.add(my_plane.get_axis_labels())
295 | self.add(my_plane)
296 | self.wait()
297 |
298 | class SimpleField(Scene):
299 | CONFIG = {
300 | "plane_kwargs" : {
301 | "color" : RED
302 | },
303 | }
304 | def construct(self):
305 | plane = NumberPlane(**self.plane_kwargs) #Create axes and grid
306 | plane.add(plane.get_axis_labels()) #add x and y label
307 | self.add(plane) #Place grid on screen
308 |
309 | points = [x*RIGHT+y*UP
310 | for x in np.arange(-5,5,1)
311 | for y in np.arange(-5,5,1)
312 | ] #List of vectors pointing to each grid point
313 |
314 | vec_field = [] #Empty list to use in for loop
315 | for point in points:
316 | field = 0.5*RIGHT + 0.5*UP #Constant field up and to right
317 | result = Vector(field).shift(point) #Create vector and shift it to grid point
318 | vec_field.append(result) #Append to list
319 |
320 | draw_field = VGroup(*vec_field) #Pass list of vectors to create a VGroup
321 |
322 |
323 | self.play(ShowCreation(draw_field)) #Draw VGroup on screen
324 |
325 |
326 | class FieldWithAxes(Scene):
327 | CONFIG = {
328 | "plane_kwargs" : {
329 | "color" : RED_B
330 | },
331 | "point_charge_loc" : 0.5*RIGHT-1.5*UP,
332 | }
333 | def construct(self):
334 | plane = NumberPlane(**self.plane_kwargs)
335 | plane.main_lines.fade(.9)
336 | plane.add(plane.get_axis_labels())
337 | self.add(plane)
338 |
339 | field = VGroup(*[self.calc_field(x*RIGHT+y*UP)
340 | for x in np.arange(-9,9,1)
341 | for y in np.arange(-5,5,1)
342 | ])
343 |
344 | self.play(ShowCreation(field))
345 |
346 |
347 | def calc_field(self,point):
348 | #This calculates the field at a single point.
349 | x,y = point[:2]
350 | Rx,Ry = self.point_charge_loc[:2]
351 | r = math.sqrt((x-Rx)**2 + (y-Ry)**2)
352 | efield = (point - self.point_charge_loc)/r**3
353 | #efield = np.array((-y,x,0))/math.sqrt(x**2+y**2) #Try one of these two fields
354 | #efield = np.array(( -2*(y%2)+1 , -2*(x%2)+1 , 0 ))/3 #Try one of these two fields
355 | return Vector(efield).shift(point)
356 |
357 | class ExampleThreeD(ThreeDScene):
358 | ### Broken in 3.7
359 | CONFIG = {
360 | "plane_kwargs" : {
361 | "color" : RED_B
362 | },
363 | "point_charge_loc" : 0.5*RIGHT-1.5*UP,
364 | }
365 | def construct(self):
366 | self.set_camera_position(0, -np.pi/2)
367 | plane = NumberPlane(**self.plane_kwargs)
368 | plane.main_lines.fade(.9)
369 | plane.add(plane.get_axis_labels())
370 | self.add(plane)
371 |
372 | field2D = VGroup(*[self.calc_field2D(x*RIGHT+y*UP)
373 | for x in np.arange(-9,9,1)
374 | for y in np.arange(-5,5,1)
375 | ])
376 |
377 |
378 | self.play(ShowCreation(field2D))
379 | self.wait()
380 | self.move_camera(0.8*np.pi/2, -0.45*np.pi)
381 | self.begin_ambient_camera_rotation()
382 | self.wait(6)
383 |
384 | def calc_field2D(self,point):
385 | x,y = point[:2]
386 | Rx,Ry = self.point_charge_loc[:2]
387 | r = math.sqrt((x-Rx)**2 + (y-Ry)**2)
388 | efield = (point - self.point_charge_loc)/r**3
389 | return Vector(efield).shift(point)
390 |
391 |
392 | class EFieldInThreeD(ThreeDScene):
393 | CONFIG = {
394 | "plane_kwargs" : {
395 | "color" : RED_B
396 | },
397 | "point_charge_loc" : 0.5*RIGHT-1.5*UP,
398 | }
399 | def construct(self):
400 | self.set_camera_position(0.1, -np.pi/2)
401 | plane = NumberPlane(**self.plane_kwargs)
402 | plane.main_lines.fade(.9)
403 | plane.add(plane.get_axis_labels())
404 | self.add(plane)
405 |
406 | field2D = VGroup(*[self.calc_field2D(x*RIGHT+y*UP)
407 | for x in np.arange(-9,9,1)
408 | for y in np.arange(-5,5,1)
409 | ])
410 |
411 | field3D = VGroup(*[self.calc_field3D(x*RIGHT+y*UP+z*OUT)
412 | for x in np.arange(-9,9,1)
413 | for y in np.arange(-5,5,1)
414 | for z in np.arange(-5,5,1)])
415 |
416 |
417 |
418 | self.play(ShowCreation(field3D))
419 | self.wait()
420 | self.move_camera(0.8*np.pi/2, -0.45*np.pi)
421 | self.begin_ambient_camera_rotation()
422 | self.wait(6)
423 |
424 |
425 | def calc_field2D(self,point):
426 | x,y = point[:2]
427 | Rx,Ry = self.point_charge_loc[:2]
428 | r = math.sqrt((x-Rx)**2 + (y-Ry)**2)
429 | efield = (point - self.point_charge_loc)/r**3
430 | return Vector(efield).shift(point)
431 |
432 | def calc_field3D(self,point):
433 | x,y,z = point
434 | Rx,Ry,Rz = self.point_charge_loc
435 | r = math.sqrt((x-Rx)**2 + (y-Ry)**2+(z-Rz)**2)
436 | efield = (point - self.point_charge_loc)/r**3
437 | #efield = np.array((-y,x,z))/math.sqrt(x**2+y**2+z**2)
438 | return Vector(efield).shift(point)
439 |
440 |
441 | class MovingCharges(Scene):
442 | CONFIG = {
443 | "plane_kwargs" : {
444 | "color" : RED_B
445 | },
446 | "point_charge_loc" : 0.5*RIGHT-1.5*UP,
447 | }
448 | def construct(self):
449 | plane = NumberPlane(**self.plane_kwargs)
450 | plane.main_lines.fade(.9)
451 | plane.add(plane.get_axis_labels())
452 | self.add(plane)
453 |
454 | field = VGroup(*[self.calc_field(x*RIGHT+y*UP)
455 | for x in np.arange(-9,9,1)
456 | for y in np.arange(-5,5,1)
457 | ])
458 | self.field=field
459 | source_charge = self.Positron().move_to(self.point_charge_loc)
460 | self.play(FadeIn(source_charge))
461 | self.play(ShowCreation(field))
462 | self.moving_charge()
463 |
464 | def calc_field(self,point):
465 | x,y = point[:2]
466 | Rx,Ry = self.point_charge_loc[:2]
467 | r = math.sqrt((x-Rx)**2 + (y-Ry)**2)
468 | efield = (point - self.point_charge_loc)/r**3
469 | return Vector(efield).shift(point)
470 |
471 | def moving_charge(self):
472 | numb_charges=4
473 | possible_points = [v.get_start() for v in self.field]
474 | points = random.sample(possible_points, numb_charges)
475 | particles = VGroup(*[
476 | self.Positron().move_to(point)
477 | for point in points
478 | ])
479 | for particle in particles:
480 | particle.velocity = np.array((0,0,0))
481 |
482 | self.play(FadeIn(particles))
483 | self.moving_particles = particles
484 | self.add_foreground_mobjects(self.moving_particles )
485 | self.always_continually_update = True
486 | self.wait(10)
487 |
488 | def field_at_point(self,point):
489 | x,y = point[:2]
490 | Rx,Ry = self.point_charge_loc[:2]
491 | r = math.sqrt((x-Rx)**2 + (y-Ry)**2)
492 | efield = (point - self.point_charge_loc)/r**3
493 | return efield
494 |
495 | def continual_update(self, *args, **kwargs):
496 | if hasattr(self, "moving_particles"):
497 | dt = self.frame_duration
498 | for p in self.moving_particles:
499 | accel = self.field_at_point(p.get_center())
500 | p.velocity = p.velocity + accel*dt
501 | p.shift(p.velocity*dt)
502 |
503 |
504 | class Positron(Circle):
505 | CONFIG = {
506 | "radius" : 0.2,
507 | "stroke_width" : 3,
508 | "color" : RED,
509 | "fill_color" : RED,
510 | "fill_opacity" : 0.5,
511 | }
512 | def __init__(self, **kwargs):
513 | Circle.__init__(self, **kwargs)
514 | plus = TexMobject("+")
515 | plus.scale(0.7)
516 | plus.move_to(self)
517 | self.add(plus)
518 |
519 | class FieldOfMovingCharge(Scene):
520 | CONFIG = {
521 | "plane_kwargs" : {
522 | "color" : RED_B
523 | },
524 | "point_charge_start_loc" : 5.5*LEFT-1.5*UP,
525 | }
526 | def construct(self):
527 | plane = NumberPlane(**self.plane_kwargs)
528 | plane.main_lines.fade(.9)
529 | plane.add(plane.get_axis_labels())
530 | self.add(plane)
531 |
532 | field = VGroup(*[self.create_vect_field(self.point_charge_start_loc,x*RIGHT+y*UP)
533 | for x in np.arange(-9,9,1)
534 | for y in np.arange(-5,5,1)
535 | ])
536 | self.field=field
537 | self.source_charge = self.Positron().move_to(self.point_charge_start_loc)
538 | self.source_charge.velocity = np.array((1,0,0))
539 | self.play(FadeIn(self.source_charge))
540 | self.play(ShowCreation(field))
541 | self.moving_charge()
542 |
543 | def create_vect_field(self,source_charge,observation_point):
544 | return Vector(self.calc_field(source_charge,observation_point)).shift(observation_point)
545 |
546 | def calc_field(self,source_point,observation_point):
547 | x,y,z = observation_point
548 | Rx,Ry,Rz = source_point
549 | r = math.sqrt((x-Rx)**2 + (y-Ry)**2 + (z-Rz)**2)
550 | if r<0.0000001: #Prevent divide by zero
551 | efield = np.array((0,0,0))
552 | else:
553 | efield = (observation_point - source_point)/r**3
554 | return efield
555 |
556 |
557 |
558 | def moving_charge(self):
559 | numb_charges=3
560 | possible_points = [v.get_start() for v in self.field]
561 | points = random.sample(possible_points, numb_charges)
562 | particles = VGroup(self.source_charge, *[
563 | self.Positron().move_to(point)
564 | for point in points
565 | ])
566 | for particle in particles[1:]:
567 | particle.velocity = np.array((0,0,0))
568 | self.play(FadeIn(particles[1:]))
569 | self.moving_particles = particles
570 | self.add_foreground_mobjects(self.moving_particles )
571 | self.always_continually_update = True
572 | self.wait(10)
573 |
574 |
575 | def continual_update(self, *args, **kwargs):
576 | Scene.continual_update(self, *args, **kwargs)
577 | if hasattr(self, "moving_particles"):
578 | dt = self.frame_duration
579 |
580 | for v in self.field:
581 | field_vect=np.zeros(3)
582 | for p in self.moving_particles:
583 | field_vect = field_vect + self.calc_field(p.get_center(), v.get_start())
584 | v.put_start_and_end_on(v.get_start(), field_vect+v.get_start())
585 |
586 | for p in self.moving_particles:
587 | accel = np.zeros(3)
588 | p.velocity = p.velocity + accel*dt
589 | p.shift(p.velocity*dt)
590 |
591 |
592 | class Positron(Circle):
593 | CONFIG = {
594 | "radius" : 0.2,
595 | "stroke_width" : 3,
596 | "color" : RED,
597 | "fill_color" : RED,
598 | "fill_opacity" : 0.5,
599 | }
600 | def __init__(self, **kwargs):
601 | Circle.__init__(self, **kwargs)
602 | plus = TexMobject("+")
603 | plus.scale(0.7)
604 | plus.move_to(self)
605 | self.add(plus)
606 |
--------------------------------------------------------------------------------
/manim_tutorial_P37.py:
--------------------------------------------------------------------------------
1 | from manimlib.imports import *
2 | import os
3 | import pyclbr
4 |
5 |
6 |
7 | class Shapes(Scene):
8 | #A few simple shapes
9 | #Python 2.7 version runs in Python 3.7 without changes
10 | def construct(self):
11 | circle = Circle()
12 | square = Square()
13 | line=Line(np.array([3,0,0]),np.array([5,0,0]))
14 | triangle=Polygon(np.array([0,0,0]),np.array([1,1,0]),np.array([1,-1,0]))
15 |
16 |
17 | self.play(ShowCreation(circle))
18 | self.play(FadeOut(circle))
19 | self.play(GrowFromCenter(square))
20 | self.play(Transform(square,triangle))
21 | self.add(line)
22 |
23 | class MoreShapes(Scene):
24 | #A few more simple shapes
25 | #2.7 version runs in 3.7 without any changes
26 | #Note: I fixed my 'play command not found' issue by installing sox
27 | def construct(self):
28 | circle = Circle(color=PURPLE_A)
29 | square = Square(fill_color=GOLD_B, fill_opacity=1, color=GOLD_A)
30 | square.move_to(UP+LEFT)
31 | circle.surround(square)
32 | rectangle = Rectangle(height=2, width=3)
33 | ellipse=Ellipse(width=3, height=1, color=RED)
34 | ellipse.shift(2*DOWN+2*RIGHT)
35 | pointer = CurvedArrow(2*RIGHT,5*RIGHT,color=MAROON_C)
36 | arrow = Arrow(LEFT,UP)
37 | arrow.next_to(circle,DOWN+LEFT)
38 | rectangle.next_to(arrow,DOWN+LEFT)
39 | ring=Annulus(inner_radius=.5, outer_radius=1, color=BLUE)
40 | ring.next_to(ellipse, RIGHT)
41 |
42 | self.add(pointer)
43 | self.play(FadeIn(square))
44 | self.play(Rotating(square),FadeIn(circle))
45 | self.play(GrowArrow(arrow))
46 | self.play(GrowFromCenter(rectangle), GrowFromCenter(ellipse), GrowFromCenter(ring))
47 |
48 | class MovingShapes(Scene):
49 | #Show the difference between .shift() and .move_to
50 | def construct(self):
51 | circle=Circle(color=TEAL_A)
52 | circle.move_to(LEFT)
53 | square=Circle()
54 | square.move_to(LEFT+3*DOWN)
55 |
56 | self.play(GrowFromCenter(circle), GrowFromCenter(square), rate=5)
57 | self.play(ApplyMethod(circle.move_to,RIGHT), ApplyMethod(square.shift,RIGHT))
58 | self.play(ApplyMethod(circle.move_to,RIGHT+UP), ApplyMethod(square.shift,RIGHT+UP))
59 | self.play(ApplyMethod(circle.move_to,LEFT+UP), ApplyMethod(square.shift,LEFT+UP))
60 |
61 | class AddingText(Scene):
62 | #Adding text on the screen
63 | def construct(self):
64 | my_first_text=TextMobject("Writing with manim is fun")
65 | second_line=TextMobject("and easy to do!")
66 | second_line.next_to(my_first_text,DOWN)
67 | third_line=TextMobject("for me and you!")
68 | third_line.next_to(my_first_text,DOWN)
69 |
70 | self.add(my_first_text, second_line)
71 | self.wait(2)
72 | self.play(Transform(second_line,third_line))
73 | self.wait(2)
74 | second_line.shift(3*DOWN)
75 | self.play(ApplyMethod(my_first_text.shift,3*UP))
76 | ###Try uncommenting the following###
77 | #self.play(ApplyMethod(second_line.move_to, LEFT_SIDE-2*LEFT))
78 | #self.play(ApplyMethod(my_first_text.next_to,second_line))
79 |
80 |
81 | class AddingMoreText(Scene):
82 | #Playing around with text properties
83 | def construct(self):
84 | quote = TextMobject("Imagination is more important than knowledge")
85 | quote.set_color(RED)
86 | quote.to_edge(UP)
87 | quote2 = TextMobject("A person who never made a mistake never tried anything new")
88 | quote2.set_color(YELLOW)
89 | author=TextMobject("-Albert Einstein")
90 | author.scale(0.75)
91 | author.next_to(quote.get_corner(DOWN+RIGHT),DOWN)
92 |
93 | self.add(quote)
94 | self.add(author)
95 | self.wait(2)
96 | self.play(Transform(quote,quote2),ApplyMethod(author.move_to,quote2.get_corner(DOWN+RIGHT)+DOWN+2*LEFT))
97 | self.play(ApplyMethod(author.scale,1.5))
98 | author.match_color(quote2)
99 | self.play(FadeOut(quote))
100 |
101 | class RotateAndHighlight(Scene):
102 | #Rotation of text and highlighting with surrounding geometries
103 | def construct(self):
104 | square=Square(side_length=5,fill_color=YELLOW, fill_opacity=1)
105 | label=TextMobject("Text at an angle")
106 | label.bg=BackgroundRectangle(label,fill_opacity=1)
107 | label_group=VGroup(label.bg,label) #Order matters
108 | label_group.rotate(TAU/8)
109 | label2=TextMobject("Boxed text",color=BLACK)
110 | label2.bg=SurroundingRectangle(label2,color=BLUE,fill_color=RED, fill_opacity=.5)
111 | label2_group=VGroup(label2,label2.bg)
112 | label2_group.next_to(label_group,DOWN)
113 | label3=TextMobject("Rainbow")
114 | label3.scale(2)
115 | label3.set_color_by_gradient(RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE)
116 | label3.to_edge(DOWN)
117 |
118 | self.add(square)
119 | self.play(FadeIn(label_group))
120 | self.play(FadeIn(label2_group))
121 | self.play(FadeIn(label3))
122 |
123 | class BasicEquations(Scene):
124 | #A short script showing how to use Latex commands
125 | def construct(self):
126 | eq1=TextMobject("$\\vec{X}_0 \\cdot \\vec{Y}_1 = 3$")
127 | eq1.shift(2*UP)
128 | eq2=TexMobject(r"\vec{F}_{net} = \sum_i \vec{F}_i")
129 | eq2.shift(2*DOWN)
130 |
131 | self.play(Write(eq1))
132 | self.play(Write(eq2))
133 |
134 | class ColoringEquations(Scene):
135 | #Grouping and coloring parts of equations
136 | def construct(self):
137 | line1=TexMobject(r"\text{The vector } \vec{F}_{net} \text{ is the net }",r"\text{force }",r"\text{on object of mass }")
138 | line1.set_color_by_tex("force", BLUE)
139 | line2=TexMobject("m", "\\text{ and acceleration }", "\\vec{a}", ". ")
140 | line2.set_color_by_tex_to_color_map({
141 | "m": YELLOW,
142 | "{a}": RED
143 | })
144 | sentence=VGroup(line1,line2)
145 | sentence.arrange_submobjects(DOWN, buff=MED_LARGE_BUFF)
146 | self.play(Write(sentence))
147 |
148 |
149 |
150 | class UsingBraces(Scene):
151 | #Using braces to group text together
152 | def construct(self):
153 | eq1A = TextMobject("4x + 3y")
154 | eq1B = TextMobject("=")
155 | eq1C = TextMobject("0")
156 | eq2A = TextMobject("5x -2y")
157 | eq2B = TextMobject("=")
158 | eq2C = TextMobject("3")
159 | eq1B.next_to(eq1A,RIGHT)
160 | eq1C.next_to(eq1B,RIGHT)
161 | eq2A.shift(DOWN)
162 | eq2B.shift(DOWN)
163 | eq2C.shift(DOWN)
164 | eq2A.align_to(eq1A,LEFT)
165 | eq2B.align_to(eq1B,LEFT)
166 | eq2C.align_to(eq1C,LEFT)
167 |
168 | eq_group=VGroup(eq1A,eq2A)
169 | braces=Brace(eq_group,LEFT)
170 | eq_text = braces.get_text("A pair of equations")
171 |
172 | self.add(eq1A, eq1B, eq1C)
173 | self.add(eq2A, eq2B, eq2C)
174 | self.play(GrowFromCenter(braces),Write(eq_text))
175 |
176 |
177 | class UsingBracesConcise(Scene):
178 | #A more concise block of code with all columns aligned
179 | def construct(self):
180 | eq1_text=["4","x","+","3","y","=","0"]
181 | eq2_text=["5","x","-","2","y","=","3"]
182 | eq1_mob=TexMobject(*eq1_text)
183 | eq2_mob=TexMobject(*eq2_text)
184 | eq1_mob.set_color_by_tex_to_color_map({
185 | "x":RED_B,
186 | "y":GREEN_C
187 | })
188 | eq2_mob.set_color_by_tex_to_color_map({
189 | "x":RED_B,
190 | "y":GREEN_C
191 | })
192 | for i,item in enumerate(eq2_mob):
193 | item.align_to(eq1_mob[i],LEFT)
194 | eq1=VGroup(*eq1_mob)
195 | eq2=VGroup(*eq2_mob)
196 | eq2.shift(DOWN)
197 | eq_group=VGroup(eq1,eq2)
198 | braces=Brace(eq_group,LEFT)
199 | eq_text = braces.get_text("A pair of equations")
200 |
201 | self.play(Write(eq1),Write(eq2))
202 | self.play(GrowFromCenter(braces),Write(eq_text))
203 |
204 | class PlotFunctions(GraphScene):
205 | CONFIG = {
206 | "x_min" : -10,
207 | "x_max" : 10.3,
208 | "y_min" : -1.5,
209 | "y_max" : 1.5,
210 | "graph_origin" : ORIGIN ,
211 | "function_color" : RED ,
212 | "axes_color" : GREEN,
213 | "x_labeled_nums" :range(-10,12,2),
214 |
215 | }
216 | def construct(self):
217 | self.setup_axes(animate=True)
218 | func_graph=self.get_graph(self.func_to_graph,self.function_color)
219 | func_graph2=self.get_graph(self.func_to_graph2)
220 | vert_line = self.get_vertical_line_to_graph(TAU,func_graph,color=YELLOW)
221 | graph_lab = self.get_graph_label(func_graph, label = "\\cos(x)")
222 | graph_lab2=self.get_graph_label(func_graph2,label = "\\sin(x)", x_val=-10, direction=UP/2)
223 | two_pi = TexMobject("x = 2 \\pi")
224 | label_coord = self.input_to_graph_point(TAU,func_graph)
225 | two_pi.next_to(label_coord,RIGHT+UP)
226 |
227 |
228 |
229 | self.play(ShowCreation(func_graph),ShowCreation(func_graph2))
230 | self.play(ShowCreation(vert_line), ShowCreation(graph_lab), ShowCreation(graph_lab2),ShowCreation(two_pi))
231 |
232 |
233 | def func_to_graph(self,x):
234 | return np.cos(x)
235 |
236 | def func_to_graph2(self,x):
237 | return np.sin(x)
238 |
239 |
240 | class ExampleApproximation(GraphScene):
241 | CONFIG = {
242 | "function" : lambda x : np.cos(x),
243 | "function_color" : BLUE,
244 | "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),
245 | 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)],
246 | "center_point" : 0,
247 | "approximation_color" : GREEN,
248 | "x_min" : -10,
249 | "x_max" : 10,
250 | "y_min" : -1,
251 | "y_max" : 1,
252 | "graph_origin" : ORIGIN ,
253 | "x_labeled_nums" :range(-10,12,2),
254 |
255 | }
256 | def construct(self):
257 | self.setup_axes(animate=True)
258 | func_graph = self.get_graph(
259 | self.function,
260 | self.function_color,
261 | )
262 | approx_graphs = [
263 | self.get_graph(
264 | f,
265 | self.approximation_color
266 | )
267 | for f in self.taylor
268 | ]
269 |
270 | term_num = [
271 | TexMobject("n = " + str(n),aligned_edge=TOP)
272 | for n in range(0,8)]
273 | #[t.to_edge(BOTTOM,buff=SMALL_BUFF) for t in term_num]
274 |
275 |
276 | #term = TexMobject("")
277 | #term.to_edge(BOTTOM,buff=SMALL_BUFF)
278 | term = VectorizedPoint(3*DOWN)
279 |
280 | approx_graph = VectorizedPoint(
281 | self.input_to_graph_point(self.center_point, func_graph)
282 | )
283 |
284 | self.play(
285 | ShowCreation(func_graph),
286 | )
287 | for n,graph in enumerate(approx_graphs):
288 | self.play(
289 | Transform(approx_graph, graph, run_time = 2),
290 | Transform(term,term_num[n])
291 | )
292 | self.wait()
293 |
294 |
295 | class DrawAnAxis(Scene):
296 | CONFIG = { "plane_kwargs" : {
297 | "x_line_frequency" : 2,
298 | "y_line_frequency" :2
299 | }
300 | }
301 |
302 | def construct(self):
303 | my_plane = NumberPlane(**self.plane_kwargs)
304 | my_plane.add(my_plane.get_axis_labels())
305 | self.add(my_plane)
306 | #self.wait()
307 |
308 | class SimpleField(Scene):
309 | CONFIG = {
310 | "plane_kwargs" : {
311 | "color" : RED
312 | },
313 | }
314 | def construct(self):
315 | plane = NumberPlane(**self.plane_kwargs) #Create axes and grid
316 | plane.add(plane.get_axis_labels()) #add x and y label
317 | self.add(plane) #Place grid on screen
318 |
319 | points = [x*RIGHT+y*UP
320 | for x in np.arange(-5,5,1)
321 | for y in np.arange(-5,5,1)
322 | ] #List of vectors pointing to each grid point
323 |
324 | vec_field = [] #Empty list to use in for loop
325 | for point in points:
326 | field = 0.5*RIGHT + 0.5*UP #Constant field up and to right
327 | result = Vector(field).shift(point) #Create vector and shift it to grid point
328 | vec_field.append(result) #Append to list
329 |
330 | draw_field = VGroup(*vec_field) #Pass list of vectors to create a VGroup
331 |
332 |
333 | self.play(ShowCreation(draw_field)) #Draw VGroup on screen
334 |
335 |
336 | class FieldWithAxes(Scene):
337 | CONFIG = {
338 | "plane_kwargs" : {
339 | "color" : RED_B
340 | },
341 | "point_charge_loc" : 0.5*RIGHT-1.5*UP,
342 | }
343 | def construct(self):
344 | plane = NumberPlane(**self.plane_kwargs)
345 | #plane.main_lines.fade(.9) #doesn't work in most recent commit
346 | plane.add(plane.get_axis_labels())
347 | self.add(plane)
348 |
349 | field = VGroup(*[self.calc_field(x*RIGHT+y*UP)
350 | for x in np.arange(-9,9,1)
351 | for y in np.arange(-5,5,1)
352 | ])
353 |
354 | self.play(ShowCreation(field))
355 |
356 |
357 | def calc_field(self,point):
358 | #This calculates the field at a single point.
359 | x,y = point[:2]
360 | Rx,Ry = self.point_charge_loc[:2]
361 | r = math.sqrt((x-Rx)**2 + (y-Ry)**2)
362 | efield = (point - self.point_charge_loc)/r**3
363 | #efield = np.array((-y,x,0))/math.sqrt(x**2+y**2) #Try one of these two fields
364 | #efield = np.array(( -2*(y%2)+1 , -2*(x%2)+1 , 0 ))/3 #Try one of these two fields
365 | return Vector(efield).shift(point)
366 |
367 | class ExampleThreeD(ThreeDScene):
368 | CONFIG = {
369 | "plane_kwargs" : {
370 | "color" : RED_B
371 | },
372 | "point_charge_loc" : 0.5*RIGHT-1.5*UP,
373 | }
374 | def construct(self):
375 | plane = NumberPlane(**self.plane_kwargs)
376 | #plane.main_lines.fade(.9) #Doesn't work in most recent commit
377 | plane.add(plane.get_axis_labels())
378 | self.add(plane)
379 |
380 | field2D = VGroup(*[self.calc_field2D(x*RIGHT+y*UP)
381 | for x in np.arange(-9,9,1)
382 | for y in np.arange(-5,5,1)
383 | ])
384 |
385 | self.set_camera_orientation(phi=PI/3,gamma=PI/5)
386 | self.play(ShowCreation(field2D))
387 | self.wait()
388 | #self.move_camera(gamma=0,run_time=1) #Doesn't work in most recent commit
389 | #self.move_camera(phi=3/4*PI, theta=-PI/2) #Doesn't work in most recent commit
390 | self.begin_ambient_camera_rotation(rate=0.1)
391 | self.wait(6)
392 |
393 | def calc_field2D(self,point):
394 | x,y = point[:2]
395 | Rx,Ry = self.point_charge_loc[:2]
396 | r = math.sqrt((x-Rx)**2 + (y-Ry)**2)
397 | efield = (point - self.point_charge_loc)/r**3
398 | return Vector(efield).shift(point)
399 |
400 |
401 | class EFieldInThreeD(ThreeDScene):
402 | CONFIG = {
403 | "plane_kwargs" : {
404 | "color" : RED_B
405 | },
406 | "point_charge_loc" : 0.5*RIGHT-1.5*UP,
407 | }
408 | def construct(self):
409 | plane = NumberPlane(**self.plane_kwargs)
410 | #plane.main_lines.fade(.9) #Doesn't work in most recent commit
411 | plane.add(plane.get_axis_labels())
412 | self.add(plane)
413 |
414 | field2D = VGroup(*[self.calc_field2D(x*RIGHT+y*UP)
415 | for x in np.arange(-9,9,1)
416 | for y in np.arange(-5,5,1)
417 | ])
418 |
419 | field3D = VGroup(*[self.calc_field3D(x*RIGHT+y*UP+z*OUT)
420 | for x in np.arange(-9,9,1)
421 | for y in np.arange(-5,5,1)
422 | for z in np.arange(-5,5,1)])
423 |
424 |
425 |
426 | self.play(ShowCreation(field3D))
427 | self.wait()
428 | #self.move_camera(0.8*np.pi/2, -0.45*np.pi) #Doesn't work in most recent commit
429 | self.begin_ambient_camera_rotation()
430 | self.wait(6)
431 |
432 |
433 | def calc_field2D(self,point):
434 | x,y = point[:2]
435 | Rx,Ry = self.point_charge_loc[:2]
436 | r = math.sqrt((x-Rx)**2 + (y-Ry)**2)
437 | efield = (point - self.point_charge_loc)/r**3
438 | return Vector(efield).shift(point)
439 |
440 | def calc_field3D(self,point):
441 | x,y,z = point
442 | Rx,Ry,Rz = self.point_charge_loc
443 | r = math.sqrt((x-Rx)**2 + (y-Ry)**2+(z-Rz)**2)
444 | efield = (point - self.point_charge_loc)/r**3
445 | #efield = np.array((-y,x,z))/math.sqrt(x**2+y**2+z**2)
446 | return Vector(efield).shift(point)
447 |
448 |
449 | class MovingCharges(Scene):
450 | CONFIG = {
451 | "plane_kwargs" : {
452 | "color" : RED_B
453 | },
454 | "point_charge_loc" : 0.5*RIGHT-1.5*UP,
455 | }
456 | def construct(self):
457 | plane = NumberPlane(**self.plane_kwargs)
458 | #plane.main_lines.fade(.9) #Doesn't work in most recent commit
459 | plane.add(plane.get_axis_labels())
460 | self.add(plane)
461 |
462 | field = VGroup(*[self.calc_field(x*RIGHT+y*UP)
463 | for x in np.arange(-9,9,1)
464 | for y in np.arange(-5,5,1)
465 | ])
466 | self.field=field
467 | source_charge = self.Positron().move_to(self.point_charge_loc)
468 | self.play(FadeIn(source_charge))
469 | self.play(ShowCreation(field))
470 | self.moving_charge()
471 |
472 | def calc_field(self,point):
473 | x,y = point[:2]
474 | Rx,Ry = self.point_charge_loc[:2]
475 | r = math.sqrt((x-Rx)**2 + (y-Ry)**2)
476 | efield = (point - self.point_charge_loc)/r**3
477 | return Vector(efield).shift(point)
478 |
479 | def moving_charge(self):
480 | numb_charges=4
481 | possible_points = [v.get_start() for v in self.field]
482 | points = random.sample(possible_points, numb_charges)
483 | particles = VGroup(*[
484 | self.Positron().move_to(point)
485 | for point in points
486 | ])
487 | for particle in particles:
488 | particle.velocity = np.array((0,0,0))
489 |
490 | self.play(FadeIn(particles))
491 | self.moving_particles = particles
492 | self.add_foreground_mobjects(self.moving_particles )
493 | self.always_continually_update = True
494 | self.wait(10)
495 |
496 | def field_at_point(self,point):
497 | x,y = point[:2]
498 | Rx,Ry = self.point_charge_loc[:2]
499 | r = math.sqrt((x-Rx)**2 + (y-Ry)**2)
500 | efield = (point - self.point_charge_loc)/r**3
501 | return efield
502 |
503 | def continual_update(self, *args, **kwargs):
504 | if hasattr(self, "moving_particles"):
505 | dt = self.frame_duration
506 | for p in self.moving_particles:
507 | accel = self.field_at_point(p.get_center())
508 | p.velocity = p.velocity + accel*dt
509 | p.shift(p.velocity*dt)
510 |
511 |
512 | class Positron(Circle):
513 | CONFIG = {
514 | "radius" : 0.2,
515 | "stroke_width" : 3,
516 | "color" : RED,
517 | "fill_color" : RED,
518 | "fill_opacity" : 0.5,
519 | }
520 | def __init__(self, **kwargs):
521 | Circle.__init__(self, **kwargs)
522 | plus = TexMobject("+")
523 | plus.scale(0.7)
524 | plus.move_to(self)
525 | self.add(plus)
526 |
527 | class FieldOfMovingCharge(Scene):
528 | CONFIG = {
529 | "plane_kwargs" : {
530 | "color" : RED_B
531 | },
532 | "point_charge_start_loc" : 5.5*LEFT-1.5*UP,
533 | }
534 | def construct(self):
535 | plane = NumberPlane(**self.plane_kwargs)
536 | #plane.main_lines.fade(.9) #Doesn't work in most recent commit
537 | plane.add(plane.get_axis_labels())
538 | self.add(plane)
539 |
540 | field = VGroup(*[self.create_vect_field(self.point_charge_start_loc,x*RIGHT+y*UP)
541 | for x in np.arange(-9,9,1)
542 | for y in np.arange(-5,5,1)
543 | ])
544 | self.field=field
545 | self.source_charge = self.Positron().move_to(self.point_charge_start_loc)
546 | self.source_charge.velocity = np.array((1,0,0))
547 | self.play(FadeIn(self.source_charge))
548 | self.play(ShowCreation(field))
549 | self.moving_charge()
550 |
551 | def create_vect_field(self,source_charge,observation_point):
552 | return Vector(self.calc_field(source_charge,observation_point)).shift(observation_point)
553 |
554 | def calc_field(self,source_point,observation_point):
555 | x,y,z = observation_point
556 | Rx,Ry,Rz = source_point
557 | r = math.sqrt((x-Rx)**2 + (y-Ry)**2 + (z-Rz)**2)
558 | if r<0.0000001: #Prevent divide by zero
559 | efield = np.array((0,0,0))
560 | else:
561 | efield = (observation_point - source_point)/r**3
562 | return efield
563 |
564 |
565 |
566 | def moving_charge(self):
567 | numb_charges=3
568 | possible_points = [v.get_start() for v in self.field]
569 | points = random.sample(possible_points, numb_charges)
570 | particles = VGroup(self.source_charge, *[
571 | self.Positron().move_to(point)
572 | for point in points
573 | ])
574 | for particle in particles[1:]:
575 | particle.velocity = np.array((0,0,0))
576 | self.play(FadeIn(particles[1:]))
577 | self.moving_particles = particles
578 | self.add_foreground_mobjects(self.moving_particles )
579 | self.always_continually_update = True
580 | self.wait(10)
581 |
582 |
583 | def continual_update(self, *args, **kwargs):
584 | Scene.continual_update(self, *args, **kwargs)
585 | if hasattr(self, "moving_particles"):
586 | dt = self.frame_duration
587 |
588 | for v in self.field:
589 | field_vect=np.zeros(3)
590 | for p in self.moving_particles:
591 | field_vect = field_vect + self.calc_field(p.get_center(), v.get_start())
592 | v.put_start_and_end_on(v.get_start(), field_vect+v.get_start())
593 |
594 | for p in self.moving_particles:
595 | accel = np.zeros(3)
596 | p.velocity = p.velocity + accel*dt
597 | p.shift(p.velocity*dt)
598 |
599 |
600 | class Positron(Circle):
601 | CONFIG = {
602 | "radius" : 0.2,
603 | "stroke_width" : 3,
604 | "color" : RED,
605 | "fill_color" : RED,
606 | "fill_opacity" : 0.5,
607 | }
608 | def __init__(self, **kwargs):
609 | Circle.__init__(self, **kwargs)
610 | plus = TexMobject("+")
611 | plus.scale(0.7)
612 | plus.move_to(self)
613 | self.add(plus)
614 |
615 |
616 | HEAD_INDEX = 0
617 | BODY_INDEX = 1
618 | ARMS_INDEX = 2
619 | LEGS_INDEX = 3
620 |
621 |
622 | class StickMan(SVGMobject):
623 | CONFIG = {
624 | "color" : BLUE_E,
625 | "file_name_prefix": "stick_man",
626 | "stroke_width" : 2,
627 | "stroke_color" : WHITE,
628 | "fill_opacity" : 1.0,
629 | "height" : 3,
630 | }
631 | def __init__(self, mode = "plain", **kwargs):
632 | digest_config(self, kwargs)
633 | self.mode = mode
634 | self.parts_named = False
635 | try:
636 | svg_file = os.path.join(
637 | SVG_IMAGE_DIR,
638 | "%s_%s.svg" % (self.file_name_prefix, mode)
639 | )
640 | SVGMobject.__init__(self, file_name=svg_file, **kwargs)
641 | except:
642 | warnings.warn("No %s design with mode %s" %
643 | (self.file_name_prefix, mode))
644 | svg_file = os.path.join(
645 | SVG_IMAGE_DIR,
646 | "stick_man_plain.svg",
647 | )
648 | SVGMobject.__init__(self, mode="plain", file_name=svg_file, **kwargs)
649 |
650 |
651 | def name_parts(self):
652 | self.head = self.submobjects[HEAD_INDEX]
653 | self.body = self.submobjects[BODY_INDEX]
654 | self.arms = self.submobjects[ARMS_INDEX]
655 | self.legs = self.submobjects[LEGS_INDEX]
656 | self.parts_named = True
657 |
658 | def init_colors(self):
659 | SVGMobject.init_colors(self)
660 | if not self.parts_named:
661 | self.name_parts()
662 | self.head.set_fill(self.color, opacity = 1)
663 | self.body.set_fill(RED, opacity = 1)
664 | self.arms.set_fill(YELLOW, opacity = 1)
665 | self.legs.set_fill(BLUE, opacity = 1)
666 | return self
667 |
668 | class Waving(Scene):
669 | def construct(self):
670 | start_man = StickMan()
671 | plain_man = StickMan()
672 | waving_man = StickMan("wave")
673 |
674 | self.add(start_man)
675 | self.wait()
676 | self.play(Transform(start_man,waving_man))
677 | self.play(Transform(start_man,plain_man))
678 |
679 | self.wait()
680 |
681 | class CirclesAndSquares(SVGMobject):
682 | CONFIG = {
683 | "color" : BLUE_E,
684 | "file_name_prefix": "circles_and_squares",
685 | "stroke_width" : 2,
686 | "stroke_color" : WHITE,
687 | "fill_opacity" : 1.0,
688 | "height" : 3,
689 | "start_corner" : None,
690 | "circle_index" : 0,
691 | "line1_index" :1,
692 | "line2_index" : 2,
693 | "square1_index" : 3,
694 | "square2_index" : 4,
695 | }
696 | def __init__(self, mode = "plain", **kwargs):
697 | digest_config(self, kwargs)
698 | self.mode = mode
699 | self.parts_named = False
700 | try:
701 | svg_file = os.path.join(
702 | SVG_IMAGE_DIR,
703 | "%s_%s.svg" % (self.file_name_prefix, mode)
704 | )
705 | SVGMobject.__init__(self, file_name=svg_file, **kwargs)
706 | except:
707 | warnings.warn("No %s design with mode %s" %
708 | (self.file_name_prefix, mode))
709 | svg_file = os.path.join(
710 | SVG_IMAGE_DIR,
711 | "circles_and_squares_plain.svg",
712 | )
713 | SVGMobject.__init__(self, mode="plain", file_name=svg_file, **kwargs)
714 |
715 |
716 | def name_parts(self):
717 | self.circle = self.submobjects[self.circle_index]
718 | self.line1 = self.submobjects[self.line1_index]
719 | self.line2 = self.submobjects[self.line2_index]
720 | self.square1 = self.submobjects[self.square1_index]
721 | self.square2 = self.submobjects[self.square2_index]
722 | self.parts_named = True
723 |
724 | def init_colors(self):
725 | SVGMobject.init_colors(self)
726 | self.name_parts()
727 | self.circle.set_fill(RED, opacity = 1)
728 | self.line1.set_fill(self.color, opacity = 0)
729 | self.line2.set_fill(self.color, opacity = 0)
730 | self.square1.set_fill(GREEN, opacity = 1)
731 | self.square2.set_fill(BLUE, opacity = 1)
732 | return self
733 |
734 |
735 | class SVGCircleAndSquare(Scene):
736 | def construct(self):
737 | thingy = CirclesAndSquares()
738 |
739 | self.add(thingy)
740 | self.wait()
741 |
742 | if __name__ == "__main__":
743 | # Call this file at command line to make sure all scenes work with version of manim
744 | # type "python manim_tutorial_P37.py" at command line to run all scenes in this file
745 | #Must have "import os" and "import pyclbr" at start of file to use this
746 | ###Using Python class browser to determine which classes are defined in this file
747 | module_name = 'manim_tutorial_P37' #Name of current file
748 | module_info = pyclbr.readmodule(module_name)
749 |
750 | for item in module_info.values():
751 | if item.module==module_name:
752 | print(item.name)
753 | os.system("python -m manim manim_tutorial_P37.py %s -l" % item.name) #Does not play files
754 |
755 |
756 |
--------------------------------------------------------------------------------
/stick_man_plain.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
79 |
--------------------------------------------------------------------------------
/stick_man_wave.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
83 |
--------------------------------------------------------------------------------