From c33ea5232a7481d4f0f955e588940c72d80b464b Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Wed, 10 May 2017 17:22:26 -0700 Subject: [PATCH] Finished CountThroughRings of leibniz --- animation/simple_animations.py | 7 - helpers.py | 12 +- leibniz.py | 264 ++++++++++++++++++++++++++++----- scene/scene.py | 15 ++ 4 files changed, 253 insertions(+), 45 deletions(-) diff --git a/animation/simple_animations.py b/animation/simple_animations.py index 1edb376b..39fc2116 100644 --- a/animation/simple_animations.py +++ b/animation/simple_animations.py @@ -37,7 +37,6 @@ class Rotating(Animation): about_point = self.about_point ) - class ShowPartial(Animation): def update_submobject(self, submobject, starting_submobject, alpha): submobject.pointwise_become_partial( @@ -47,7 +46,6 @@ class ShowPartial(Animation): def get_bounds(self, alpha): raise Exception("Not Implemented") - class ShowCreation(ShowPartial): CONFIG = { "submobject_mode" : "one_at_a_time", @@ -131,7 +129,6 @@ class DrawBorderThenFill(Animation): submobject.set_stroke(width = width) submobject.set_fill(opacity = opacity) - class ShowPassingFlash(ShowPartial): CONFIG = { "time_width" : 0.1, @@ -144,7 +141,6 @@ class ShowPassingFlash(ShowPartial): upper = min(1, alpha + self.time_width/2) return (lower, upper) - class MoveAlongPath(Animation): def __init__(self, mobject, vmobject, **kwargs): digest_config(self, kwargs, locals()) @@ -183,7 +179,6 @@ class SmoothedVectorizedHomotopy(Homotopy): Homotopy.update_submobject(self, submob, start, alpha) submob.make_smooth() - class ApplyWave(Homotopy): CONFIG = { "direction" : DOWN, @@ -206,7 +201,6 @@ class ApplyWave(Homotopy): return np.array([x, y, z]) + nudge*vect Homotopy.__init__(self, homotopy, mobject, **kwargs) - class PhaseFlow(Animation): CONFIG = { "virtual_time" : 1, @@ -250,7 +244,6 @@ class UpdateFromAlphaFunc(UpdateFromFunc): def update_mobject(self, alpha): self.update_function(self.mobject, alpha) - class MaintainPositionRelativeTo(Animation): CONFIG = { "tracked_critical_point" : ORIGIN diff --git a/helpers.py b/helpers.py index 9ddaa3d5..e1b0d319 100644 --- a/helpers.py +++ b/helpers.py @@ -265,10 +265,14 @@ def merge_config(all_dicts): config[key] = merge_config([config[key], value]) return config -def digest_locals(obj): - caller_locals = inspect.currentframe().f_back.f_locals - obj.__dict__.update(filtered_locals(caller_locals)) - +def digest_locals(obj, keys = None): + caller_locals = filtered_locals( + inspect.currentframe().f_back.f_locals + ) + if keys is None: + keys = caller_locals.keys() + for key in keys: + setattr(obj, key, caller_locals[key]) def interpolate(start, end, alpha): return (1-alpha)*start + alpha*end diff --git a/leibniz.py b/leibniz.py index 8e6183f1..7cfe70d4 100644 --- a/leibniz.py +++ b/leibniz.py @@ -157,7 +157,7 @@ class ShowSum(TeacherStudentsScene): def show_sum(self): line = UnitInterval() line.add_numbers(0, 1) - line.shift(UP) + # line.shift(UP) sum_point = line.number_to_point(np.pi/4) numbers = [0] + [ @@ -177,19 +177,19 @@ class ShowSum(TeacherStudentsScene): dot = Dot(points[0]) sum_mob = TexMobject( - "1", "-\\frac{1}{3}", "+\\frac{1}{5}", - "-\\frac{1}{7}", "+\\cdots" + "1", "-\\frac{1}{3}", + "+\\frac{1}{5}", "-\\frac{1}{7}", + "+\\frac{1}{9}", "-\\frac{1}{11}", + "+\\cdots" ) - sum_mob.to_edge(UP) - sum_mob.shift(LEFT) - rhs = TexMobject( - "=", "\\frac{\\pi}{4}", - "\\approx %.5f\\dots"%(np.pi/4) + sum_mob.to_corner(UP+RIGHT) + lhs = TexMobject( + "\\frac{\\pi}{4}", "=", ) - rhs.next_to(sum_mob, RIGHT) - rhs.highlight_by_tex("pi", YELLOW) + lhs.next_to(sum_mob, LEFT) + lhs.highlight_by_tex("pi", YELLOW) sum_arrow = Arrow( - rhs.get_part_by_tex("pi"), + lhs.get_part_by_tex("pi").get_bottom(), sum_point ) fading_terms = [ @@ -211,6 +211,7 @@ class ShowSum(TeacherStudentsScene): look_at_arg = line, added_anims = [ FadeIn(VGroup(line, dot)), + FadeIn(lhs), RemovePiCreatureBubble( self.teacher, target_mode = "raise_right_hand" @@ -243,10 +244,7 @@ class ShowSum(TeacherStudentsScene): FadeOut(fading_term), dot.move_to, sum_point ) - self.play( - Write(rhs), - ShowCreation(sum_arrow) - ) + self.play(ShowCreation(sum_arrow)) self.dither() self.change_student_modes("erm", "confused", "maybe") self.play(self.teacher.change_mode, "happy") @@ -262,16 +260,6 @@ class FermatsDreamExcerptWrapper(Scene): self.add(words) self.dither() -class ShowSumMeantForFadedBackground(Scene): - def construct(self): - tex_mob = TexMobject( - "1 - \\frac{1}{3} + \\frac{1}{5} - \\frac{1}{7} + \\cdots", - "=", "\\frac{\\pi}{4}" - ) - tex_mob.highlight_by_tex("pi", YELLOW) - self.add(tex_mob) - self.dither() - class ShowCalculus(PiCreatureScene): def construct(self): frac_sum = TexMobject( @@ -321,6 +309,10 @@ class ShowCalculus(PiCreatureScene): def create_pi_creature(self): return Randolph(color = BLUE_C).to_corner(DOWN+LEFT) +class CertainRegularityInPrimes(LatticePointScene): + def construct(self): + pass + class Outline(PiCreatureScene): def construct(self): self.generate_list() @@ -600,10 +592,14 @@ class LatticePointScene(Scene): return circle def get_lattice_points_on_r_squared_circle(self, r_squared): - return VGroup(*filter( + points = VGroup(*filter( lambda dot : dot.r_squared == r_squared, self.lattice_points )) + points.sort_submobjects( + lambda p : angle_of_vector(p-self.plane_center)%(2*np.pi) + ) + return points def draw_lattice_points(self, points = None, run_time = 4): if points is None: @@ -823,17 +819,17 @@ class SoYouPlay(TeacherStudentsScene): class CountThroughRings(LatticePointScene): CONFIG = { - "example_point_coords" : (3, 2), - "num_rings_to_show_explicitly" : 6, + "example_coords" : (3, 2), + "num_rings_to_show_explicitly" : 7, "x_radius" : 15, + "plane_center" : 2*RIGHT, "max_lattice_point_radius" : 5, } def construct(self): - self.force_skipping() - self.add_lattice_points() self.preview_rings() + self.isolate_single_ring() self.show_specific_lattice_point_distance() self.count_through_rings() @@ -854,7 +850,6 @@ class CountThroughRings(LatticePointScene): ]) circles.set_stroke(width = 2) - self.revert_to_original_skipping_status() self.add_foreground_mobject(self.lattice_points) self.play(FadeIn(circles)) self.play(LaggedStart( @@ -864,16 +859,217 @@ class CountThroughRings(LatticePointScene): rate_func = there_and_back, )) self.dither() - self.remove_foreground_mobject(self.lattice_points) + digest_locals(self, ["circles", "radii"]) + def isolate_single_ring(self): + x, y = self.example_coords + example_circle = self.circles[ + self.radii.index(np.sqrt(x**2 + y**2)) + ] + self.circles.remove(example_circle) + points_on_example_circle = self.get_lattice_points_on_r_squared_circle( + x**2 + y**2 + ).copy() + + self.play( + FadeOut(self.circles), + self.lattice_points.set_fill, GREY, 0.5, + Animation(points_on_example_circle) + ) + self.dither() + + digest_locals(self, ["points_on_example_circle", "example_circle"]) def show_specific_lattice_point_distance(self): - pass + x, y = self.example_coords + dot = Dot( + self.plane.coords_to_point(x, y), + color = self.dot_color, + radius = self.dot_radius + ) + label = TexMobject("(a, b)") + num_label = TexMobject(str(self.example_coords)) + for mob in label, num_label: + mob.add_background_rectangle() + mob.next_to(dot, UP + RIGHT, SMALL_BUFF) + a, b = label[1][1].copy(), label[1][3].copy() + + x_spot = self.plane.coords_to_point(x, 0) + radial_line = Line(self.plane_center, dot) + h_line = Line(self.plane_center, x_spot) + h_line.highlight(GREEN) + v_line = Line(x_spot, dot) + v_line.highlight(RED) + + distance = TexMobject("\\sqrt{a^2 + b^2}") + distance_num = TexMobject("\\sqrt{%d}"%(x**2 + y**2)) + for mob in distance, distance_num: + mob.scale(0.75) + mob.add_background_rectangle() + mob.next_to(radial_line.get_center(), UP, SMALL_BUFF) + mob.rotate( + radial_line.get_angle(), + about_point = mob.get_bottom() + ) + + self.play(Write(label)) + self.play( + ApplyMethod(a.next_to, h_line, DOWN, SMALL_BUFF), + ApplyMethod(b.next_to, v_line, RIGHT, SMALL_BUFF), + ShowCreation(h_line), + ShowCreation(v_line), + ) + self.play(ShowCreation(radial_line)) + self.play(Write(distance)) + self.dither(2) + + a_num, b_num = [ + TexMobject(str(coord))[0] + for coord in self.example_coords + ] + a_num.move_to(a, UP) + b_num.move_to(b, LEFT) + self.play( + Transform(label, num_label), + Transform(a, a_num), + Transform(b, b_num), + ) + self.dither() + self.play(Transform(distance, distance_num)) + self.dither(3) + self.play(*map(FadeOut, [ + self.example_circle, self.points_on_example_circle, + distance, a, b, + radial_line, h_line, v_line, + label + ])) def count_through_rings(self): - pass + counts = [ + len(self.get_lattice_points_on_r_squared_circle(N)) + for N in range(self.max_lattice_point_radius**2 + 1) + ] + left_list = VGroup(*[ + TexMobject( + "\\sqrt{%d} \\Rightarrow"%n, str(count) + ) + for n, count in zip( + range(self.num_rings_to_show_explicitly), + counts + ) + ]) + left_counts = VGroup() + left_roots = VGroup() + for mob in left_list: + mob[1].highlight(YELLOW) + left_counts.add(VGroup(mob[1])) + mob.add_background_rectangle() + left_roots.add(VGroup(mob[0], mob[1][0])) + + left_list.arrange_submobjects( + DOWN, + buff = MED_LARGE_BUFF, + aligned_edge = LEFT, + ) + left_list.to_corner(UP + LEFT) + + top_list = VGroup(*[ + TexMobject("%d, "%count) + for count in counts + ]) + top_list.highlight(YELLOW) + top_list.arrange_submobjects(RIGHT, aligned_edge = DOWN) + top_list.scale_to_fit_width(2*SPACE_WIDTH - MED_LARGE_BUFF) + top_list.to_edge(UP, buff = SMALL_BUFF) + top_rect = BackgroundRectangle(top_list) + + for r_squared, count_mob, root in zip(it.count(), left_counts, left_roots): + self.show_ring_count( + r_squared, + count_mob, + added_anims = [FadeIn(root)] + ) + self.dither(2) + self.play( + FadeOut(left_roots), + FadeIn(top_rect), + *[ + ReplacementTransform( + lc, VGroup(tc), + path_arc = np.pi/2 + ) + for lc, tc in zip(left_counts, top_list) + ] + ) + for r_squared in range(len(left_counts), self.max_lattice_point_radius**2 + 1): + self.show_ring_count( + r_squared, top_list[r_squared], + ) + self.dither(3) + + + def show_ring_count( + self, radius_squared, target, + added_anims = None, + run_time = 1 + ): + added_anims = added_anims or [] + radius = np.sqrt(radius_squared) + points = self.get_lattice_points_on_r_squared_circle(radius_squared) + points.save_state() + circle = self.get_circle(radius) + radial_line = Line( + self.plane_center, self.plane.coords_to_point(radius, 0), + color = RED + ) + root = TexMobject("\\sqrt{%d}"%radius_squared) + root.add_background_rectangle() + root.scale_to_fit_width( + min(0.7*radial_line.get_width(), root.get_width()) + ) + root.next_to(radial_line, DOWN, SMALL_BUFF) + if not hasattr(self, "little_circle"): + self.little_circle = circle + if not hasattr(self, "radial_line"): + self.radial_line = radial_line + if not hasattr(self, "root"): + self.root = root + if hasattr(self, "last_points"): + added_anims += [self.last_points.restore] + self.last_points = points + + if radius_squared == 0: + points.set_fill(YELLOW, 1) + self.play( + DrawBorderThenFill(points, stroke_color = PINK), + *added_anims, + run_time = run_time + ) + self.play(ReplacementTransform( + points.copy(), target + )) + return + points.set_fill(YELLOW, 1) + self.play( + Transform(self.little_circle, circle), + Transform(self.radial_line, radial_line), + Transform(self.root, root), + DrawBorderThenFill( + points, + stroke_width = 4, + stroke_color = PINK, + ), + *added_anims, + run_time = run_time + ) + self.dither(run_time) + if len(points) > 0: + mover = points.copy() + else: + mover = VectorizedPoint(self.plane_center) + self.play(ReplacementTransform(mover, target, run_time = run_time)) diff --git a/scene/scene.py b/scene/scene.py index 09bb2a60..d0a8f682 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -38,6 +38,7 @@ class Scene(object): self.foreground_mobjects = [] self.num_plays = 0 self.saved_frames = [] + self.shared_locals = {} if self.name is None: self.name = self.__class__.__name__ @@ -66,6 +67,20 @@ class Scene(object): self.name = name return self + def update_shared_locals(self, *keys): + """ + Often in constructing a scene, it's nice to refer to + what was a local variable from a previous subroutine, + so a dict of shared_locals is recorded, and it can be updated + by passing in the objects directly. + """ + caller_locals = inspect.currentframe().f_back.f_locals + self.shared_locals.update(dict([ + (key, caller_locals[key]) + for key in keys + ])) + return self + ### Only these methods should touch the camera def set_camera(self, camera):