diff --git a/eoc/chapter4.py b/eoc/chapter4.py index 6ece758d..b1978396 100644 --- a/eoc/chapter4.py +++ b/eoc/chapter4.py @@ -1676,9 +1676,10 @@ class GeneralizeChainRule(Scene): ) general = TexMobject( "\\frac{d}{dx}", "g(", "h(x)", ")", "=", - "{dg \\over ", " dh}(", "h(x)", ")", "{dh \\over", " dx}", "(x)" + "{dg \\over ", " dh}", "(", "h(x)", ")", "{dh \\over", " dx}", "(x)" ) example.to_edge(UP, buff = LARGE_BUFF) + example.shift(RIGHT) general.next_to(example, DOWN, buff = 1.5*LARGE_BUFF) for mob in example, general: mob.highlight(SINE_COLOR) @@ -1694,24 +1695,26 @@ class GeneralizeChainRule(Scene): general_outer = VGroup(*general[1:4]) general_inner = general[2] - d_general_outer = VGroup(*general[5:9]) - d_general_inner = general[7] - d_general_d_inner = VGroup(*general[9:12]) + d_general_outer = VGroup(*general[5:10]) + d_general_inner = general[8] + d_general_d_inner = VGroup(*general[10:13]) example_outer_brace = Brace(example_outer) - example_inner_brace = Brace(example_inner, buff = SMALL_BUFF) + example_inner_brace = Brace(example_inner, UP, buff = SMALL_BUFF) d_example_outer_brace = Brace(d_example_outer) d_example_inner_brace = Brace(d_example_inner, buff = SMALL_BUFF) d_example_d_inner_brace = Brace(d_example_d_inner, UP, buff = SMALL_BUFF) general_outer_brace = Brace(general_outer) - general_inner_brace = Brace(general_inner, buff = SMALL_BUFF) + general_inner_brace = Brace(general_inner, UP, buff = SMALL_BUFF) d_general_outer_brace = Brace(d_general_outer) d_general_inner_brace = Brace(d_general_inner, buff = SMALL_BUFF) d_general_d_inner_brace = Brace(d_general_d_inner, UP, buff = SMALL_BUFF) for brace in example_outer_brace, general_outer_brace: brace.text = brace.get_text("Outer") + for brace in example_inner_brace, general_inner_brace: + brace.text = brace.get_text("Inner") for brace in d_example_outer_brace, d_general_outer_brace: brace.text = brace.get_text("d(Outer)") brace.text.shift(SMALL_BUFF*LEFT) @@ -1720,12 +1723,16 @@ class GeneralizeChainRule(Scene): #d(out)d(in) for example self.add(example) - outer_braces = VGroup(example_outer_brace, d_example_outer_brace) - self.play(*map(GrowFromCenter, outer_braces)) - for brace in outer_braces: + braces = VGroup( + example_outer_brace, + example_inner_brace, + d_example_outer_brace + ) + for brace in braces: + self.play(GrowFromCenter(brace)) self.play(Write(brace.text, run_time = 1)) + self.dither() self.dither() - inner_braces = VGroup(example_inner_brace, d_example_inner_brace) self.play(*it.chain(*[ [mob.scale_in_place, 1.2, mob.highlight, YELLOW] for mob in example_inner, d_example_inner @@ -1750,12 +1757,247 @@ class GeneralizeChainRule(Scene): self.dither() #Generalize - - - - - - + self.play(*map(FadeIn, general[:5])) + self.dither() + self.play( + Transform(example_outer_brace, general_outer_brace), + Transform(example_outer_brace.text, general_outer_brace.text), + Transform(example_inner_brace, general_inner_brace), + Transform(example_inner_brace.text, general_inner_brace.text), + ) + self.dither() + self.play( + Transform(d_example_outer_brace, d_general_outer_brace), + Transform(d_example_outer_brace.text, d_general_outer_brace.text), + ) + self.play(Write(d_general_outer)) + self.dither(2) + self.play( + Transform(d_example_d_inner_brace, d_general_d_inner_brace), + Transform(d_example_d_inner_brace.text, d_general_d_inner_brace.text), + ) + self.play(Write(d_general_d_inner)) + self.dither(2) + + #Name chain rule + name = TextMobject("``Chain rule''") + name.scale(1.2) + name.highlight(YELLOW) + name.to_corner(UP+LEFT) + self.play(Write(name)) + self.dither() + + #Point out dh bottom + morty = Mortimer().flip() + morty.to_corner(DOWN+LEFT) + d_general_outer_copy = d_general_outer.copy() + morty.set_fill(opacity = 0) + self.play( + morty.set_fill, None, 1, + morty.change_mode, "raise_left_hand", + morty.look, UP+LEFT, + d_general_outer_copy.next_to, + morty.get_corner(UP+LEFT), UP, MED_LARGE_BUFF, + d_general_outer_copy.shift_onto_screen + ) + self.dither() + circle = Circle(color = YELLOW) + circle.replace(d_general_outer_copy[1]) + circle.scale_in_place(1.4) + self.play(ShowCreation(circle)) + self.play(Blink(morty)) + self.dither() + inner = d_general_outer_copy[3] + self.play( + morty.change_mode, "hooray", + morty.look_at, inner, + inner.shift, UP + ) + self.play(inner.shift, DOWN) + self.dither() + self.play(morty.change_mode, "pondering") + self.play(Blink(morty)) + self.dither() + self.play(*map(FadeOut, [ + d_general_outer_copy, inner, circle + ])) + + #Show cancelation + braces = [ + d_example_d_inner_brace, + d_example_outer_brace, + example_inner_brace, + example_outer_brace, + ] + texts = [brace.text for brace in braces] + self.play(*map(FadeOut, braces+texts)) + + to_collapse = VGroup(VGroup(*general[7:10]), general[12]) + dg_dh = VGroup(*general[5:7]) + dh_dx = VGroup(*general[10:12]) + to_collapse.generate_target() + points = VGroup(*map(VectorizedPoint, + [m.get_left() for m in to_collapse] + )) + self.play( + Transform(to_collapse, points), + dh_dx.next_to, dg_dh, + morty.look_at, dg_dh, + ) + self.dither() + for mob in list(dg_dh)+list(dh_dx): + circle = Circle(color = YELLOW) + circle.replace(mob) + circle.scale_in_place(1.3) + self.play(ShowCreation(circle)) + self.dither() + self.play(FadeOut(circle)) + + strikes = VGroup() + for dh in dg_dh[1], dh_dx[0]: + strike = TexMobject("/") + strike.stretch(2, dim = 0) + strike.rotate(-np.pi/12) + strike.move_to(dh) + strike.highlight(RED) + strikes.add(strike) + self.play(Write(strikes)) + self.play(morty.change_mode, "hooray") + equals_dg_dx = TexMobject("= \\frac{dg}{dx}") + equals_dg_dx.next_to(dh_dx) + self.play(Write(equals_dg_dx)) + self.play(Blink(morty)) + self.dither() + +class WatchingVideo(PiCreatureScene): + def construct(self): + randy = self.get_primary_pi_creature() + laptop = Laptop() + laptop.next_to(randy.get_corner(UP+RIGHT), RIGHT) + randy.look_at(laptop.screen) + + formulas = VGroup(*[ + TexMobject("\\frac{d}{dx}\\left( %s \\right)"%s) + for s in [ + "e^x \\sin(x)", + "\\sin(x) \\cdot \\frac{1}{\\cos(x)}", + "\\cos(3x)^2", + "e^x(x^2 + 3x + 2)", + ] + ]) + formulas.arrange_submobjects( + DOWN, + buff = MED_LARGE_BUFF, + aligned_edge = LEFT + ) + formulas.next_to(randy, LEFT, buff = MED_LARGE_BUFF) + formulas.shift_onto_screen() + + self.add(randy, laptop) + self.dither() + self.play(randy.change_mode, "erm") + self.play(Blink(randy)) + self.dither() + self.play(randy.change_mode, "maybe") + self.dither() + self.play(Blink(randy)) + for formula in formulas: + self.play( + Write(formula, run_time = 2), + randy.change_mode, "thinking" + ) + self.dither() + + def create_pi_creatures(self): + return [Randolph().shift(DOWN+RIGHT)] + + + +class NextVideo(TeacherStudentsScene): + def construct(self): + series = VideoSeries() + series.to_edge(UP) + next_video = series[4] + + pre_expression = TexMobject( + "x", "^2", "+", "y", "^2", "=", "1" + ) + d_expression = TexMobject( + "2", "x", "\\,dx", "+", "2", "y", "\\,dy", "=", "0" + ) + expression_to_d_expression_indices = [ + 1, 0, 0, 2, 4, 3, 3, 5, 6 + ] + expression = VGroup() + for i, j in enumerate(expression_to_d_expression_indices): + submob = pre_expression[j].copy() + if d_expression.expression_parts[i] == "2": + two = TexMobject("2") + two.replace(submob) + expression.add(two) + else: + expression.add(submob) + + for mob in expression, d_expression: + mob.scale(1.2) + mob.next_to( + self.get_teacher().get_corner(UP+LEFT), UP, + buff = MED_LARGE_BUFF + ) + mob.shift_onto_screen() + + self.add(series) + self.play( + next_video.shift, 0.5*DOWN, + next_video.highlight, YELLOW, + self.get_teacher().change_mode, "raise_right_hand" + ) + self.dither() + self.play( + Write(expression), + *[ + ApplyMethod(pi.change_mode, "pondering") + for pi in self.get_students() + ] + ) + self.dither() + self.remove(expression) + self.play(Transform(expression, d_expression, path_arc = np.pi/2)) + self.dither(3) + +class Chapter4Thanks(PatreonThanks): + CONFIG = { + "specific_patrons" : [ + "Ali Yahya", + "Meshal Alshammari", + "CrypticSwarm ", + "Ankit Agarwal", + "Yu Jun", + "Shelby Doolittle", + "Dave Nicponski", + "Damion Kistler", + "Juan Benet", + "Othman Alikhan", + "Justin Helps", + "Markus Persson", + "Dan Buchoff", + "Derek Dai", + "Joseph John Cox", + "Luc Ritchie", + "Nils Schneider", + "Mathew Bramson", + "Guido Gambardella", + "Jerry Ling", + "Mark Govea", + "Vecht", + "Shimin Kuang", + "Rish Kundalia", + "Achille Brighton", + "Kirk Werklund", + "Ripta Pasay", + "Felipe Diniz", + ] + } diff --git a/topics/objects.py b/topics/objects.py index 8252749d..a62f64a9 100644 --- a/topics/objects.py +++ b/topics/objects.py @@ -8,9 +8,83 @@ from mobject.tex_mobject import TextMobject from animation import Animation from animation.simple_animations import Rotating -from topics.geometry import Circle, Line +from topics.geometry import Circle, Line, Rectangle, Square +from topics.three_dimensions import Cube +class Laptop(VGroup): + CONFIG = { + "width" : 3, + "body_dimensions" : [4, 3, 0.05], + "screen_thickness" : 0.01, + "keyboard_width_to_body_width" : 0.9, + "keyboard_height_to_body_height" : 0.5, + "screen_width_to_screen_plate_width" : 0.9, + "key_color_kwargs" : { + "stroke_width" : 0, + "fill_color" : BLACK, + "fill_opacity" : 1, + }, + "body_color" : LIGHT_GREY, + "shaded_body_color" : GREY, + "open_angle" : np.pi/4, + } + def generate_points(self): + body = Cube(side_length = 1) + for dim, scale_factor in enumerate(self.body_dimensions): + body.stretch(scale_factor, dim = dim) + body.scale_to_fit_width(self.width) + body.set_fill(self.shaded_body_color, opacity = 1) + body.sort_submobjects(lambda p : p[2]) + body[-1].set_fill(self.body_color) + keyboard = VGroup(*[ + VGroup(*[ + Square(**self.key_color_kwargs) + for x in range(12-y%2) + ]).arrange_submobjects(RIGHT, buff = SMALL_BUFF) + for y in range(4) + ]).arrange_submobjects(DOWN, buff = MED_SMALL_BUFF) + keyboard.stretch_to_fit_width( + self.keyboard_width_to_body_width*body.get_width(), + ) + keyboard.stretch_to_fit_height( + self.keyboard_height_to_body_height*body.get_height(), + ) + keyboard.next_to(body, OUT, buff = 0.1*SMALL_BUFF) + keyboard.shift(MED_SMALL_BUFF*UP) + body.add(keyboard) + + screen_plate = body.copy() + screen_plate.stretch(self.screen_thickness/self.body_dimensions[2], dim = 2) + screen = Rectangle( + stroke_width = 0, + fill_color = BLACK, + fill_opacity = 1, + ) + screen.replace(screen_plate, stretch = True) + screen.scale_in_place(self.screen_width_to_screen_plate_width) + screen.next_to(screen_plate, OUT, buff = 0.1*SMALL_BUFF) + screen_plate.add(screen) + screen_plate.next_to(body, UP, buff = 0) + screen_plate.rotate( + self.open_angle, RIGHT, + about_point = screen_plate.get_bottom() + ) + self.screen_plate = screen_plate + self.screen = screen + + axis = Line( + body.get_corner(UP+LEFT+OUT), + body.get_corner(UP+RIGHT+OUT), + color = BLACK, + stroke_width = 2 + ) + self.axis = axis + + self.add(body, screen_plate, axis) + self.rotate(5*np.pi/12, LEFT) + self.rotate(np.pi/6, DOWN) + class PatreonLogo(SVGMobject): CONFIG = { "file_name" : "patreon_logo", diff --git a/topics/three_dimensions.py b/topics/three_dimensions.py index a849e7c4..8ceda8a6 100644 --- a/topics/three_dimensions.py +++ b/topics/three_dimensions.py @@ -16,8 +16,11 @@ class ThreeDCamera(Camera): self.unit_sun_vect = self.sun_vect/np.linalg.norm(self.sun_vect) def display_multiple_vectorized_mobjects(self, vmobjects): - def cmp_vmobs(vm1, vm2): - return cmp(vm1.get_center()[2], vm2.get_center()[2]) + def cmp_vmobs(*vmobs): + return cmp(*[ + vm.get_edge_center(IN)[2] + for vm in vmobs + ]) Camera.display_multiple_vectorized_mobjects( self, sorted(vmobjects, cmp = cmp_vmobs) @@ -73,13 +76,12 @@ class Cube(VGroup): "side_length" : 2, } def generate_points(self): - faces = [ - Square(side_length = self.side_length).shift(OUT).apply_function( - lambda p : np.dot(p, z_to_vector(vect).T) - ) - for vect in IN, OUT, LEFT, RIGHT, UP, DOWN - ] - self.add(*faces) + for vect in IN, OUT, LEFT, RIGHT, UP, DOWN: + face = Square(side_length = self.side_length) + face.shift(self.side_length*OUT/2.0) + face.apply_function(lambda p : np.dot(p, z_to_vector(vect).T)) + + self.add(face)