diff --git a/active_projects/wallis_g.py b/active_projects/wallis_g.py new file mode 100644 index 00000000..cf90cefa --- /dev/null +++ b/active_projects/wallis_g.py @@ -0,0 +1,303 @@ +from big_ol_pile_of_manim_imports import * +from once_useful_constructs.light import AmbientLight +from once_useful_constructs.light import Lighthouse +from once_useful_constructs.light import SwitchOn +# from once_useful_constructs.light import LightSource + + +class IntroduceDistanceProduct(MovingCameraScene): + CONFIG = { + "ambient_light_config": { + "opacity_function": inverse_quadratic(1, 1.1, 1), + "num_levels": 100, + # "num_levels": 10, + "light_radius": 10, + # "radius": 1, + "max_opacity": 0.4, + "color": YELLOW, + }, + "num_lighthouses": 6, + "circle_radius": 3, + "observer_color": MAROON_B, + "lighthouse_height": 0.5, + "camera_class": MovingCamera, + } + + def construct(self): + self.draw_circle_with_points() + self.turn_into_lighthouses_and_observer() + self.show_sum_of_inverse_squares() + + def draw_circle_with_points(self): + circle = Circle(color=BLUE) + circle.scale(self.circle_radius) + + lh_dots = self.lh_dots = VGroup(*[ + Dot().move_to(circle.point_from_proportion(alpha)) + for alpha in np.arange(0, 1, 1.0 / self.num_lighthouses) + ]) + lh_dot_arrows = VGroup(*[ + Arrow(*[ + interpolate(circle.get_center(), dot.get_center(), a) + for a in 0.6, 0.9 + ], buff=0) + for dot in lh_dots + ]) + evenly_space_dots_label = TextMobject("Evenly-spaced \\\\ dots") + evenly_space_dots_label.scale_to_fit_width(0.5 * circle.get_width()) + evenly_space_dots_label.move_to(circle) + + special_dot = self.special_dot = Dot(color=self.observer_color) + special_dot.move_to(circle.point_from_proportion(0.04)) + special_dot_arrow = Vector(DL) + special_dot_arrow.next_to(special_dot, UR, SMALL_BUFF) + special_dot_arrow.match_color(special_dot) + special_dot_label = TextMobject("Special dot") + special_dot_label.next_to( + special_dot_arrow.get_start(), UP, SMALL_BUFF) + special_dot_label.match_color(special_dot) + special_dot.save_state() + special_dot.next_to(special_dot_arrow, UR) + special_dot.set_fill(opacity=0) + + self.play(ShowCreation(circle)) + self.play( + LaggedStart(ShowCreation, lh_dots), + LaggedStart(GrowArrow, lh_dot_arrows), + Write(evenly_space_dots_label) + ) + self.wait() + self.play( + special_dot.restore, + GrowArrow(special_dot_arrow), + Write(special_dot_label, run_time=1), + FadeOut(VGroup(lh_dot_arrows, evenly_space_dots_label)) + ) + self.wait() + self.play(FadeOut(VGroup(special_dot_arrow, special_dot_label))) + + def turn_into_lighthouses_and_observer(self): + lighthouses = self.lighthouses = VGroup() + lights = self.lights = VGroup() + for dot in self.lh_dots: + point = dot.get_center() + lighthouse = Lighthouse() + lighthouse.scale_to_fit_height(self.lighthouse_height) + lighthouse.move_to(point) + lighthouses.add(lighthouse) + + light = AmbientLight( + source_point=VectorizedPoint(point), + **self.ambient_light_config + ) + lights.add(light) + + observer = self.observer = PiCreature(color=self.observer_color) + observer.flip() + observer.move_to(self.special_dot) + observer.to_edge(RIGHT) + + self.play( + LaggedStart(FadeOut, self.lh_dots), + LaggedStart(FadeIn, lighthouses), + LaggedStart(SwitchOn, lights), + ) + self.wait() + self.play(FadeIn(observer)) + self.play( + observer.scale_to_fit_height, 0.25, + observer.next_to, self.special_dot, RIGHT, 0.5 * SMALL_BUFF, + ) + self.wait() + + def show_sum_of_inverse_squares(self): + lines = VGroup(*[ + Line(self.special_dot.get_center(), dot.get_center()) + for dot in self.lh_dots + ]) + labels = VGroup(*[TexMobject("d_%d" % i) for i in range(len(lines))]) + for label, line in zip(labels, lines): + label.scale(0.75) + vect = rotate_vector(line.get_vector(), TAU / 4) + vect *= 2 * SMALL_BUFF / np.linalg.norm(vect) + label.move_to(line.get_center() + vect) + + sum_of_inverse_squares = TexMobject(*it.chain(*[ + ["{1", "\\over", "(", "d_%d" % i, ")", "^2}", "+"] + for i in range(len(lines)) + ])) + sum_of_inverse_squares.submobjects.pop(-1) + sum_of_inverse_squares.to_edge(UP) + d_terms = sum_of_inverse_squares.get_parts_by_tex("d_") + d_terms.set_color(YELLOW) + plusses = sum_of_inverse_squares.get_parts_by_tex("+") + last_term = sum_of_inverse_squares[-6:] + non_d_terms = VGroup(*filter( + lambda m: m not in d_terms and m not in last_term, + sum_of_inverse_squares + )) + + brace = Brace(sum_of_inverse_squares, DOWN) + brace_text = brace.get_text("Total intensity of light") + + arrow = Vector(DOWN, color=WHITE).next_to(brace, DOWN) + basel_sum = TexMobject( + "{1 \\over 1^2} + ", + "{1 \\over 2^2} + ", + "{1 \\over 3^2} + ", + "{1 \\over 4^2} + ", + "\\cdots", + ) + basel_sum.next_to(arrow, DOWN) + basel_cross = Cross(basel_sum) + useful_for = TextMobject("Useful for") + useful_for.next_to(arrow, RIGHT) + + wallis_product = TexMobject( + "{2 \\over 1} \\cdot", "{2 \\over 3} \\cdot", + "{4 \\over 3} \\cdot", "{4 \\over 5} \\cdot", + "{6 \\over 5} \\cdot", "{6 \\over 7} \\cdot", + "\\cdots" + ) + wallis_product.move_to(basel_sum) + + light_rings = VGroup(*it.chain(*self.lights)) + + self.play( + LaggedStart(ShowCreation, lines), + LaggedStart(Write, labels), + ) + circle_group = VGroup(*self.get_top_level_mobjects()) + self.wait() + self.play( + ReplacementTransform(labels[-1].copy(), last_term[3]), + Write(VGroup(*it.chain(last_term[:3], last_term[4:]))) + ) + self.remove(last_term) + self.add(last_term) + self.wait() + self.play( + Write(non_d_terms), + ReplacementTransform( + labels[:-1].copy(), + d_terms[:-1], + ), + circle_group.scale, 0.8, {"about_edge": DOWN} + ) + self.wait() + self.play(LaggedStart( + ApplyMethod, light_rings, + lambda m: (m.set_fill, {"opacity": 2 * m.get_fill_opacity()}), + rate_func=there_and_back, + run_time=3, + )) + self.wait() + + # Mention useful just to basel problem + circle_group.save_state() + self.play( + circle_group.scale, 0.5, + circle_group.to_corner, DL, + ) + self.play( + GrowFromCenter(brace), + Write(brace_text) + ) + self.wait() + self.play( + FadeOut(brace_text), + GrowArrow(arrow), + FadeIn(useful_for), + FadeIn(basel_sum), + ) + self.wait() + self.play( + ShowCreation(basel_cross), + FadeOut(VGroup(arrow, useful_for, brace)) + ) + basel_group = VGroup(basel_sum, basel_cross) + self.play( + basel_group.scale, 0.5, + basel_group.to_corner, DR, + ) + self.play(Write(wallis_product)) + self.wait() + + # Transition to distance product + self.play( + circle_group.restore, + wallis_product.match_width, basel_sum, + wallis_product.next_to, basel_sum, UP, {"aligned_edge": RIGHT}, + ) + self.play( + d_terms.shift, d_terms.get_height() * UP / 2, + *[ + FadeOut(mob) + for mob in sum_of_inverse_squares + if mob not in d_terms and mob not in plusses + ] + ) + self.wait() + self.play( + FadeOut(plusses), + d_terms.arrange_submobjects, RIGHT, 0.5 * SMALL_BUFF, + d_terms.move_to, sum_of_inverse_squares, DOWN, + ) + self.wait() + + # Label distance product + brace = Brace(d_terms, UP, buff=SMALL_BUFF) + distance_product_label = brace.get_text("``Distance product''") + + self.play( + GrowFromCenter(brace), + Write(distance_product_label) + ) + line_copies = lines.copy().set_color(RED) + self.play(LaggedStart(ShowCreationThenDestruction, line_copies)) + self.wait() + self.play(LaggedStart( + ApplyFunction, light_rings, + lambda mob: ( + lambda m: m.shift(MED_SMALL_BUFF * UP).set_fill(opacity=2 * m.get_fill_opacity()), + mob + ), + rate_func=wiggle, + run_time=6, + )) + self.wait() + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/camera/moving_camera.py b/camera/moving_camera.py index eb68fdde..dab64fe7 100644 --- a/camera/moving_camera.py +++ b/camera/moving_camera.py @@ -1,6 +1,9 @@ from __future__ import absolute_import +from constants import FRAME_HEIGHT + from camera.camera import Camera +from mobject.frame import ScreenRectangle class MovingCamera(Camera): @@ -12,11 +15,14 @@ class MovingCamera(Camera): "aligned_dimension": "width" # or height } - def __init__(self, frame, **kwargs): + def __init__(self, frame=None, **kwargs): """ frame is a Mobject, (should be a rectangle) determining which region of space the camera displys """ + if frame is None: + frame = ScreenRectangle(height=FRAME_HEIGHT) + frame.fade(1) self.frame = frame Camera.__init__(self, **kwargs) diff --git a/constants.py b/constants.py index af068348..8950d90b 100644 --- a/constants.py +++ b/constants.py @@ -67,6 +67,12 @@ X_AXIS = np.array((1., 0., 0.)) Y_AXIS = np.array((0., 1., 0.)) Z_AXIS = np.array((0., 0., 1.)) +# Useful abbreviations for diagonals +UL = UP + LEFT +UR = UP + RIGHT +DL = DOWN + LEFT +DR = DOWN + RIGHT + TOP = FRAME_Y_RADIUS * UP BOTTOM = FRAME_Y_RADIUS * DOWN LEFT_SIDE = FRAME_X_RADIUS * LEFT