├── 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 | 19 | 21 | 39 | 41 | 42 | 44 | image/svg+xml 45 | 47 | 48 | 49 | 50 | 51 | 55 | 61 | 66 | 71 | 78 | 85 | 86 | 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 | 19 | 21 | 39 | 41 | 42 | 44 | image/svg+xml 45 | 47 | 48 | 49 | 50 | 51 | 55 | 62 | 67 | 72 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /stick_man_wave.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 39 | 41 | 42 | 44 | image/svg+xml 45 | 47 | 48 | 49 | 50 | 51 | 55 | 57 | 64 | 69 | 74 | 80 | 81 | 82 | 83 | --------------------------------------------------------------------------------