From 5ad1de5434c01d9f55d25d04efd1a485641fc21b Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Fri, 27 Jan 2017 19:31:20 -0800 Subject: [PATCH] Pattern for power rule in eoc3 --- animation/transform.py | 1 + eoc/chapter3.py | 234 ++++++++++++++++++++++++++++++++++++++++- eoc/graph_scene.py | 54 +++++++--- mobject/mobject.py | 3 +- topics/geometry.py | 2 +- 5 files changed, 273 insertions(+), 21 deletions(-) diff --git a/animation/transform.py b/animation/transform.py index 9ea71d55..e1fa2c26 100644 --- a/animation/transform.py +++ b/animation/transform.py @@ -124,6 +124,7 @@ class ApplyMethod(Transform): target = copy.deepcopy(method)(*args) Transform.__init__(self, method.im_self, target, **kwargs) + class FadeOut(Transform): CONFIG = { "remover" : True, diff --git a/eoc/chapter3.py b/eoc/chapter3.py index 052d2811..2a6b2042 100644 --- a/eoc/chapter3.py +++ b/eoc/chapter3.py @@ -1008,10 +1008,236 @@ class ShowCubeDVIn3D(Scene): def construct(self): raise Exception("This scene is only here for the stage_scenes script.") - - - - +class GraphOfXCubed(GraphScene): + CONFIG = { + "x_min" : -6, + "x_max" : 6, + "x_axis_width" : 2*SPACE_WIDTH, + "x_labeled_nums" : range(-6, 7), + "y_min" : -35, + "y_max" : 35, + "y_axis_height" : 2*SPACE_HEIGHT, + "y_tick_frequency" : 5, + "y_labeled_nums" : range(-30, 40, 10), + "graph_origin" : ORIGIN, + "dx" : 0.2, + "deriv_x_min" : -3, + "deriv_x_max" : 3, + } + def construct(self): + self.setup_axes(animate = False) + graph = self.get_graph(lambda x : x**3) + label = self.get_graph_label( + graph, "f(x) = x^3", + direction = LEFT, + ) + + + deriv_graph, full_deriv_graph = [ + self.get_derivative_graph( + graph, + color = DERIVATIVE_COLOR, + x_min = low_x, + x_max = high_x, + ) + for low_x, high_x in [ + (self.deriv_x_min, self.deriv_x_max), + (self.x_min, self.x_max), + ] + ] + deriv_label = self.get_graph_label( + deriv_graph, + "\\frac{df}{dx}(x) = 3x^2", + x_val = -3, + direction = LEFT + ) + deriv_label.shift(0.5*DOWN) + + ss_group = self.get_secant_slope_group( + self.deriv_x_min, graph, + dx = self.dx, + dx_line_color = WHITE, + df_line_color = WHITE, + secant_line_color = YELLOW, + ) + + self.play(ShowCreation(graph)) + self.play(Write(label, run_time = 1)) + self.dither() + self.play(Write(deriv_label, run_time = 1)) + self.play(ShowCreation(ss_group, submobject_mode = "all_at_once")) + self.animate_secant_slope_group_change( + ss_group, + target_x = self.deriv_x_max, + run_time = 10, + added_anims = [ + ShowCreation(deriv_graph, run_time = 10) + ] + ) + self.play(FadeIn(full_deriv_graph)) + self.dither() + for x_val in -2, -self.dx/2, 2: + self.animate_secant_slope_group_change( + ss_group, + target_x = x_val, + run_time = 2 + ) + if x_val != -self.dx/2: + v_line = self.get_vertical_line_to_graph( + x_val, deriv_graph, + line_class = DashedLine + ) + self.play(ShowCreation(v_line)) + self.play(FadeOut(v_line)) + +class PatternForPowerRule(PiCreatureScene): + CONFIG = { + "num_exponents" : 5, + } + def construct(self): + self.introduce_pattern() + self.generalize_pattern() + self.show_hopping() + + def introduce_pattern(self): + exp_range = range(1, 1+self.num_exponents) + colors = color_gradient([BLUE_D, YELLOW], self.num_exponents) + derivatives = VGroup() + for exponent, color in zip(exp_range, colors): + derivative = TexMobject( + "\\frac{d(x^%d)}{dx} = "%exponent, + "%d x^{%d}"%(exponent, exponent-1) + ) + VGroup(*derivative[0][2:4]).highlight(color) + derivatives.add(derivative) + derivatives.arrange_submobjects( + DOWN, aligned_edge = LEFT, + buff = MED_LARGE_BUFF + ) + derivatives.scale_to_fit_height(2*SPACE_HEIGHT-1) + derivatives.to_edge(LEFT) + + self.play(FadeIn(derivatives[0])) + for d1, d2 in zip(derivatives, derivatives[1:]): + self.play(Transform( + d1.copy(), d2, + replace_mobject_with_target_in_scene = True + )) + self.change_mode("thinking") + self.dither() + for derivative in derivatives[-2:]: + derivative.save_state() + self.play( + derivative.scale, 2, + derivative.next_to, derivative, + RIGHT, SMALL_BUFF, DOWN, + ) + self.dither(2) + self.play(derivative.restore) + self.remove(derivative) + derivative.restore() + self.add(derivative) + + self.derivatives = derivatives + self.colors = colors + + def generalize_pattern(self): + derivatives = self.derivatives + + + power_rule = TexMobject( + "\\frac{d (x^n)}{dx} = ", + "nx^{n-1}" + ) + title = TextMobject("``Power rule''") + title.next_to(power_rule, UP, MED_LARGE_BUFF) + lines = VGroup(*[ + Line( + deriv.get_right(), power_rule.get_left(), + buff = MED_SMALL_BUFF, + color = deriv[0][2].get_color() + ) + for deriv in derivatives + ]) + + self.play( + Transform( + VGroup(*[d[0].copy() for d in derivatives]), + VGroup(power_rule[0]), + replace_mobject_with_target_in_scene = True + ), + ShowCreation(lines), + submobject_mode = "lagged_start", + run_time = 2, + ) + self.dither() + self.play(Write(power_rule[1])) + self.dither() + self.play( + Write(title), + self.pi_creature.change_mode, "speaking" + ) + self.dither() + + def show_hopping(self): + exp_range = range(2, 2+self.num_exponents-1) + self.change_mode("tired") + for exp, color in zip(exp_range, self.colors[1:]): + form = TexMobject( + "x^", + str(exp), + "\\rightarrow", + str(exp), + "x^", + str(exp-1) + ) + form.highlight(color) + form.to_corner(UP+RIGHT, buff = LARGE_BUFF) + lhs = VGroup(*form[:2]) + lhs_copy = lhs.copy() + rhs = VGroup(*form[-2:]) + arrow = form[2] + + self.play(Write(lhs)) + self.play( + lhs_copy.move_to, rhs, DOWN+LEFT, + Write(arrow) + ) + self.dither() + self.play(Transform( + lhs_copy[1], form[3], + path_arc = np.pi, + rate_func = running_start, + replace_mobject_with_target_in_scene = True + )) + self.play(FadeIn(form[5])) + self.dither() + self.play( + self.pi_creature.change_mode, "hesitant", + self.pi_creature.look_at, lhs_copy + ) + self.play(*map(FadeOut, [form, lhs_copy])) + +# class PowerRuleAlgebra(Scene): +# CONFIG = { +# "dx_color" : YELLOW, +# } +# def construct(self): +# value = TextMobject("Value:") +# x_to_n = TexMobject("x^n") +# nudged = TextMobject("Nudged Value:") +# x_dx_to_n = TexMobject("(x+dx)^n") +# equals = TexMobject("equals") +# full_product = TexMobject("(x+dx)(x+dx)(x+dx)\\cdots(x+dx)") + +# value.to_corner(UP+LEFT) +# x_to_n.next_to(value, RIGHT) +# nudged.next_to(value, DOWN, aligned_edge = LEFT) +# x_dx_group = VGroup(x_dx_to_n, equals, full_product) +# x_dx_group.arrange_submobjects(RIGHT) + + +# self.add(value, nudged_value) diff --git a/eoc/graph_scene.py b/eoc/graph_scene.py index cbb26708..96d7af6b 100644 --- a/eoc/graph_scene.py +++ b/eoc/graph_scene.py @@ -28,7 +28,7 @@ class GraphScene(Scene): "y_axis_label" : "$y$", "axes_color" : GREY, "graph_origin" : 2.5*DOWN + 4*LEFT, - "y_axis_numbers_nudge" : 0.4*UP+0.5*LEFT, + "y_axis_numbers_nudge" : 0.4*UP, "num_graph_anchor_points" : 25, "default_graph_colors" : [BLUE, GREEN, YELLOW], "default_derivative_color" : GREEN, @@ -48,9 +48,16 @@ class GraphScene(Scene): ) x_axis.shift(self.graph_origin - x_axis.number_to_point(0)) if self.x_labeled_nums: - x_axis.add_numbers(*self.x_labeled_nums) + x_axis.add_numbers(*filter( + lambda x : x != 0, + self.x_labeled_nums + )) x_label = TextMobject(self.x_axis_label) - x_label.next_to(x_axis, RIGHT+UP, buff = SMALL_BUFF) + x_label.next_to( + x_axis.get_tick_marks(), UP, + aligned_edge = RIGHT, + buff = SMALL_BUFF + ) x_label.shift_onto_screen() x_axis.add(x_label) self.x_axis_label_mob = x_label @@ -69,10 +76,19 @@ class GraphScene(Scene): y_axis.shift(self.graph_origin-y_axis.number_to_point(0)) y_axis.rotate(np.pi/2, about_point = y_axis.number_to_point(0)) if self.y_labeled_nums: - y_axis.add_numbers(*self.y_labeled_nums) - y_axis.numbers.shift(self.y_axis_numbers_nudge) + y_axis.add_numbers(*filter( + lambda y : y != 0, + self.y_labeled_nums + )) + for mob in y_axis.numbers: + mob.next_to(mob.get_center(), LEFT, MED_SMALL_BUFF) + mob.shift(self.y_axis_numbers_nudge) y_label = TextMobject(self.y_axis_label) - y_label.next_to(y_axis.get_top(), RIGHT, buff = MED_LARGE_BUFF) + y_label.next_to( + y_axis.get_tick_marks(), RIGHT, + aligned_edge = UP, + buff = SMALL_BUFF + ) y_label.shift_onto_screen() y_axis.add(y_label) self.y_axis_label_mob = y_label @@ -90,12 +106,21 @@ class GraphScene(Scene): result += self.y_axis.number_to_point(y)[1]*UP return result - def get_graph(self, func, color = None): + def get_graph( + self, func, + color = None, + x_min = None, + x_max = None, + ): if color is None: color = self.default_graph_colors.next() + if x_min is None: + x_min = self.x_min + if x_max is None: + x_max = self.x_max def parameterized_function(alpha): - x = interpolate(self.x_min, self.x_max, alpha) + x = interpolate(x_min, x_max, alpha) return self.coords_to_point(x, func(x)) graph = ParametricFunction( @@ -116,13 +141,12 @@ class GraphScene(Scene): def slope_of_tangent(self, *args, **kwargs): return np.tan(self.angle_of_tangent(*args, **kwargs)) - def get_derivative_graph(self, graph, dx = 0.01, color = None): - if color is None: - color = self.default_derivative_color - return self.get_graph( - lambda x : self.slope_of_tangent(x, graph, dx) / self.space_unit_to_y, - color = color, - ) + def get_derivative_graph(self, graph, dx = 0.01, **kwargs): + if "color" not in kwargs: + kwargs["color"] = self.default_derivative_color + def deriv(x): + return self.slope_of_tangent(x, graph, dx) / self.space_unit_to_y + return self.get_graph(deriv, **kwargs) def get_graph_label( self, diff --git a/mobject/mobject.py b/mobject/mobject.py index b614f65c..6d3d4d03 100644 --- a/mobject/mobject.py +++ b/mobject/mobject.py @@ -268,7 +268,8 @@ class Mobject(object): dim = np.argmax(np.abs(vect)) buff = kwargs.get("buff", DEFAULT_MOBJECT_TO_EDGE_BUFFER) max_val = space_lengths[dim] - buff - if abs(self.get_edge_center(vect)[dim]) > max_val: + edge_center = self.get_edge_center(vect) + if np.dot(edge_center, vect) > max_val: self.to_edge(vect, **kwargs) return self diff --git a/topics/geometry.py b/topics/geometry.py index 2f25b565..c3e26b05 100644 --- a/topics/geometry.py +++ b/topics/geometry.py @@ -135,7 +135,7 @@ class Line(VMobject): class DashedLine(Line): CONFIG = { - "dashed_segment_length" : 0.15 + "dashed_segment_length" : 0.05 } def __init__(self, *args, **kwargs): self.init_kwargs = kwargs