from big_ol_pile_of_manim_imports import * def apply_function_to_center(point_func, mobject): mobject.apply_function_to_position(point_func) def apply_function_to_submobjects(point_func, mobject): mobject.apply_function_to_submobject_positions(point_func) def apply_function_to_points(point_func, mobject): mobject.apply_function(point_func) class NumberlineTransformationScene(ZoomedScene): CONFIG = { "input_line_zero_point": 0.5 * UP + (FRAME_X_RADIUS - 1) * LEFT, "output_line_zero_point": 2 * DOWN + (FRAME_X_RADIUS - 1) * LEFT, "number_line_config": { "include_numbers": True, "x_min": -3.5, "x_max": 3.5, "unit_size": 2, }, # These would override number_line_config "input_line_config": { "color": BLUE, }, "output_line_config": {}, "num_inserted_number_line_anchors": 20, "default_delta_x": 0.1, "default_sample_dot_radius": 0.07, "default_sample_dot_colors": [RED, YELLOW], "default_mapping_animation_config": { "run_time": 3, # "path_arc": 30 * DEGREES, }, "local_coordinate_num_decimal_places": 2, "zoom_factor": 0.1, "zoomed_display_height": 2.5, "zoomed_display_corner_buff": MED_SMALL_BUFF, "mini_line_scale_factor": 2, "default_coordinate_value_dx": 0.05, "zoomed_camera_background_rectangle_fill_opacity": 1.0, } def setup(self): ZoomedScene.setup(self) self.setup_number_lines() self.setup_titles() self.setup_zoomed_camera_background_rectangle() def setup_number_lines(self): number_lines = self.number_lines = VGroup() added_configs = (self.input_line_config, self.output_line_config) zero_opints = (self.input_line_zero_point, self.output_line_zero_point) for added_config, zero_point in zip(added_configs, zero_opints): full_config = dict(self.number_line_config) full_config.update(added_config) number_line = NumberLine(**full_config) number_line.main_line.insert_n_anchor_points( self.num_inserted_number_line_anchors ) number_line.shift(zero_point - number_line.number_to_point(0)) number_lines.add(number_line) self.input_line, self.output_line = number_lines self.add(number_lines) def setup_titles(self): input_title, output_title = self.titles = VGroup(*[ TextMobject(word) for word in "Inputs", "Outputs" ]) vects = [UP, DOWN] for title, line, vect in zip(self.titles, self.number_lines, vects): title.next_to(line, vect, aligned_edge=LEFT) title.shift_onto_screen() self.add(self.titles) def setup_zoomed_camera_background_rectangle(self): frame = self.zoomed_camera.frame frame.next_to(self.camera.frame, UL) self.zoomed_camera_background_rectangle = BackgroundRectangle( frame, fill_opacity=self.zoomed_camera_background_rectangle_fill_opacity ) self.zoomed_camera_background_rectangle_anim = UpdateFromFunc( self.zoomed_camera_background_rectangle, lambda m: m.replace(frame, stretch=True) ) self.zoomed_camera_background_rectangle_group = VGroup( self.zoomed_camera_background_rectangle, ) def get_sample_input_points(self, x_min=None, x_max=None, delta_x=None): x_min = x_min or self.input_line.x_min x_max = x_max or self.input_line.x_max delta_x = delta_x or self.default_delta_x return [ self.get_input_point(x) for x in np.arange(x_min, x_max + delta_x, delta_x) ] def get_sample_dots(self, x_min=None, x_max=None, delta_x=None, dot_radius=None, colors=None): dot_radius = dot_radius or self.default_sample_dot_radius colors = colors or self.default_sample_dot_colors dots = VGroup(*[ Dot(point, radius=dot_radius) for point in self.get_sample_input_points(x_min, x_max, delta_x) ]) dots.set_color_by_gradient(*colors) return dots def get_local_sample_dots(self, x, sample_radius=None, **kwargs): zoom_factor = self.get_zoom_factor() delta_x = kwargs.get("delta_x", self.default_delta_x * zoom_factor) dot_radius = kwargs.get("dot_radius", self.default_sample_dot_radius * zoom_factor) if sample_radius is None: unrounded_radius = self.zoomed_camera.frame.get_width() / 2 sample_radius = int(unrounded_radius / delta_x) * delta_x config = { "x_min": x - sample_radius, "x_max": x + sample_radius, "delta_x": delta_x, "dot_radius": dot_radius, } config.update(kwargs) return self.get_sample_dots(**config) def get_local_coordinate_values(self, x, dx=None, n_neighbors=1): dx = dx or self.default_coordinate_value_dx return [ x + n * dx for n in range(-n_neighbors, n_neighbors + 1) ] # Mapping animations def get_mapping_animation(self, func, mobject, how_to_apply_func=apply_function_to_center, **kwargs): anim_config = dict(self.default_mapping_animation_config) anim_config.update(kwargs) point_func = self.number_func_to_point_func(func) mobject.generate_target(use_deepcopy=True) how_to_apply_func(point_func, mobject.target) return MoveToTarget(mobject, **anim_config) def get_line_mapping_animation(self, func, **kwargs): input_line_copy = self.input_line.deepcopy() self.moving_input_line = input_line_copy input_line_copy.remove(input_line_copy.numbers) # input_line_copy.set_stroke(width=2) input_line_copy.main_line.insert_n_anchor_points( self.num_inserted_number_line_anchors ) return AnimationGroup( self.get_mapping_animation( func, input_line_copy.main_line, apply_function_to_points ), self.get_mapping_animation( func, input_line_copy.tick_marks, apply_function_to_submobjects ), ) def get_sample_dots_mapping_animation(self, func, dots, **kwargs): return self.get_mapping_animation( func, dots, how_to_apply_func=apply_function_to_submobjects ) def get_zoomed_camera_frame_mapping_animation(self, func, x=None, **kwargs): frame = self.zoomed_camera.frame if x is None: point = frame.get_center() else: point = self.get_input_point(x) point_mob = VectorizedPoint(point) return AnimationGroup( self.get_mapping_animation(func, point_mob), UpdateFromFunc(frame, lambda m: m.move_to(point_mob)), ) def apply_function(self, func, apply_function_to_number_line=True, sample_dots=None, local_sample_dots=None, target_coordinate_values=None, added_anims=None ): zcbr_group = self.zoomed_camera_background_rectangle_group zcbr_anim = self.zoomed_camera_background_rectangle_anim frame = self.zoomed_camera.frame anims = [] if apply_function_to_number_line: anims.append(self.get_line_mapping_animation(func)) if hasattr(self, "mini_line"): # Test for if mini_line is in self? anims.append(self.get_mapping_animation( func, self.mini_line, how_to_apply_func=apply_function_to_center )) if sample_dots: anims.append( self.get_sample_dots_mapping_animation(func, sample_dots) ) if self.zoom_activated: zoom_anim = self.get_zoomed_camera_frame_mapping_animation(func) anims.append(zoom_anim) anims.append(zcbr_anim) zoom_anim.update(1) target_mini_line = Line(frame.get_left(), frame.get_right()) target_mini_line.scale(self.mini_line_scale_factor) target_mini_line.match_style(self.output_line.main_line) zoom_anim.update(0) zcbr_group.submobjects.insert(1, target_mini_line) if target_coordinate_values: coordinates = self.get_local_coordinates( self.output_line, *target_coordinate_values ) anims.append(FadeIn(coordinates)) zcbr_group.add(coordinates) self.local_target_coordinates = coordinates if local_sample_dots: anims.append( self.get_sample_dots_mapping_animation(func, local_sample_dots) ) zcbr_group.add(local_sample_dots) if added_anims: anims += added_anims anims.append(Animation(zcbr_group)) self.play(*anims) # Zooming def zoom_in_on_input(self, x, local_sample_dots=None, local_coordinate_values=None, pop_out=True, first_added_anims=[], second_added_anims=[], ): input_point = self.get_input_point(x) # Decide how to move camera frame into place frame = self.zoomed_camera.frame movement = ApplyMethod(frame.move_to, input_point) zcbr = self.zoomed_camera_background_rectangle zcbr_group = self.zoomed_camera_background_rectangle_group zcbr_anim = self.zoomed_camera_background_rectangle_anim anims = [] if self.zoom_activated: anims.append(movement) anims.append(zcbr_anim) else: movement.update(1) zcbr_anim.update(1) anims.append(self.get_zoom_in_animation()) anims.append(FadeIn(zcbr)) # Make sure frame is in final place for anim in anims: anim.update(1) # Add miniature number_line mini_line = self.mini_line = Line(frame.get_left(), frame.get_right()) mini_line.scale(self.mini_line_scale_factor) mini_line.insert_n_anchor_points(self.num_inserted_number_line_anchors) mini_line.match_style(self.input_line.main_line) mini_line_copy = mini_line.copy() zcbr_group.add(mini_line_copy, mini_line) anims += [FadeIn(mini_line), FadeIn(mini_line_copy)] # Add tiny coordiantes if local_coordinate_values is None: local_coordinate_values = [x] local_coordinates = self.get_local_coordinates( self.input_line, *local_coordinate_values ) anims.append(FadeIn(local_coordinates)) zcbr_group.add(local_coordinates) self.local_coordinates = local_coordinates # Add tiny dots if local_sample_dots is not None: anims.append(LaggedStart(GrowFromCenter, local_sample_dots)) zcbr_group.add(local_sample_dots) if first_added_anims: anims += first_added_anims anims.append(Animation(zcbr_group)) if not pop_out: self.activate_zooming(animate=False) self.play(*anims) if not self.zoom_activated and pop_out: self.activate_zooming(animate=False) added_anims = second_added_anims or [] self.play( self.get_zoomed_display_pop_out_animation(), *added_anims ) def get_local_coordinates(self, line, *x_values, **kwargs): num_decimal_places = kwargs.get( "num_decimal_places", self.local_coordinate_num_decimal_places ) result = VGroup() result.tick_marks = VGroup() result.numbers = VGroup() result.add(result.tick_marks, result.numbers) for x in x_values: tick_mark = Line(UP, DOWN) tick_mark.scale_to_fit_height( 0.15 * self.zoomed_camera.frame.get_height() ) tick_mark.move_to(line.number_to_point(x)) result.tick_marks.add(tick_mark) number = DecimalNumber(x, num_decimal_places=num_decimal_places) number.scale(self.get_zoom_factor()) number.scale(0.5) # To make it seem small number.next_to(tick_mark, DOWN, buff=0.5 * number.get_height()) result.numbers.add(number) return result def get_mobjects_in_zoomed_camera(self, mobjects): frame = self.zoomed_camera.frame x_min = frame.get_left()[0] x_max = frame.get_right()[0] y_min = frame.get_bottom()[1] y_max = frame.get_top()[1] result = VGroup() for mob in mobjects: for point in mob.get_all_points(): if (x_min < point[0] < x_max) and (y_min < point[1] < y_max): result.add(mob) break return result # Helpers def get_input_point(self, x): return self.input_line.number_to_point(x) def get_output_point(self, fx): return self.output_line.number_to_point(fx) def number_func_to_point_func(self, number_func): input_line, output_line = self.number_lines def point_func(point): input_number = input_line.point_to_number(point) output_number = number_func(input_number) return output_line.number_to_point(output_number) return point_func class ExampleNumberlineTransformationScene(NumberlineTransformationScene): CONFIG = { "number_line_config": { "x_min": 0, "x_max": 5, "unit_size": 2.0 }, "output_line_config": { "x_max": 20, }, } def construct(self): func = lambda x: x**2 x = 3 dx = 0.05 sample_dots = self.get_sample_dots() local_sample_dots = self.get_local_sample_dots(x) self.play(LaggedStart(GrowFromCenter, sample_dots)) self.zoom_in_on_input( x, local_sample_dots=local_sample_dots, local_coordinate_values=[x - dx, x, x + dx], ) self.wait() self.apply_function( func, sample_dots=sample_dots, local_sample_dots=local_sample_dots, target_coordinate_values=[func(x) - dx, func(x), func(x) + dx], ) self.wait() # Scenes class WriteOpeningWords(Scene): def construct(self): raw_string1 = "Dear calculus student," raw_string2 = "You're about to go through your first course. Like " + \ "any new topic, it will take some hard work to understand," words1, words2 = [ TextMobject("\\Large", *rs.split(" ")) for rs in raw_string1, raw_string2 ] words1.next_to(words2, UP, aligned_edge=LEFT, buff=LARGE_BUFF) words = VGroup(*it.chain(words1, words2)) words.scale_to_fit_width(FRAME_WIDTH - 2 * LARGE_BUFF) words.to_edge(UP) letter_wait = 0.05 word_wait = 2 * letter_wait comma_wait = 5 * letter_wait for word in words: self.play(LaggedStart( FadeIn, word, run_time=len(word) * letter_wait, lag_ratio=1.5 / len(word) )) self.wait(word_wait) if word.get_tex_string()[-1] == ",": self.wait(comma_wait) class StartingCalc101(PiCreatureScene): CONFIG = { } def construct(self): randy = self.pi_creature deriv_equation = TexMobject( "\\frac{df}{dx}(x) = \\lim_{\\Delta x \\to \\infty}" + "{f(x + \\Delta x) - f(x) \\over \\Delta x}", tex_to_color_map={"\\Delta x": BLUE} ) title = TextMobject("Calculus 101") title.to_edge(UP) h_line = Line(LEFT, RIGHT) h_line.scale_to_fit_width(FRAME_WIDTH - LARGE_BUFF) h_line.next_to(title, DOWN) self.add(title, h_line) self.play(randy.change, "erm", title) self.wait() class StandardDerivativeVisual(GraphScene): CONFIG = { "y_max": 8, "y_axis_height": 5, } def construct(self): self.add_title() self.show_function_graph() self.show_slope_of_graph() self.encourage_not_to_think_of_slope_as_definition() self.show_sensitivity() def add_title(self): title = self.title = TextMobject("Standard derivative visual") title.to_edge(UP) h_line = Line(LEFT, RIGHT) h_line.scale_to_fit_width(FRAME_WIDTH - 2 * LARGE_BUFF) h_line.next_to(title, DOWN) self.add(title, h_line) def show_function_graph(self): self.setup_axes() def func(x): x -= 5 return 0.1 * (x + 3) * (x - 3) * x + 3 graph = self.get_graph(func) graph_label = self.get_graph_label(graph, x_val=9.5) input_tracker = ValueTracker(4) def get_x_value(): return input_tracker.get_value() def get_y_value(): return graph.underlying_function(get_x_value()) def get_x_point(): return self.coords_to_point(get_x_value(), 0) def get_y_point(): return self.coords_to_point(0, get_y_value()) def get_graph_point(): return self.coords_to_point(get_x_value(), get_y_value()) def get_v_line(): return DashedLine(get_x_point(), get_graph_point(), stroke_width=2) def get_h_line(): return DashedLine(get_graph_point(), get_y_point(), stroke_width=2) input_triangle = RegularPolygon(n=3, start_angle=TAU / 4) output_triangle = RegularPolygon(n=3, start_angle=0) for triangle in input_triangle, output_triangle: triangle.set_fill(WHITE, 1) triangle.set_stroke(width=0) triangle.scale(0.1) input_triangle_update = ContinualUpdateFromFunc( input_triangle, lambda m: m.move_to(get_x_point(), UP) ) output_triangle_update = ContinualUpdateFromFunc( output_triangle, lambda m: m.move_to(get_y_point(), RIGHT) ) x_label = TexMobject("x") x_label_update = ContinualUpdateFromFunc( x_label, lambda m: m.next_to(input_triangle, DOWN, SMALL_BUFF) ) output_label = TexMobject("f(x)") output_label_update = ContinualUpdateFromFunc( output_label, lambda m: m.next_to( output_triangle, LEFT, SMALL_BUFF) ) v_line = get_v_line() v_line_update = ContinualUpdateFromFunc( v_line, lambda vl: Transform(vl, get_v_line()).update(1) ) h_line = get_h_line() h_line_update = ContinualUpdateFromFunc( h_line, lambda hl: Transform(hl, get_h_line()).update(1) ) graph_dot = Dot(color=YELLOW) graph_dot_update = ContinualUpdateFromFunc( graph_dot, lambda m: m.move_to(get_graph_point()) ) self.play( ShowCreation(graph), Write(graph_label), ) self.play( DrawBorderThenFill(input_triangle, run_time=1), Write(x_label), ShowCreation(v_line), GrowFromCenter(graph_dot), ) self.add_foreground_mobject(graph_dot) self.play( ShowCreation(h_line), Write(output_label), DrawBorderThenFill(output_triangle, run_time=1) ) self.add( input_triangle_update, x_label_update, graph_dot_update, v_line_update, h_line_update, output_triangle_update, output_label_update, ) self.play( input_tracker.set_value, 8, run_time=6, rate_func=there_and_back ) self.input_tracker = input_tracker self.graph = graph def show_slope_of_graph(self): input_tracker = self.input_tracker deriv_input_tracker = ValueTracker(input_tracker.get_value()) # Slope line def get_slope_line(): return self.get_secant_slope_group( x=deriv_input_tracker.get_value(), graph=self.graph, dx=0.01, secant_line_length=4 ).secant_line slope_line = get_slope_line() slope_line_update = ContinualUpdateFromFunc( slope_line, lambda sg: Transform(sg, get_slope_line()).update(1) ) def position_deriv_label(deriv_label): deriv_label.next_to(slope_line, UP) return deriv_label deriv_label = TexMobject( "\\frac{df}{dx}(x) =", "\\text{Slope}", "=" ) deriv_label.get_part_by_tex("Slope").match_color(slope_line) deriv_label_update = ContinualUpdateFromFunc( deriv_label, position_deriv_label ) slope_decimal = DecimalNumber(slope_line.get_slope()) slope_decimal.match_color(slope_line) slope_decimal_update = ContinualChangingDecimal( slope_decimal, lambda dt: slope_line.get_slope(), position_update_func=lambda m: m.next_to( deriv_label, RIGHT, SMALL_BUFF ).shift(0.2 * SMALL_BUFF * DOWN) ) self.play( ShowCreation(slope_line), Write(deriv_label), Write(slope_decimal), run_time=1 ) self.wait() self.add( slope_line_update, # deriv_label_update, slope_decimal_update, ) for x in 9, 2, 4: self.play( input_tracker.set_value, x, deriv_input_tracker.set_value, x, run_time=3 ) self.wait() self.deriv_input_tracker = deriv_input_tracker def encourage_not_to_think_of_slope_as_definition(self): morty = Mortimer(height=2) morty.to_corner(DR) self.play(FadeIn(morty)) self.play(PiCreatureSays( morty, "Don't think of \\\\ this as the definition", bubble_kwargs={"height": 2, "width": 4} )) self.play(Blink(morty)) self.wait() self.play( RemovePiCreatureBubble(morty), UpdateFromAlphaFunc( morty, lambda m, a: m.set_fill(opacity=1 - a), remover=True ) ) def show_sensitivity(self): input_tracker = self.input_tracker deriv_input_tracker = self.deriv_input_tracker self.wiggle_input() for x in 9, 7, 2: self.play( input_tracker.set_value, x, deriv_input_tracker.set_value, x, run_time=3 ) self.wiggle_input() ### def wiggle_input(self, dx=0.5, run_time=3): input_tracker = self.input_tracker x = input_tracker.get_value() x_min = x - dx x_max = x + dx y, y_min, y_max = map( self.graph.underlying_function, [x, x_min, x_max] ) x_line = Line( self.coords_to_point(x_min, 0), self.coords_to_point(x_max, 0), ) y_line = Line( self.coords_to_point(0, y_min), self.coords_to_point(0, y_max), ) x_rect, y_rect = rects = VGroup(Rectangle(), Rectangle()) rects.set_stroke(width=0) rects.set_fill(YELLOW, 0.5) x_rect.match_width(x_line) x_rect.stretch_to_fit_height(0.25) x_rect.move_to(x_line) y_rect.match_height(y_line) y_rect.stretch_to_fit_width(0.25) y_rect.move_to(y_line) self.play( ApplyMethod( input_tracker.set_value, input_tracker.get_value() + dx, rate_func=lambda t: wiggle(t, 6) ), FadeIn( rects, rate_func=squish_rate_func(smooth, 0, 0.33), remover=True, ), run_time=run_time, ) self.play(FadeOut(rects)) class EoCWrapper(Scene): def construct(self): title = Title("Essence of calculus") self.play(Write(title)) self.wait() class IntroduceTransformationView(NumberlineTransformationScene): CONFIG = { "func": lambda x: 0.5 * np.sin(2 * x) + x, "number_line_config": { "x_min": 0, "x_max": 6, "unit_size": 2.0 }, } def construct(self): self.add_title() self.show_animation_preview() self.indicate_indicate_point_densities() self.show_zoomed_transformation() def add_title(self): title = self.title = TextMobject("$f(x)$ as a transformation") title.to_edge(UP) self.add(title) def show_animation_preview(self): input_points = self.get_sample_input_points() output_points = map( self.number_func_to_point_func(self.func), input_points ) sample_dots = self.get_sample_dots() sample_dot_ghosts = sample_dots.copy().fade(0.5) arrows = VGroup(*[ Arrow(ip, op, buff=MED_SMALL_BUFF) for ip, op in zip(input_points, output_points) ]) arrows = arrows[1::3] arrows.set_stroke(BLACK, 1) self.play( LaggedStart(GrowFromCenter, sample_dots, run_time=1), LaggedStart(GrowArrow, arrows) ) self.add(sample_dot_ghosts) self.apply_function( self.func, sample_dots=sample_dots ) self.wait() self.play(LaggedStart(FadeOut, arrows, run_time=1)) self.sample_dots = sample_dots self.sample_dot_ghosts = sample_dot_ghosts def indicate_indicate_point_densities(self): lower_brace = Brace(Line(LEFT, RIGHT), UP) upper_brace = lower_brace.copy() input_tracker = ValueTracker(0.5) dx = 0.5 def update_upper_brace(brace): x = input_tracker.get_value() line = Line( self.get_input_point(x), self.get_input_point(x + dx), ) brace.match_width(line, stretch=True) brace.next_to(line, UP, buff=SMALL_BUFF) return brace def update_lower_brace(brace): x = input_tracker.get_value() line = Line( self.get_output_point(self.func(x)), self.get_output_point(self.func(x + dx)), ) brace.match_width(line, stretch=True) brace.next_to(line, UP, buff=SMALL_BUFF) return brace lower_brace_anim = UpdateFromFunc(lower_brace, update_lower_brace) upper_brace_anim = UpdateFromFunc(upper_brace, update_upper_brace) new_title = TextMobject( "$\\frac{df}{dx}(x)$ measures stretch/squishing" ) new_title.move_to(self.title, UP) stretch_factor = DecimalNumber(0, color=YELLOW) stretch_factor_anim = ChangingDecimal( stretch_factor, lambda a: lower_brace.get_width() / upper_brace.get_width(), position_update_func=lambda m: m.next_to(lower_brace, UP, SMALL_BUFF) ) self.play( GrowFromCenter(upper_brace), FadeOut(self.title), # FadeIn(new_title) Write(new_title, run_time=2) ) self.title = new_title self.play( ReplacementTransform(upper_brace.copy(), lower_brace), GrowFromPoint(stretch_factor, upper_brace.get_center()) ) self.play( input_tracker.set_value, self.input_line.x_max - dx, lower_brace_anim, upper_brace_anim, stretch_factor_anim, run_time=8, rate_func=bezier([0, 0, 1, 1]) ) self.wait() new_sample_dots = self.get_sample_dots() self.play( FadeOut(VGroup( upper_brace, lower_brace, stretch_factor, self.sample_dots, self.moving_input_line, )), FadeIn(new_sample_dots), ) self.sample_dots = new_sample_dots def show_zoomed_transformation(self): x = 2.75 local_sample_dots = self.get_local_sample_dots(x) self.zoom_in_on_input( x, local_sample_dots=local_sample_dots, local_coordinate_values=self.get_local_coordinate_values(x), ) self.wait() self.apply_function( self.func, sample_dots=self.sample_dots, local_sample_dots=local_sample_dots, target_coordinate_values=self.get_local_coordinate_values(self.func(x)) ) self.wait() class TalkThroughXSquaredExample(IntroduceTransformationView): CONFIG = { "func": lambda x: x**2, "number_line_config": { "x_min": 0, "x_max": 5, "unit_size": 1.25, }, "output_line_config": { "x_max": 25, }, "default_delta_x": 0.2 } def construct(self): self.add_title() self.show_specific_points_mapping() def add_title(self): title = self.title = TextMobject("$f(x) = x^2$") title.to_edge(UP, buff=MED_SMALL_BUFF) self.add(title) def show_specific_points_mapping(self): # First, just show integers as examples int_dots = self.get_sample_dots(1, 6, 1) int_dot_ghosts = int_dots.copy().fade(0.5) int_arrows = VGroup(*[ Arrow( # num.get_bottom(), self.get_input_point(x), self.get_output_point(self.func(x)), buff=MED_SMALL_BUFF ) for x, num in zip(range(1, 6), self.input_line.numbers[1:]) ]) point_func = self.number_func_to_point_func(self.func) numbers = self.input_line.numbers numbers.next_to(self.input_line, UP, SMALL_BUFF) self.titles[0].next_to(numbers, UP, MED_SMALL_BUFF, LEFT) # map(TexMobject.add_background_rectangle, numbers) # self.add_foreground_mobject(numbers) for dot, dot_ghost, arrow in zip(int_dots, int_dot_ghosts, int_arrows): arrow.match_color(dot) self.play(DrawBorderThenFill(dot, run_time=1)) self.add(dot_ghost) self.play( GrowArrow(arrow), dot.apply_function_to_position, point_func ) self.wait() # Show more sample_dots sample_dots = self.get_sample_dots() sample_dot_ghosts = sample_dots.copy().fade(0.5) self.play( LaggedStart(DrawBorderThenFill, sample_dots), LaggedStart(FadeOut, int_arrows), ) self.remove(int_dot_ghosts) self.add(sample_dot_ghosts) self.apply_function(self.func, sample_dots=sample_dots) self.remove(int_dots) self.wait() self.sample_dots = sample_dots self.sample_dot_ghosts = sample_dot_ghosts def get_stretch_words(self, factor, color=RED, less_than_one=False): result = TextMobject( "Scale \\\\ by", str(factor), tex_to_color_map={str(factor): color} ) result.scale(0.7) la, ra = TexMobject("\\leftarrow \\rightarrow") if less_than_one: la, ra = ra, la la.next_to(result, LEFT) ra.next_to(result, RIGHT) result.add(la, ra) result.next_to( self.zoomed_display.get_top(), DOWN, SMALL_BUFF ) return result def get_deriv_equation(self, x, rhs, color=RED): deriv_equation = self.deriv_equation = TexMobject( "\\frac{df}{dx}(", str(x), ")", "=", str(rhs), tex_to_color_map={str(x): color, str(rhs): color} ) deriv_equation.next_to(self.title, DOWN, MED_LARGE_BUFF) return deriv_equation class ZoomInOnXSquaredNearOne(TalkThroughXSquaredExample): def setup(self): TalkThroughXSquaredExample.setup(self) self.force_skipping() self.add_title() self.show_specific_points_mapping() self.revert_to_original_skipping_status() def construct(self): zoom_words = TextMobject("Zoomed view \\\\ near 1") zoom_words.next_to(self.zoomed_display, DOWN) # zoom_words.shift_onto_screen() x = 1 local_sample_dots = self.get_local_sample_dots(x) local_coords = self.get_local_coordinate_values(x, dx=0.1) zcbr_anim = self.zoomed_camera_background_rectangle_anim zcbr_group = self.zoomed_camera_background_rectangle_group frame = self.zoomed_camera.frame self.zoom_in_on_input(x, local_sample_dots, local_coords) self.play(FadeIn(zoom_words)) self.wait() local_sample_dots.save_state() frame.save_state() self.mini_line.save_state() sample_dot_ghost_copies = self.sample_dot_ghosts.copy() self.apply_function( self.func, apply_function_to_number_line=False, local_sample_dots=local_sample_dots, target_coordinate_values=local_coords ) self.remove(sample_dot_ghost_copies) self.wait() # Go back self.play( frame.restore, self.mini_line.restore, local_sample_dots.restore, zcbr_anim, Animation(zcbr_group) ) self.wait() # Zoom in even more extra_zoom_factor = 0.3 one_group = VGroup( self.local_coordinates.tick_marks[1], self.local_coordinates.numbers[1], ) all_other_coordinates = VGroup( self.local_coordinates.tick_marks[::2], self.local_coordinates.numbers[::2], self.local_target_coordinates, ) self.play(frame.scale, extra_zoom_factor) new_local_sample_dots = self.get_local_sample_dots(x, delta_x=0.005) new_coordinate_values = self.get_local_coordinate_values(x, dx=0.02) new_local_coordinates = self.get_local_coordinates( self.input_line, *new_coordinate_values ) self.play( Write(new_local_coordinates), Write(new_local_sample_dots), one_group.scale, extra_zoom_factor, {"about_point": self.get_input_point(1)}, FadeOut(all_other_coordinates), *[ ApplyMethod(dot.scale, extra_zoom_factor) for dot in local_sample_dots ] ) self.remove(one_group, local_sample_dots) zcbr_group.remove( self.local_coordinates, self.local_target_coordinates, local_sample_dots ) # Transform new zoomed view stretch_by_two_words = self.get_stretch_words(2) self.add_foreground_mobject(stretch_by_two_words) sample_dot_ghost_copies = self.sample_dot_ghosts.copy() self.apply_function( self.func, apply_function_to_number_line=False, sample_dots=sample_dot_ghost_copies, local_sample_dots=new_local_sample_dots, target_coordinate_values=new_coordinate_values, added_anims=[FadeIn(stretch_by_two_words)] ) self.remove(sample_dot_ghost_copies) self.wait() # Write derivative deriv_equation = self.get_deriv_equation(1, 2, color=RED) self.play(Write(deriv_equation)) self.wait() class ZoomInOnXSquaredNearThree(ZoomInOnXSquaredNearOne): CONFIG = { "zoomed_display_width": 4, } def construct(self): zoom_words = TextMobject("Zoomed view \\\\ near 3") zoom_words.next_to(self.zoomed_display, DOWN) x = 3 local_sample_dots = self.get_local_sample_dots(x) local_coordinate_values = self.get_local_coordinate_values(x, dx=0.1) target_coordinate_values = self.get_local_coordinate_values(self.func(x), dx=0.1) color = self.sample_dots[len(self.sample_dots) / 2].get_color() sample_dot_ghost_copies = self.sample_dot_ghosts.copy() stretch_words = self.get_stretch_words(2 * x, color) deriv_equation = self.get_deriv_equation(x, 2 * x, color) self.add(deriv_equation) self.zoom_in_on_input( x, pop_out=False, local_sample_dots=local_sample_dots, local_coordinate_values=local_coordinate_values ) self.play(Write(zoom_words, run_time=1)) self.wait() self.add_foreground_mobject(stretch_words) self.apply_function( self.func, apply_function_to_number_line=False, sample_dots=sample_dot_ghost_copies, local_sample_dots=local_sample_dots, target_coordinate_values=target_coordinate_values, added_anims=[Write(stretch_words)] ) self.wait(2) class ZoomInOnXSquaredNearOneFourth(ZoomInOnXSquaredNearOne): CONFIG = { "zoom_factor": 0.01, "local_coordinate_num_decimal_places": 4, "zoomed_display_width": 4, "default_delta_x": 0.25, } def construct(self): # Much copy-pasting from previous scenes. Not great, but # the fastest way to get the ease-of-tweaking I'd like. zoom_words = TextMobject("Zoomed view \\\\ near $1/4$") zoom_words.next_to(self.zoomed_display, DOWN) x = 0.25 local_sample_dots = self.get_local_sample_dots( x, sample_radius=2.5 * self.zoomed_camera.frame.get_width(), ) local_coordinate_values = self.get_local_coordinate_values( x, dx=0.01, ) target_coordinate_values = self.get_local_coordinate_values( self.func(x), dx=0.01, ) color = RED sample_dot_ghost_copies = self.sample_dot_ghosts.copy() stretch_words = self.get_stretch_words("1/2", color, less_than_one=True) deriv_equation = self.get_deriv_equation("1/4", "1/2", color) one_fourth_point = self.get_input_point(x) one_fourth_arrow = Vector(0.5 * UP, color=WHITE) one_fourth_arrow.stem.stretch(0.75, 0) one_fourth_arrow.tip.scale(0.75, about_edge=DOWN) one_fourth_arrow.next_to(one_fourth_point, DOWN, SMALL_BUFF) one_fourth_label = TexMobject("0.25") one_fourth_label.match_height(self.input_line.numbers) one_fourth_label.next_to(one_fourth_arrow, DOWN, SMALL_BUFF) self.add(deriv_equation) self.zoom_in_on_input( x, local_sample_dots=local_sample_dots, local_coordinate_values=local_coordinate_values, pop_out=False, first_added_anims=[ FadeIn(one_fourth_label), GrowArrow(one_fourth_arrow), ] ) self.play(Write(zoom_words, run_time=1)) self.wait() self.add_foreground_mobject(stretch_words) self.apply_function( self.func, apply_function_to_number_line=False, sample_dots=sample_dot_ghost_copies, local_sample_dots=local_sample_dots, target_coordinate_values=target_coordinate_values, added_anims=[Write(stretch_words)] ) self.wait(2) class ZoomInOnXSquaredNearZero(ZoomInOnXSquaredNearOne): CONFIG = { "zoom_factor": 0.1, "zoomed_display_width": 4, "scale_by_term": "???", } def construct(self): zoom_words = TextMobject( "Zoomed %sx \\\\ near 0" % "{:,}".format(int(1.0 / self.zoom_factor)) ) zoom_words.next_to(self.zoomed_display, DOWN) x = 0 local_sample_dots = self.get_local_sample_dots( x, sample_radius=2 * self.zoomed_camera.frame.get_width() ) local_coordinate_values = self.get_local_coordinate_values( x, dx=self.zoom_factor ) # target_coordinate_values = self.get_local_coordinate_values( # self.func(x), dx=self.zoom_factor # ) color = self.sample_dots[len(self.sample_dots) / 2].get_color() sample_dot_ghost_copies = self.sample_dot_ghosts.copy() stretch_words = self.get_stretch_words( self.scale_by_term, color, less_than_one=True ) deriv_equation = self.get_deriv_equation(x, 2 * x, color) self.add(deriv_equation) self.zoom_in_on_input( x, pop_out=False, local_sample_dots=local_sample_dots, local_coordinate_values=local_coordinate_values ) self.play(Write(zoom_words, run_time=1)) self.wait() self.add_foreground_mobject(stretch_words) self.apply_function( self.func, apply_function_to_number_line=False, sample_dots=sample_dot_ghost_copies, local_sample_dots=local_sample_dots, # target_coordinate_values=target_coordinate_values, added_anims=[ Write(stretch_words), MaintainPositionRelativeTo( self.local_coordinates, self.zoomed_camera.frame ) ] ) self.wait(2) class ZoomInOnXSquared100xZero(ZoomInOnXSquaredNearZero): CONFIG = { "zoom_factor": 0.01 } class ZoomInOnXSquared1000xZero(ZoomInOnXSquaredNearZero): CONFIG = { "zoom_factor": 0.001, "local_coordinate_num_decimal_places": 3, } class ZoomInOnXSquared10000xZero(ZoomInOnXSquaredNearZero): CONFIG = { "zoom_factor": 0.0001, "local_coordinate_num_decimal_places": 4, "scale_by_term": "0", } class XSquaredForNegativeInput(TalkThroughXSquaredExample): CONFIG = { "input_line_config": { "x_min": -3, "x_max": 3, }, "output_line_config": {}, } def construct(self): pass