diff --git a/eoc/chapter1.py b/eoc/chapter1.py index bc59fd2c..7450aae0 100644 --- a/eoc/chapter1.py +++ b/eoc/chapter1.py @@ -24,6 +24,8 @@ from camera import Camera from mobject.svg_mobject import * from mobject.tex_mobject import * +from eoc.graph_scene import GraphScene + class CircleScene(PiCreatureScene): CONFIG = { "radius" : 1.5, @@ -1839,9 +1841,177 @@ class IntroduceConcentricRings(CircleScene): ) self.dither(4) - - - +class AskAboutGeneralCircles(TeacherStudentsScene): + def construct(self): + self.student_says(""" + What about integrals + beyond this circle + example? + """) + self.change_student_modes("confused") + self.random_blink(2) + self.teacher_says( + "All in due time", + ) + self.change_student_modes(*["happy"]*3) + self.random_blink(2) + +class GraphIntegral(GraphScene): + CONFIG = { + "x_min" : -0.25, + "x_max" : 4, + "x_tick_frequency" : 0.25, + "x_leftmost_tick" : -0.25, + "x_labeled_nums" : range(1, 5), + "x_axis_label" : "r", + "y_min" : -2, + "y_max" : 25, + "y_tick_frequency" : 2.5, + "y_bottom_tick" : 0, + "y_labeled_nums" : range(5, 30, 5), + "y_axis_label" : "", + "dr" : 0.125, + "R" : 3.5, + } + def construct(self): + integral = TexMobject("\\int_0^R 2\\pi r \\, dr") + integral.to_edge(UP).shift(LEFT) + + self.play(Write(integral)) + self.dither() + self.setup_axes() + self.add_rectangles() + self.thinner_rectangles() + self.ask_about_area() + + + def add_rectangles(self): + tick_height = 0.2 + special_tick_index = 12 + ticks = VGroup(*[ + Line(UP, DOWN).move_to(self.coords_to_point(x, 0)) + for x in np.arange(0, self.R+self.dr, self.dr) + ]) + ticks.stretch_to_fit_height(tick_height) + ticks.highlight(YELLOW) + R_label = TexMobject("R") + R_label.next_to(self.coords_to_point(self.R, 0), DOWN) + + values_words = TextMobject("Values of $r$") + values_words.shift(UP) + arrows = VGroup(*[ + Arrow( + values_words.get_bottom(), + tick.get_center(), + tip_length = 0.15 + ) + for tick in ticks + ]) + + dr_brace = Brace( + VGroup(*ticks[special_tick_index:special_tick_index+2]), + buff = SMALL_BUFF + ) + dr_text = dr_brace.get_text("$dr$", buff = SMALL_BUFF) + # dr_text.highlight(YELLOW) + + rectangles = self.get_rectangles(self.dr) + special_rect = rectangles[special_tick_index] + left_brace = Brace(special_rect, LEFT) + height_label = left_brace.get_text("$2\\pi r$") + + self.play( + ShowCreation(ticks, submobject_mode = "lagged_start"), + Write(R_label) + ) + self.play( + Write(values_words), + ShowCreation(arrows) + ) + self.dither() + self.play( + GrowFromCenter(dr_brace), + Write(dr_text) + ) + self.dither() + rectangles.save_state() + rectangles.stretch_to_fit_height(0) + rectangles.move_to(self.graph_origin, DOWN+LEFT) + self.play(*map(FadeOut, [arrows, values_words])) + self.play( + rectangles.restore, + Animation(ticks), + run_time = 2 + ) + self.dither() + self.play(*[ + ApplyMethod(rect.fade, 0.7) + for rect in rectangles + if rect is not special_rect + ] + [Animation(ticks)]) + self.play( + GrowFromCenter(left_brace), + Write(height_label) + ) + self.dither() + + graph = self.graph_function( + lambda r : 2*np.pi*r, + animate = False + ) + graph_label = self.label_graph( + self.graph, "f(r) = 2\\pi r", + proportion = 0.5, + direction = LEFT, + animate = False + ) + self.play( + rectangles.restore, + Animation(ticks), + FadeOut(left_brace), + Transform(height_label, graph_label), + ShowCreation(graph) + ) + self.dither(3) + self.play(*map(FadeOut, [ticks, dr_brace, dr_text])) + self.rectangles = rectangles + + def thinner_rectangles(self): + for x in range(2, 8): + new_rects = self.get_rectangles( + dr = self.dr/x, stroke_width = 1./x + ) + self.play(Transform(self.rectangles, new_rects)) + self.dither() + + def ask_about_area(self): + question = TextMobject("What's this \\\\ area") + question.to_edge(RIGHT).shift(2*UP) + arrow = Arrow( + question.get_bottom(), + self.rectangles, + buff = SMALL_BUFF + ) + self.play( + Write(question), + ShowCreation(arrow) + ) + self.dither() + + def get_rectangles(self, dr, stroke_width = 1): + rectangles = VGroup() + for r in np.arange(0, self.R, dr): + points = VGroup( + VectorizedPoint(self.coords_to_point(r, 0)), + VectorizedPoint(self.coords_to_point(r+dr, 2*np.pi*r)), + ) + rect = Rectangle() + rect.replace(points, stretch = True) + rect.set_fill(opacity = 1) + rectangles.add(rect) + rectangles.gradient_highlight(BLUE, GREEN) + rectangles.set_stroke(BLACK, width = stroke_width) + return rectangles diff --git a/eoc/graph_scene.py b/eoc/graph_scene.py new file mode 100644 index 00000000..72fd0bd2 --- /dev/null +++ b/eoc/graph_scene.py @@ -0,0 +1,136 @@ +from helpers import * + +from scene import Scene +# from topics.geometry import +from mobject.tex_mobject import TexMobject +from mobject.vectorized_mobject import VGroup +from animation.simple_animations import Write, ShowCreation +from topics.number_line import NumberLine +from topics.functions import ParametricFunction + +class GraphScene(Scene): + CONFIG = { + "x_min" : -1, + "x_max" : 10, + "x_axis_width" : 9, + "x_tick_frequency" : 1, + "x_leftmost_tick" : -1, + "x_labeled_nums" : range(1, 10), + "x_axis_label" : "x", + "y_min" : -1, + "y_max" : 10, + "y_axis_height" : 6, + "y_tick_frequency" : 1, + "y_bottom_tick" : -1, + "y_labeled_nums" : range(1, 10), + "y_axis_label" : "y", + "axes_color" : GREY, + "graph_origin" : 2.5*DOWN + 4*LEFT, + "y_axis_numbers_nudge" : 0.4*UP+0.5*LEFT, + } + def setup_axes(self, animate = True): + x_num_range = float(self.x_max - self.x_min) + x_axis = NumberLine( + x_min = self.x_min, + x_max = self.x_max, + space_unit_to_num = self.x_axis_width/x_num_range, + tick_frequency = self.x_tick_frequency, + leftmost_tick = self.x_leftmost_tick, + numbers_with_elongated_ticks = self.x_labeled_nums, + color = self.axes_color + ) + x_axis.shift(self.graph_origin - x_axis.number_to_point(0)) + x_axis.add_numbers(*self.x_labeled_nums) + x_label = TexMobject(self.x_axis_label) + x_label.next_to(x_axis, RIGHT+UP, buff = SMALL_BUFF) + + y_num_range = float(self.y_max - self.y_min) + y_axis = NumberLine( + x_min = self.y_min, + x_max = self.y_max, + space_unit_to_num = self.y_axis_height/y_num_range, + tick_frequency = self.y_tick_frequency, + leftmost_tick = self.y_bottom_tick, + numbers_with_elongated_ticks = self.y_labeled_nums, + color = self.axes_color + ) + 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)) + y_axis.add_numbers(*self.y_labeled_nums) + y_axis.numbers.shift(self.y_axis_numbers_nudge) + y_label = TexMobject(self.y_axis_label) + y_label.next_to(y_axis.get_top(), RIGHT, buff = 2*MED_BUFF) + + if animate: + self.play(Write(VGroup(x_axis, y_axis))) + else: + selfe.add(x_axis, y_axis_label) + self.x_axis, self.y_axis = x_axis, y_axis + + def coords_to_point(self, x, y): + assert(hasattr(self, "x_axis") and hasattr(self, "y_axis")) + result = self.x_axis.number_to_point(x)[0]*RIGHT + result += self.y_axis.number_to_point(y)[1]*UP + return result + + def graph_function(self, func, + color = BLUE, + animate = True, + is_main_graph = True, + ): + + def parameterized_graph(alpha): + x = interpolate(self.x_min, self.x_max, alpha) + return self.coords_to_point(x, func(x)) + + graph = ParametricFunction(parameterized_graph, color = color) + + if is_main_graph: + self.graph = graph + if animate: + self.play(ShowCreation(graph)) + self.add(graph) + return graph + + def input_to_graph_point(self, x): + assert(hasattr(self, "graph")) + alpha = (x - self.x_min)/(self.x_max - self.x_min) + return self.graph.point_from_proportion(alpha) + + def angle_of_tangent(self, x, dx = 0.01): + assert(hasattr(self, "graph")) + vect = self.graph_point(x + dx) - self.graph_point(x) + return angle_of_vector(vect) + + def label_graph(self, graph, label = "f(x)", + proportion = 0.7, + direction = LEFT, + buff = 2*MED_BUFF, + animate = True + ): + label = TexMobject(label) + label.highlight(graph.get_color()) + label.next_to( + graph.point_from_proportion(proportion), + direction, + buff = buff + ) + if animate: + self.play(Write(label)) + self.add(label) + return label + + + + + + + + + + + + + + +