From e2fc29851efa0818a5ff39d3059a6985102dbe7c Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Fri, 9 Oct 2015 19:53:38 -0700 Subject: [PATCH] Much pythagoras, plus better point thickness and display implementations --- animation/transform.py | 4 +- constants.py | 5 + displayer.py | 111 +- helpers.py | 7 +- mobject/image_mobject.py | 10 +- mobject/mobject.py | 20 +- mobject/simple_mobjects.py | 23 +- mobject/three_dimensional_mobjects.py | 2 +- region.py | 6 + sample_script.py | 4 +- scene/scene.py | 23 +- scripts/inventing_math.py | 2168 ++++++++++++++++++++++ scripts/old_scripts/music_and_measure.py | 6 +- scripts/pythagorean_proof.py | 523 ++++++ 14 files changed, 2831 insertions(+), 81 deletions(-) create mode 100644 scripts/inventing_math.py create mode 100644 scripts/pythagorean_proof.py diff --git a/animation/transform.py b/animation/transform.py index 9bf1ce32..2b7ca1e5 100644 --- a/animation/transform.py +++ b/animation/transform.py @@ -48,8 +48,8 @@ class Transform(Animation): Animation.__init__(self, mobject, **kwargs) self.name += "To" + str(ending_mobject) - self.mobject.should_buffer_points = \ - mobject.should_buffer_points and ending_mobject.should_buffer_points + self.mobject.point_thickness = ending_mobject.point_thickness + def black_out_extra_points(self, count1, count2): #Ensure redundant pixels fade to black diff --git a/constants.py b/constants.py index 40fdaf68..b50a3bd3 100644 --- a/constants.py +++ b/constants.py @@ -46,6 +46,11 @@ LEFT = np.array((-1, 0, 0)) IN = np.array(( 0, 0,-1)) OUT = np.array(( 0, 0, 1)) +TOP = SPACE_HEIGHT*UP +BOTTOM = SPACE_HEIGHT*DOWN +LEFT_SIDE = SPACE_WIDTH*LEFT +RIGHT_SIDE = SPACE_WIDTH*RIGHT + THIS_DIR = os.path.dirname(os.path.realpath(__file__)) FILE_DIR = os.path.join(THIS_DIR, "files") IMAGE_DIR = os.path.join(FILE_DIR, "images") diff --git a/displayer.py b/displayer.py index e8122cbe..9b5a8173 100644 --- a/displayer.py +++ b/displayer.py @@ -33,59 +33,80 @@ def paint_region(region, image_array = None, color = None): return pixels def paint_mobject(mobject, image_array = None): - pixels = get_pixels(image_array) - if mobject.get_num_points() == 0: - return pixels + return paint_mobjects([mobject], image_array) + +def paint_mobjects(mobjects, image_array = None): + pixels = get_pixels(image_array) height = pixels.shape[0] width = pixels.shape[1] space_height = SPACE_HEIGHT space_width = SPACE_HEIGHT * width / height - # camera_distance = 10 + pixels = pixels.reshape((pixels.size/3, 3)) + + for mobject in mobjects: + if mobject.get_num_points() == 0: + continue + points, rgbs = place_on_screen(mobject.points, mobject.rgbs, + space_width, space_height) + #Map points to pixel space, which requires rescaling and shifting + #Remember, 2*space_height -> height + points[:,0] = points[:,0]*width/space_width/2 + width/2 + #Flip on y-axis as you go + points[:,1] = -1*points[:,1]*height/space_height/2 + height/2 + points, rgbs = add_thickness( + points.astype('int'), rgbs, + mobject.point_thickness, + width, height + ) + + flattener = np.array([[1], [width]], dtype = 'int') + indices = np.dot(points, flattener) + pixels[indices[:,0]] = (255*rgbs).astype(int) - points = np.array(mobject.points[:, :2]) - # for i in range(2): - # points[:,i] *= camera_distance/(camera_distance-mobject.points[:,2]) - rgbs = np.array(mobject.rgbs) - #Flips y-axis - points[:,1] *= -1 - #Removes points out of space - to_remove = (abs(points[:,0]) < SPACE_WIDTH) & \ - (abs(points[:,1]) < SPACE_HEIGHT) - points = points[to_remove] - rgbs = rgbs[to_remove] - #Map points to pixel space, then create pixel array first in terms - #of its flattened version - try: - points += np.array( - [space_width, space_height]*points.shape[0] - ).reshape(points.shape) - except: - print points.shape, mobject.points.shape - points *= np.array( - [width / (2.0 * space_width), height / (2.0 * space_height)]*\ - points.shape[0] - ).reshape(points.shape) - points = points.astype('int') - flattener = np.array([1, width], dtype = 'int').reshape((2, 1)) - indices = np.dot(points, flattener) - indices = indices.reshape(indices.size) - if mobject.should_buffer_points:#Is this alright? - for tweak in [ - indices + 1, - indices + width, - indices + width + 1 - ]: - indices = np.append(indices, tweak) - rgbs = np.array(list(rgbs) * 4) - admissibles = (indices < height * width) * (indices > 0) - indices = indices[admissibles] - rgbs = rgbs[admissibles] - rgbs = (rgbs * 255).astype(int) - pixels = pixels.reshape((height * width, 3)) - pixels[indices] = rgbs.reshape((rgbs.size/3), 3)#WHY? pixels = pixels.reshape((height, width, 3)).astype('uint8') return pixels +def add_thickness(pixel_indices, rgbs, thickness, width, height): + """ + Imagine dragging each pixel around like a paintbrush in + a square of pixels tickness x thickness big surrounding it + """ + nudge_values = range(-thickness/2+1, thickness/2+1) + original = np.array(pixel_indices) + original_rgbs = np.array(rgbs) + for x, y in it.product(nudge_values, nudge_values): + if x == 0 and y == 0: + continue + pixel_indices = np.append( + pixel_indices, + original+[x, y], + axis = 0 + ) + rgbs = np.append(rgbs, original_rgbs, axis = 0) + admissibles = (pixel_indices[:,0] >= 0) & \ + (pixel_indices[:,0] < width) & \ + (pixel_indices[:,1] >= 0) & \ + (pixel_indices[:,1] < height) + return pixel_indices[admissibles], rgbs[admissibles] + + + +def place_on_screen(points, rgbs, space_width, space_height): + """ + Projects points to 2d space and remove those outside a + the space constraints + """ + # camera_distance = 10 + points = np.array(points[:, :2]) + # for i in range(2): + # points[:,i] *= camera_distance/(camera_distance-mobject.points[:,2]) + rgbs = np.array(rgbs) + + #Removes points out of space + to_keep = (abs(points[:,0]) < space_width) & \ + (abs(points[:,1]) < space_height) + return points[to_keep], rgbs[to_keep] + def write_to_gif(scene, name): #TODO, find better means of compression if not name.endswith(".gif"): diff --git a/helpers.py b/helpers.py index 939ea7e3..a1354dd3 100644 --- a/helpers.py +++ b/helpers.py @@ -238,8 +238,11 @@ def angle_between(v1, v2): v2 / np.linalg.norm(v2) )) - - +def angle_of_vector(vector): + """ + Returns polar coordinate theta when vector is project on xy plane + """ + return np.log(complex(*vector[:2])).imag diff --git a/mobject/image_mobject.py b/mobject/image_mobject.py index df94a535..8d3dcfdc 100644 --- a/mobject/image_mobject.py +++ b/mobject/image_mobject.py @@ -15,7 +15,7 @@ class ImageMobject(Mobject): "filter_color" : "black", "invert" : True, "use_cache" : True, - "should_buffer_points" : False, + "point_thickness" : 1, "scale_value" : 1.0, "should_center" : True } @@ -134,7 +134,7 @@ def tex_mobject(expression, #Todo, make this more sophisticated. image_files = tex_to_image(expression, size, template_tex_file) config = { - "should_buffer_points" : True, + "point_thickness" : 1, "should_center" : False, } if isinstance(image_files, list): @@ -148,6 +148,10 @@ def tex_mobject(expression, return result.center().highlight("white") - +def underbrace(left, right): + result = tex_mobject("\\underbrace{%s}"%(14*"\\quad")) + result.stretch_to_fit_width(right[0]-left[0]) + result.shift(left - result.points[0]) + return result diff --git a/mobject/mobject.py b/mobject/mobject.py index 5553c533..6aaff828 100644 --- a/mobject/mobject.py +++ b/mobject/mobject.py @@ -21,7 +21,7 @@ class Mobject(object): #Number of numbers used to describe a point (3 for pos, 3 for normal vector) DEFAULT_CONFIG = { "color" : "white", - "should_buffer_points" : GENERALLY_BUFFER_POINTS, + "point_thickness" : 2, "name" : None, } DIM = 3 @@ -156,9 +156,12 @@ class Mobject(object): if aligned_edge is not None: anchor_point = self.get_corner(aligned_edge-direction) target_point = mobject.get_corner(aligned_edge+direction) - else: + elif list(direction) in map(list, [LEFT, RIGHT, UP, DOWN]): anchor_point = self.get_edge_center(-direction) target_point = mobject.get_edge_center(direction) + else: + anchor_point = self.get_boundary_point(-direction) + target_point = mobject.get_boundary_point(direction) self.shift(target_point - anchor_point + buff*direction) return self @@ -353,13 +356,13 @@ class Mobject1D(Mobject): self.epsilon = 1.0 / self.density Mobject.__init__(self, **kwargs) - def add_line(self, start, end, min_density = 0.1): + def add_line(self, start, end, min_density = 0.1, color = None): length = np.linalg.norm(end - start) epsilon = self.epsilon / max(length, min_density) self.add_points([ interpolate(start, end, t) for t in np.arange(0, 1, epsilon) - ]) + ], color = color) class Mobject2D(Mobject): DEFAULT_CONFIG = { @@ -377,11 +380,10 @@ class CompoundMobject(Mobject): for mobject in mobjects: self.original_mobs_num_points.append(mobject.points.shape[0]) self.add_points(mobject.points, mobject.rgbs) - self.should_buffer_points = reduce( - op.and_, - [m.should_buffer_points for m in mobjects], - GENERALLY_BUFFER_POINTS - ) + self.point_thickness = max([ + m.point_thickness + for m in mobjects + ]) def split(self): result = [] diff --git a/mobject/simple_mobjects.py b/mobject/simple_mobjects.py index c2307948..ef39b04f 100644 --- a/mobject/simple_mobjects.py +++ b/mobject/simple_mobjects.py @@ -163,17 +163,29 @@ class Circle(Mobject1D): class Polygon(Mobject1D): DEFAULT_CONFIG = { "color" : "limegreen", + "edge_colors" : None } def __init__(self, *points, **kwargs): assert len(points) > 1 digest_config(self, Polygon, kwargs) - self.vertices = points + self.original_points = points Mobject1D.__init__(self, **kwargs) def generate_points(self): - points = list(self.vertices) + [self.vertices[0]] + if self.edge_colors: + colors = it.cycle(self.edge_colors) + else: + colors = it.cycle([self.color]) + self.indices_of_vertices = [] + points = list(self.original_points) + points.append(points[0]) for start, end in zip(points, points[1:]): - self.add_line(start, end) + self.indices_of_vertices.append(self.get_num_points()) + self.add_line(start, end, color = colors.next()) + + + def get_vertices(self): + return self.points[self.indices_of_vertices] class Rectangle(Mobject1D): @@ -197,11 +209,12 @@ class Rectangle(Mobject1D): class Square(Rectangle): DEFAULT_CONFIG = { - "height" : 2.0, - "width" : 2.0, + "side_length" : 2.0, } def __init__(self, **kwargs): digest_config(self, Square, kwargs) + for arg in ["height", "width"]: + kwargs[arg] = self.side_length Rectangle.__init__(self, **kwargs) class Bubble(Mobject): diff --git a/mobject/three_dimensional_mobjects.py b/mobject/three_dimensional_mobjects.py index 605e69c7..372bccac 100644 --- a/mobject/three_dimensional_mobjects.py +++ b/mobject/three_dimensional_mobjects.py @@ -8,7 +8,7 @@ from helpers import * class Stars(Mobject): DEFAULT_CONFIG = { - "should_buffer_points" : False, + "point_thickness" : 1, "radius" : SPACE_WIDTH, "num_points" : 1000, } diff --git a/region.py b/region.py index 6ed9b85c..fc4e030b 100644 --- a/region.py +++ b/region.py @@ -84,6 +84,12 @@ def region_from_line_boundary(*lines, **kwargs): reg.intersect(HalfPlane(line, **kwargs)) return reg +def region_from_polygon_vertices(*vertices, **kwargs): + points = list(vertices) + points.append(points[0]) + return region_from_line_boundary(*zip(points, points[1:]), **kwargs) + + def plane_partition(*lines, **kwargs): """ A 'line' is a pair of points [(x0, y0,...), (x1, y1,...)] diff --git a/sample_script.py b/sample_script.py index 0f1c64c7..f0b79852 100644 --- a/sample_script.py +++ b/sample_script.py @@ -16,8 +16,8 @@ from script_wrapper import command_line_create_scene class SampleScene(Scene): def construct(self): - plane = NumberPlane(density = 400) - arrow1 = Arrow(ORIGIN, UP, color = "green") + plane = NumberPlane() + arrow1 = Arrow(ORIGIN, UP, color = "green", point_thickness = 5) arrow2 = Arrow(ORIGIN, LEFT, color = "Red") self.add(plane, arrow1, arrow2) diff --git a/scene/scene.py b/scene/scene.py index bae92d1e..d8565f7b 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -7,6 +7,7 @@ import time import os import copy import progressbar +import inspect from helpers import * from mobject import * @@ -63,6 +64,16 @@ class Scene(object): self.mobjects.append(mobject) return self + def add_local_mobjects(self): + """ + So a scene can just add all mobjects it's defined up to that point + """ + caller_locals = inspect.currentframe().f_back.f_locals + self.add(*filter( + lambda m : isinstance(m, Mobject), + caller_locals.values() + )) + def remove(self, *mobjects): for mobject in mobjects: if not isinstance(mobject, Mobject): @@ -106,9 +117,7 @@ class Scene(object): #This way current mobjects don't have to be redrawn with #every change, and one can later call "apply" without worrying #about it applying to these mobjects - self.background = disp.paint_mobject( - CompoundMobject(*mobjects), self.background - ) + self.background = disp.paint_mobjects(mobjects, self.background) return self def play(self, *animations, **kwargs): @@ -130,9 +139,7 @@ class Scene(object): progress_bar.update(t) for animation in animations: animation.update(t / animation.run_time) - new_frame = disp.paint_mobject( - CompoundMobject(*moving_mobjects), background - ) + new_frame = disp.paint_mobjects(moving_mobjects, background) self.frames.append(new_frame) for animation in animations: animation.clean_up() @@ -172,9 +179,7 @@ class Scene(object): ]) def get_frame(self): - return disp.paint_mobject( - CompoundMobject(*self.mobjects), self.background - ) + return disp.paint_mobjects(self.mobjects, self.background) def dither(self, duration = DEFAULT_DITHER_TIME): self.frames += [self.get_frame()]*int(duration / self.frame_duration) diff --git a/scripts/inventing_math.py b/scripts/inventing_math.py new file mode 100644 index 00000000..5ac3d580 --- /dev/null +++ b/scripts/inventing_math.py @@ -0,0 +1,2168 @@ +#!/usr/bin/env python + +import numpy as np +import itertools as it +from copy import deepcopy +import sys +import operator as op +from random import sample + + +from animation import * +from mobject import * +from constants import * +from region import * +from scene import Scene, RearrangeEquation +from script_wrapper import command_line_create_scene + +# from inventing_math_images import * + +MOVIE_PREFIX = "inventing_math/" +DIVERGENT_SUM_TEXT = [ + "1", + "+2", + "+4", + "+8", + "+\\cdots", + "+2^n", + "+\\cdots", + "= -1", +] + +CONVERGENT_SUM_TEXT = [ + "\\frac{1}{2}", + "+\\frac{1}{4}", + "+\\frac{1}{8}", + "+\\frac{1}{16}", + "+\\cdots", + "+\\frac{1}{2^n}", + "+\\cdots", + "=1", +] + +CONVERGENT_SUM_TERMS = [ + "\\frac{1}{2}", + "\\frac{1}{4}", + "\\frac{1}{8}", + "\\frac{1}{16}", +] + +PARTIAL_CONVERGENT_SUMS_TEXT = [ + "\\frac{1}{2}", + "", "", ",\\quad", + "\\frac{1}{2} + \\frac{1}{4}", + "=", "\\frac{3}{4}", ",\\quad", + "\\frac{1}{2} + \\frac{1}{4} + \\frac{1}{8}", + "=", "\\frac{7}{8}", ",\\quad", + "\\frac{1}{2} + \\frac{1}{4} + \\frac{1}{8} + \\frac{1}{16}", + "=", "\\frac{15}{16}", ",\\dots" +] + +def partial_sum(n): + return sum([1.0/2**(k+1) for k in range(n)]) + +ALT_PARTIAL_SUM_TEXT = reduce(op.add, [ + [str(partial_sum(n)), "&=", "+".join(CONVERGENT_SUM_TERMS[:n])+"\\\\"] + for n in range(1, len(CONVERGENT_SUM_TERMS)+1) +])+ [ + "\\vdots", "&", "\\\\", + "1.0", "&=", "+".join(CONVERGENT_SUM_TERMS)+"+\\cdots+\\frac{1}{2^n}+\\cdots" +] + + +NUM_WRITTEN_TERMS = 4 +INTERVAL_RADIUS = 5 +NUM_INTERVAL_TICKS = 16 + + +def divergent_sum(): + return tex_mobject(DIVERGENT_SUM_TEXT, size = "\\large").scale(2) + +def convergent_sum(): + return tex_mobject(CONVERGENT_SUM_TEXT, size = "\\large").scale(2) + +def zero_to_one_interval(): + interval = NumberLine( + radius = INTERVAL_RADIUS, + interval_size = 2.0*INTERVAL_RADIUS/NUM_INTERVAL_TICKS + ) + interval.elongate_tick_at(-INTERVAL_RADIUS, 4) + interval.elongate_tick_at(INTERVAL_RADIUS, 4) + zero = tex_mobject("0").shift(INTERVAL_RADIUS*LEFT+DOWN) + one = tex_mobject("1").shift(INTERVAL_RADIUS*RIGHT+DOWN) + return CompoundMobject(interval, zero, one) + +def draw_you(with_bubble = False): + result = PiCreature() + result.give_straight_face().highlight("grey") + result.to_corner(LEFT+DOWN) + result.rewire_part_attributes() + if with_bubble: + bubble = ThoughtBubble() + bubble.stretch_to_fit_width(11) + bubble.pin_to(result) + return result, bubble + return result + +def get_room_colors(): + return list(Color("yellow").range_to("red", 4)) + +def power_of_divisor(n, d): + result = 0 + while n%d == 0: + result += 1 + n /= d + return result + +class FlipThroughNumbers(Animation): + def __init__(self, function = lambda x : x, + start = 0, end = 10, + start_center = ORIGIN, + end_center = ORIGIN, + **kwargs): + self.function = function + self.start = start + self.end = end + self.start_center = start_center + self.end_center = end_center + self.current_number = function(start) + mobject = tex_mobject(str(self.current_number)).shift(start_center) + Animation.__init__(self, mobject, **kwargs) + + def update_mobject(self, alpha): + new_number = self.function( + self.start + int(alpha *(self.end-self.start)) + ) + if new_number != self.current_number: + self.current_number = new_number + self.mobject = tex_mobject(str(new_number)).shift(self.start_center) + if not all(self.start_center == self.end_center): + self.mobject.center().shift( + (1-alpha)*self.start_center + alpha*self.end_center + ) + + +###################################### + +class IntroduceDivergentSum(Scene): + def construct(self): + equation = divergent_sum().split() + sum_value = None + brace = underbrace( + equation[0].get_boundary_point(DOWN+LEFT), + equation[1].get_boundary_point(DOWN+RIGHT) + ).shift(0.2*DOWN) + min_x_coord = min(equation[0].points[:,0]) + for x in range(NUM_WRITTEN_TERMS): + self.add(equation[x]) + if x == 0: + self.dither(0.75) + continue + brace.stretch_to_fit_width( + max(equation[x].points[:,0]) - min_x_coord + ) + brace.to_edge(LEFT, buff = SPACE_WIDTH+min_x_coord) + if sum_value: + self.remove(sum_value) + sum_value = tex_mobject(str(2**(x+1) - 1)) + sum_value.shift(brace.get_center() + 0.5*DOWN) + self.add(brace, sum_value) + self.dither(0.75) + self.remove(sum_value) + ellipses = CompoundMobject( + *[equation[NUM_WRITTEN_TERMS + i] for i in range(3)] + ) + end_brace = deepcopy(brace).stretch_to_fit_width( + max(ellipses.points[:,0])-min_x_coord + ).to_edge(LEFT, buff = SPACE_WIDTH+min_x_coord) + kwargs = {"run_time" : 5.0, "alpha_func" : rush_into} + flip_through = FlipThroughNumbers( + lambda x : 2**(x+1)-1, + start = NUM_WRITTEN_TERMS-1, + end = 50, + start_center = brace.get_center() + 0.5*DOWN, + end_center = end_brace.get_center() + 0.5*DOWN, + **kwargs + ) + self.add(ellipses) + self.play( + Transform(brace, end_brace, **kwargs), + flip_through, + ) + self.clear() + self.add(*equation) + self.dither() + +class ClearlyNonsense(Scene): + def construct(self): + number_line = NumberLine().add_numbers() + div_sum = divergent_sum() + this_way = text_mobject("Sum goes this way...") + this_way.to_edge(LEFT).shift(RIGHT*(SPACE_WIDTH+1) + DOWN) + how_here = text_mobject("How does it end up here?") + how_here.shift(1.5*UP+LEFT) + neg_1_arrow = Arrow( + (-1, 0.3, 0), + tail=how_here.get_center()+0.5*DOWN + ) + right_arrow = Arrow( + (SPACE_WIDTH-0.5)*RIGHT + DOWN, + tail = (max(this_way.points[:,0]), -1, 0) + ) + how_here.highlight("red") + neg_1_arrow.highlight("red") + this_way.highlight("yellow") + right_arrow.highlight("yellow") + + self.play(Transform( + div_sum, + deepcopy(div_sum).scale(0.5).shift(3*UP) + )) + self.play(ShowCreation(number_line)) + self.dither() + self.add(how_here) + self.play(ShowCreation(neg_1_arrow)) + self.dither() + self.add(this_way) + self.play(ShowCreation(right_arrow)) + self.dither() + +class OutlineOfVideo(Scene): + def construct(self): + conv_sum = convergent_sum().scale(0.5) + div_sum = divergent_sum().scale(0.5) + overbrace = underbrace( + conv_sum.get_left(), + conv_sum.get_right() + ).rotate(np.pi, RIGHT).shift(0.75*UP*conv_sum.get_height()) + dots = conv_sum.split()[-2].highlight("green") + dots.sort_points() + arrow = Arrow( + dots.get_bottom(), + direction = UP+LEFT + ) + u_brace = underbrace(div_sum.get_left(), div_sum.get_right()) + u_brace.shift(1.5*div_sum.get_bottom()) + for mob in conv_sum, overbrace, arrow, dots: + mob.shift(2*UP) + for mob in div_sum, u_brace: + mob.shift(DOWN) + texts = [ + text_mobject(words).highlight("yellow") + for words in [ + "1. Discover this", + "2. Clarify what this means", + "3. Discover this", + ["4. Invent ", "\\textbf{new math}"] + ] + ] + last_one_split = texts[-1].split() + last_one_split[1].highlight("skyblue") + texts[-1] = CompoundMobject(*last_one_split) + texts[0].shift(overbrace.get_top()+texts[0].get_height()*UP) + texts[1].shift(sum([ + arrow.get_boundary_point(DOWN+RIGHT), + texts[1].get_height()*DOWN + ])) + texts[2].shift(u_brace.get_bottom()+texts[3].get_height()*DOWN) + texts[3].to_edge(DOWN) + + groups = [ + [texts[0], overbrace, conv_sum], + [texts[1], arrow, dots], + [texts[2], u_brace, div_sum], + [texts[3]] + ] + for group in groups: + self.play(*[ + DelayByOrder(FadeIn(element)) + for element in group + ]) + self.dither() + +# # class ReasonsForMakingVideo(Scene): +# # def construct(self): +# # text = text_mobject([ +# # """ +# # \\begin{itemize} +# # \\item Understand what ``$ +# # """, +# # "".join(DIVERGENT_SUM_TEXT), +# # """ +# # $'' is saying. +# # """, +# # """ +# # \\item Nonsense-Driven Construction +# # \\end{itemize} +# # """ +# # ], size = "\\Small") +# # text.scale(1.5).to_edge(LEFT).shift(UP).highlight("white") +# # text.highlight("green", lambda (x, y, z) : x < -SPACE_WIDTH + 1) +# # line_one_first, equation, line_one_last, line_two = text.split() +# # line_two.shift(2*DOWN) +# # div_sum = divergent_sum().scale(0.5).shift(3*UP) + +# # self.add(div_sum) +# # self.play( +# # ApplyMethod(div_sum.replace, equation), +# # FadeIn(line_one_first), +# # FadeIn(line_one_last) +# # ) +# # self.dither() +# # self.add(line_two) +# # self.dither() + +# class DiscoverAndDefine(Scene): +# def construct(self): +# sum_mob = tex_mobject("\\sum_{n = 1}^\\infty a_n") +# discover = text_mobject("What does it feel like to discover these?") +# define = text_mobject([ +# "What does it feel like to", +# "\\emph{define} ", +# "them?" +# ]) +# sum_mob.shift(2*UP) +# define.shift(2*DOWN) +# define_parts = define.split() +# define_parts[1].highlight("skyblue") + +# self.add(sum_mob) +# self.play(FadeIn(discover)) +# self.dither() +# self.play(FadeIn(CompoundMobject(*define_parts))) +# self.dither() + +class YouAsMathematician(Scene): + def construct(self): + you, bubble = draw_you(with_bubble = True) + explanation = text_mobject( + "You as a (questionably accurate portrayal of a) mathematician.", + size = "\\small" + ).shift([2, you.get_center()[1], 0]) + arrow = Arrow(you.get_center(), direction = LEFT) + arrow.nudge(you.get_width()) + for mob in arrow, explanation: + mob.highlight("yellow") + equation = convergent_sum() + bubble.add_content(equation) + equation_parts = equation.split() + equation.shift(0.5*RIGHT) + bubble.clear() + dot_pair = [ + Dot(density = 3*DEFAULT_POINT_DENSITY_1D).shift(x+UP) + for x in LEFT, RIGHT + ] + self.add(you, explanation) + self.play( + ShowCreation(arrow), + BlinkPiCreature(you) + ) + self.dither() + self.play(ShowCreation(bubble)) + for part in equation_parts: + self.play(DelayByOrder(FadeIn(part)), run_time = 0.5) + self.dither() + self.play( + BlinkPiCreature(you), + FadeOut(explanation), + FadeOut(arrow) + ) + self.remove(bubble, *equation_parts) + self.disapproving_friend() + self.add(bubble, equation) + self.play(Transform(equation, CompoundMobject(*dot_pair))) + self.remove(equation) + self.add(*dot_pair) + two_arrows = [ + Arrow(x, direction = x).shift(UP).nudge() + for x in LEFT, RIGHT + ] + self.play(*[ShowCreation(a) for a in two_arrows]) + self.play(BlinkPiCreature(you)) + self.remove(*dot_pair+two_arrows) + everything = CompoundMobject(*self.mobjects) + self.clear() + self.play( + ApplyPointwiseFunction( + lambda p : 3*SPACE_WIDTH*p/np.linalg.norm(p), + everything + ), + *[ + Transform(dot, deepcopy(dot).shift(DOWN).scale(3)) + for dot in dot_pair + ], + run_time = 2.0 + ) + self.dither() + + def disapproving_friend(self): + friend = Mortimer().to_corner(DOWN+RIGHT) + bubble = SpeechBubble().pin_to(friend) + bubble.write("It's simply not rigorous!") + bubble.content.sort_points(lambda p : np.dot(p, DOWN+RIGHT)) + + self.add(friend, bubble) + self.play(DelayByOrder(FadeIn(bubble.content))) + self.dither() + self.remove(friend, bubble, bubble.content) + + +class DotsGettingCloser(Scene): + def construct(self): + dots = [ + Dot(radius = 3*Dot.DEFAULT_RADIUS).shift(3*x) + for x in LEFT, RIGHT + ] + self.add(*dots) + self.dither() + for x in range(10): + distance = min(dots[1].points[:,0])-max(dots[0].points[:,0]) + self.play(ApplyMethod(dots[0].shift, 0.5*distance*RIGHT)) + + +class ZoomInOnInterval(Scene): + def construct(self): + number_line = NumberLine(density = 10*DEFAULT_POINT_DENSITY_1D) + number_line.add_numbers() + interval = zero_to_one_interval().split() + + new_line = deepcopy(number_line) + new_line.highlight("black", lambda (x,y,z) : x < 0 or x > 1 or y < -0.2) + # height = new_line.get_height() + new_line.scale(2*INTERVAL_RADIUS) + new_line.shift(INTERVAL_RADIUS*LEFT) + # new_line.stretch_to_fit_height(height) + + self.add(number_line) + self.dither() + self.play(Transform(number_line, new_line)) + self.clear() + squish = lambda p : (p[0], 0, 0) + self.play( + ApplyMethod(new_line.apply_function, squish), + ApplyMethod( + interval[0].apply_function, squish, + alpha_func = lambda t : 1-t + ), + *[FadeIn(interval[x]) for x in [1, 2]] + ) + self.clear() + self.add(*interval) + self.dither() + +class DanceDotOnInterval(Scene): + def construct(self, mode): + num_written_terms = NUM_WRITTEN_TERMS + prop = 0.5 + sum_terms = [ + "\\frac{1}{2}", + "\\frac{1}{4}", + "\\frac{1}{8}", + "\\frac{1}{16}", + ] + num_height = 1.3*DOWN + interval = zero_to_one_interval() + dots = [ + Dot(radius = 3*Dot.DEFAULT_RADIUS).shift(INTERVAL_RADIUS*x+UP) + for x in LEFT, RIGHT + ] + color_range = Color("green").range_to("yellow", num_written_terms) + conv_sum = tex_mobject(sum_terms, size = "\\large").split() + + self.add(interval) + self.play(*[ + ApplyMethod(dot.shift, DOWN) + for dot in dots + ]) + self.dither() + for count in range(num_written_terms): + shift_val = 2*RIGHT*INTERVAL_RADIUS*(1-prop)*(prop**count) + start = dots[0].get_center() + line = Line(start, start + shift_val*RIGHT) + line.highlight(color_range.next()) + self.play( + ApplyMethod(dots[0].shift, shift_val), + ShowCreation(line) + ) + num = conv_sum[count] + num.shift(RIGHT*(line.get_center()[0]-num.get_center()[0])) + num.shift(num_height) + arrow = Mobject() + if num.get_width() > line.get_length(): + num.center().shift(3*DOWN+2*(count-2)*RIGHT) + arrow = Arrow( + line.get_center()+2*DOWN, + tail = num.get_center()+0.5*num.get_height()*UP + ) + self.play( + ApplyMethod(line.shift, 2*DOWN), + FadeIn(num), + FadeIn(arrow), + ) + self.dither() + self.write_partial_sums() + self.dither() + + def write_partial_sums(self): + partial_sums = tex_mobject(PARTIAL_CONVERGENT_SUMS_TEXT, size = "\\small") + partial_sums.scale(1.5).to_edge(UP) + partial_sum_parts = partial_sums.split() + partial_sum_parts[0].highlight("yellow") + + for x in range(0, len(partial_sum_parts), 4): + partial_sum_parts[x+2].highlight("yellow") + self.play(*[ + FadeIn(partial_sum_parts[y]) + for y in range(x, x+4) + ]) + self.dither(2) + +class OrganizePartialSums(Scene): + def construct(self): + partial_sums = tex_mobject(PARTIAL_CONVERGENT_SUMS_TEXT, size = "\\small") + partial_sums.scale(1.5).to_edge(UP) + partial_sum_parts = partial_sums.split() + for x in [0] + range(2, len(partial_sum_parts), 4): + partial_sum_parts[x].highlight("yellow") + pure_sums = [ + partial_sum_parts[x] + for x in range(0, len(partial_sum_parts), 4) + ] + new_pure_sums = deepcopy(pure_sums) + for pure_sum, count in zip(new_pure_sums, it.count(3, -1.2)): + pure_sum.center().scale(1/1.25).highlight("white") + pure_sum.to_edge(LEFT).shift(2*RIGHT+count*UP) + + self.add(*partial_sum_parts) + self.dither() + self.play(*[ + ClockwiseTransform(*pair) + for pair in zip(pure_sums, new_pure_sums) + ]+[ + FadeOut(mob) + for mob in partial_sum_parts + if mob not in pure_sums + ]) + down_arrow = tex_mobject("\\downarrow") + down_arrow.to_edge(LEFT).shift(2*RIGHT+2*DOWN) + dots = tex_mobject("\\vdots") + dots.shift(down_arrow.get_center()+down_arrow.get_height()*UP) + infinite_sum = tex_mobject("".join(CONVERGENT_SUM_TEXT[:-1]), size = "\\samll") + infinite_sum.scale(1.5/1.25) + infinite_sum.to_corner(DOWN+LEFT).shift(2*RIGHT) + + self.play(ShowCreation(dots)) + self.dither() + self.play(FadeIn(CompoundMobject(down_arrow, infinite_sum))) + self.dither() + +class SeeNumbersApproachOne(Scene): + def construct(self): + interval = zero_to_one_interval() + arrow = Arrow(INTERVAL_RADIUS*RIGHT, tail=ORIGIN).nudge() + arrow.shift(DOWN).highlight("yellow") + num_dots = 6 + colors = Color("green").range_to("yellow", num_dots) + dots = CompoundMobject(*[ + Dot( + density = 2*DEFAULT_POINT_DENSITY_1D + ).scale(1+1.0/2.0**x).shift( + INTERVAL_RADIUS*RIGHT +\ + (INTERVAL_RADIUS/2.0**x)*LEFT + ).highlight(colors.next()) + for x in range(num_dots) + ]) + + self.add(interval) + self.play( + ShowCreation(arrow), + ShowCreation(dots), + run_time = 2.0 + ) + self.dither() + +class OneAndInfiniteSumAreTheSameThing(Scene): + def construct(self): + one, equals, inf_sum = tex_mobject([ + "1", "=", "\\sum_{n=1}^\\infty \\frac{1}{2^n}" + ]).split() + point = Point(equals.get_center()).highlight("black") + + self.add(one.shift(LEFT)) + self.dither() + self.add(inf_sum.shift(RIGHT)) + self.dither() + self.play( + ApplyMethod(one.shift, RIGHT), + ApplyMethod(inf_sum.shift, LEFT), + CounterclockwiseTransform(point, equals) + ) + self.dither() + + +class HowDoYouDefineInfiniteSums(Scene): + def construct(self): + you = draw_you().center().rewire_part_attributes() + text = text_mobject( + ["How", " do", " you,\\\\", "\\emph{define}"], + size = "\\Huge" + ).shift(UP).split() + text[-1].shift(3*DOWN).highlight("skyblue") + sum_mob = tex_mobject("\\sum_{n=0}^\\infty{a_n}") + text[-1].shift(LEFT) + sum_mob.shift(text[-1].get_center()+2*RIGHT) + + self.add(you) + self.dither() + for mob in text[:-1]: + self.add(mob) + self.dither(0.1) + self.play(BlinkPiCreature(you)) + self.dither() + self.add(text[-1]) + self.dither() + self.add(sum_mob) + self.dither() + + +class LessAboutNewThoughts(Scene): + def construct(self): + words = generating, new, thoughts, to, definitions = text_mobject([ + "Generating", " new", " thoughts", "$\\rightarrow$", + "useful definitions" + ], size = "\\large").split() + gen_cross = tex_mobject("\\hline").highlight("red") + new_cross = deepcopy(gen_cross) + for cross, mob in [(gen_cross, generating), (new_cross, new)]: + cross.replace(mob) + cross.stretch_to_fit_height(0.03) + disecting = text_mobject("Disecting").highlight("green") + disecting.shift(generating.get_center() + 0.6*UP) + old = text_mobject("old").highlight("green") + old.shift(new.get_center()+0.6*UP) + + kwargs = {"run_time" : 0.25} + self.add(*words) + self.dither() + self.play(ShowCreation(gen_cross, **kwargs)) + self.play(ShowCreation(new_cross, **kwargs)) + self.dither() + self.add(disecting) + self.dither(0.5) + self.add(old) + self.dither() + +class ListOfPartialSums(Scene): + def construct(self): + all_terms = np.array(tex_mobject( + ALT_PARTIAL_SUM_TEXT, + size = "\\large" + ).split()) + numbers, equals, sums = [ + all_terms[range(k, 12, 3)] + for k in 0, 1, 2 + ] + dots = all_terms[12] + one = all_terms[-3] + last_equal = all_terms[-2] + infinite_sum = all_terms[-1] + + self.count( + numbers, + mode = "show", + display_numbers = False, + run_time = 1.0 + ) + self.play(ShowCreation(dots)) + self.dither() + self.play( + FadeIn(CompoundMobject(*equals)), + *[ + Transform(deepcopy(number), finite_sum) + for number, finite_sum in zip(numbers, sums) + ] + ) + self.dither() + self.play(*[ + ApplyMethod(s.highlight, "yellow", alpha_func = there_and_back) + for s in sums + ]) + self.dither() + self.add(one.highlight("green")) + self.dither() + + +class ShowDecreasingDistance(Scene): + args_list = [(1,), (2,)] + @staticmethod + def args_to_string(num): + return str(num) + + def construct(self, num): + number_line = NumberLine(interval_size = 1).add_numbers() + vert_line0 = Line(0.5*UP+RIGHT, UP+RIGHT) + vert_line1 = Line(0.5*UP+2*num*RIGHT, UP+2*num*RIGHT) + horiz_line = Line(vert_line0.end, vert_line1.end) + lines = [vert_line0, vert_line1, horiz_line] + for line in lines: + line.highlight("green") + dots = CompoundMobject(*[ + Dot().scale(1.0/(n+1)).shift((1+partial_sum(n))*RIGHT) + for n in range(10) + ]) + + self.add(dots.split()[0]) + self.add(number_line, *lines) + self.dither() + self.play( + ApplyMethod(vert_line0.shift, RIGHT), + Transform( + horiz_line, + Line(vert_line0.end+RIGHT, vert_line1.end).highlight("green") + ), + ShowCreation(dots), + run_time = 2.5 + ) + self.dither() + +class CircleZoomInOnOne(Scene): + def construct(self): + number_line = NumberLine(interval_size = 1).add_numbers() + dots = CompoundMobject(*[ + Dot().scale(1.0/(n+1)).shift((1+partial_sum(n))*RIGHT) + for n in range(10) + ]) + circle = Circle().shift(2*RIGHT) + text = text_mobject( + "All but finitely many dots fall inside even the tiniest circle." + ) + numbers = map( + lambda s : tex_mobject("\\frac{1}{%s}"%s), + ["100", "1,000,000", "g_{g_{64}}"] + ) + for num in numbers + [text]: + num.shift(2*UP) + num.sort_points(lambda p : np.dot(p, DOWN+RIGHT)) + curr_num = numbers[0] + arrow = Arrow(2*RIGHT, direction = 1.5*(DOWN+RIGHT)).nudge() + + self.add(number_line, dots) + self.play( + Transform(circle, Point(2*RIGHT).highlight("white")), + run_time = 5.0 + ) + + self.play(*[ + DelayByOrder(FadeIn(mob)) + for mob in arrow, curr_num + ]) + self.dither() + for num in numbers[1:] + [text]: + curr_num.points = np.array(list(reversed(curr_num.points))) + self.play( + ShowCreation( + curr_num, + alpha_func = lambda t : smooth(1-t) + ), + ShowCreation(num) + ) + self.remove(curr_num) + curr_num = num + self.dither() + +class ZoomInOnOne(Scene): + def construct(self): + num_iterations = 8 + number_line = NumberLine(interval_size = 1, radius = SPACE_WIDTH+2) + number_line.filter_out(lambda (x, y, z):abs(y)>0.1) + nl_with_nums = deepcopy(number_line).add_numbers() + self.play(ApplyMethod(nl_with_nums.shift, 2*LEFT)) + zero, one, two = [ + tex_mobject(str(n)).scale(0.5).shift(0.4*DOWN+2*(-1+n)*RIGHT) + for n in 0, 1, 2 + ] + self.play( + FadeOut(nl_with_nums), + *[Animation(mob) for mob in zero, one, two, number_line] + ) + self.remove(nl_with_nums, number_line, zero, two) + powers_of_10 = [10**(-n) for n in range(num_iterations+1)] + number_pairs = [(1-epsilon, 1+epsilon) for epsilon in powers_of_10] + for count in range(num_iterations): + self.zoom_with_numbers(number_pairs[count], number_pairs[count+1]) + self.clear() + self.add(one) + + def zoom_with_numbers(self, numbers, next_numbers): + all_numbers = map( + lambda (n, u): tex_mobject(str(n)).scale(0.5).shift(0.4*DOWN+2*u*RIGHT), + zip(numbers+next_numbers, it.cycle([-1, 1])) + ) + + num_levels = 3 + scale_val = 10 + number_lines = [ + NumberLine( + interval_size = 1, + density = scale_val*DEFAULT_POINT_DENSITY_1D + ).filter_out( + lambda (x, y, z):abs(y)>0.1 + ).scale(1.0/scale_val**x) + for x in range(num_levels) + ] + kwargs = {"alpha_func" : None} + self.play(*[ + ApplyMethod(number_lines[x].scale, scale_val, **kwargs) + for x in range(1, num_levels) + ]+[ + ApplyMethod(number_lines[0].stretch, scale_val, 0, **kwargs), + ]+[ + ApplyMethod( + all_numbers[i].shift, + 2*LEFT*(scale_val-1)*(-1)**i, + **kwargs + ) + for i in 0, 1 + ]+[ + Transform(Point(0.4*DOWN + u*0.2*RIGHT), num, **kwargs) + for u, num in zip([-1, 1], all_numbers[2:]) + ]) + self.remove(*all_numbers) + + +class DefineInfiniteSum(Scene): + def construct(self): + self.put_expression_in_corner() + self.list_partial_sums() + self.dither() + + def put_expression_in_corner(self): + buff = 0.24 + define, infinite_sum = tex_mobject([ + "\\text{\\emph{Define} }", + "\\sum_{n = 0}^\\infty a_n = X" + ]).split() + define.highlight("skyblue") + expression = CompoundMobject(define, infinite_sum) + + self.add(expression) + self.dither() + self.play(ApplyFunction( + lambda mob : mob.scale(0.5).to_corner(UP+LEFT, buff = buff), + expression + )) + bottom = (min(expression.points[:,1]) - buff)*UP + side = (max(expression.points[:,0]) + buff)*RIGHT + lines = [ + Line(SPACE_WIDTH*LEFT+bottom, side+bottom), + Line(SPACE_HEIGHT*UP+side, side+bottom) + ] + self.play(*[ + ShowCreation(line.highlight("white")) + for line in lines + ]) + self.dither() + + def list_partial_sums(self): + num_terms = 10 + term_strings = reduce(op.add, [ + [ + "s_%d"%n, + "&=", + "+".join(["a_%d"%k for k in range(n+1)])+"\\\\" + ] + for n in range(num_terms) + ]) + terms = tex_mobject(term_strings, size = "\\large").split() + number_line = NumberLine() + ex_point = 2*RIGHT + ex = tex_mobject("X").shift(ex_point + LEFT + UP) + arrow = Arrow(ex_point, tail = ex.points[-1]).nudge() + + for term, count in zip(terms, it.count()): + self.add(term) + self.dither(0.1) + if count % 3 == 2: + self.dither(0.5) + self.dither() + esses = np.array(terms)[range(0, len(terms), 3)] + other_terms = filter(lambda m : m not in esses, terms) + self.play(*[ + ApplyMethod(ess.highlight, "yellow") + for ess in esses + ]) + + def move_s(s, n): + s.center() + s.scale(1.0/(n+1)) + s.shift(ex_point-RIGHT*2.0/2**n) + return s + self.play(*[ + FadeOut(term) + for term in other_terms + ]+[ + ApplyFunction(lambda s : move_s(s, n), ess) + for ess, n in zip(esses, it.count()) + ]+[ + FadeIn(number_line), + FadeIn(ex), + FadeIn(arrow) + ]) + + lines = [ + Line(x+0.25*DOWN, x+0.25*UP).highlight("white") + for y in [-1, -0.01, 1, 0.01] + for x in [ex_point+y*RIGHT] + ] + self.play(*[ + Transform(lines[x], lines[x+1], run_time = 3.0) + for x in 0, 2 + ]) + + +class YouJustInventedSomeMath(Scene): + def construct(self): + text = text_mobject([ + "You ", "just ", "invented\\\\", "some ", "math" + ]).split() + for mob in text[:3]: + mob.shift(UP) + for mob in text[3:]: + mob.shift(1.3*DOWN) + # you = draw_you().center().rewire_part_attributes() + # smile = PiCreature().mouth.center().shift(you.mouth.get_center()) + you = PiCreature().highlight("grey") + you.center().rewire_part_attributes() + + self.add(you) + for mob in text: + self.add(mob) + self.dither(0.2) + self.play(WaveArm(you)) + self.play(BlinkPiCreature(you)) + + + +class SeekMoreGeneralTruths(Scene): + def construct(self): + summands = [ + "\\frac{1}{3^n}", + "2^n", + "\\frac{1}{n^2}", + "\\frac{(-1)^n}{n}", + "\\frac{(-1)^n}{(2n)!}", + "\\frac{2\sqrt{2}}{99^2}\\frac{(4n)!}{(n!)^4} \\cdot \\frac{26390n + 1103}{396^{4k}}", + ] + sums = tex_mobject([ + "&\\sum_{n = 0}^\\infty" + summand + "= ? \\\\" + for summand in summands + ], size = "") + sums.stretch_to_fit_height(2*SPACE_HEIGHT-1) + sums.shift((SPACE_HEIGHT-0.5-max(sums.points[:,1]))*UP) + + for qsum in sums.split(): + qsum.sort_points(lambda p : np.dot(p, DOWN+RIGHT)) + self.play(DelayByOrder(FadeIn(qsum))) + self.dither(0.5) + self.dither() + +class ChopIntervalInProportions(Scene): + args_list = [("9", ), ("p", )] + @staticmethod + def args_to_string(*args): + return args[0] + + def construct(self, mode): + if mode == "9": + prop = 0.1 + num_terms = 2 + left_terms, right_terms = [ + [ + tex_mobject("\\frac{%d}{%d}"%(k, (10**(count+1)))) + for count in range(num_terms) + ] + for k in 9, 1 + ] + if mode == "p": + num_terms = 4 + prop = 0.7 + left_terms = map(tex_mobject, ["(1-p)", ["p","(1-p)"]]+[ + ["p^%d"%(count), "(1-p)"] + for count in range(2, num_terms) + ]) + right_terms = map(tex_mobject, ["p"] + [ + ["p", "^%d"%(count+1)] + for count in range(1, num_terms) + ]) + interval = zero_to_one_interval() + left = INTERVAL_RADIUS*LEFT + right = INTERVAL_RADIUS*RIGHT + left_paren = tex_mobject("(") + right_paren = tex_mobject(")").shift(right + 1.1*UP) + curr = left.astype("float") + brace_to_replace = None + term_to_replace = None + + self.add(interval) + additional_anims = [] + for lt, rt, count in zip(left_terms, right_terms, it.count()): + last = deepcopy(curr) + curr += 2*RIGHT*INTERVAL_RADIUS*(1-prop)*(prop**count) + braces = [ + underbrace(a, b).rotate(np.pi, RIGHT) + for a, b in [(last, curr), (curr, right)] + ] + for term, brace, count2 in zip([lt, rt], braces, it.count()): + term.scale(0.75) + term.shift(brace.get_center()+UP) + if term.get_width() > brace.get_width(): + term.shift(UP+1.5*(count-2)*RIGHT) + arrow = Arrow( + brace.get_center()+0.3*UP, + tail = term.get_center()+0.5*DOWN + ) + arrow.points = np.array(list(reversed(arrow.points))) + additional_anims = [ShowCreation(arrow)] + if brace_to_replace is not None: + if mode == "p": + lt, rt = lt.split(), rt.split() + if count == 1: + new_term_to_replace = deepcopy(term_to_replace) + new_term_to_replace.center().shift(last+UP+0.3*LEFT) + left_paren.center().shift(last+1.1*UP) + self.play( + FadeIn(lt[1]), + FadeIn(rt[0]), + Transform( + brace_to_replace.repeat(2), + CompoundMobject(*braces) + ), + FadeIn(left_paren), + FadeIn(right_paren), + Transform(term_to_replace, new_term_to_replace), + *additional_anims + ) + self.dither() + self.play( + Transform( + term_to_replace, + CompoundMobject(lt[0], rt[1]) + ), + FadeOut(left_paren), + FadeOut(right_paren) + ) + self.remove(left_paren, right_paren) + else: + self.play( + FadeIn(lt[1]), + FadeIn(rt[0]), + Transform( + brace_to_replace.repeat(2), + CompoundMobject(*braces) + ), + Transform( + term_to_replace, + CompoundMobject(lt[0], rt[1]) + ), + *additional_anims + ) + self.remove(*lt+rt) + lt, rt = CompoundMobject(*lt), CompoundMobject(*rt) + self.add(lt, rt) + else: + self.play( + Transform( + brace_to_replace.repeat(2), + CompoundMobject(*braces) + ), + Transform( + term_to_replace, + CompoundMobject(lt, rt) + ), + *additional_anims + ) + self.remove(brace_to_replace, term_to_replace) + self.add(lt, rt, *braces) + else: + self.play(*[ + FadeIn(mob) + for mob in braces + [lt, rt] + ] + additional_anims) + self.dither() + brace_to_replace = braces[1] + term_to_replace = rt + if mode == "9": + split_100 = tex_mobject("\\frac{9}{1000}+\\frac{1}{1000}") + split_100.scale(0.5) + split_100.shift(right_terms[-1].get_center()) + split_100.to_edge(RIGHT) + split_100.sort_points() + right_terms[-1].sort_points() + self.play(Transform( + right_terms[-1], split_100 + )) + self.dither() + + + +class GeometricSum(RearrangeEquation): + def construct(self): + start_terms = "(1-p) + p (1-p) + p^2 (1-p) + p^3 (1-p) + \\cdots = 1" + end_terms = "1 + p + p^2 + p^3 + \\cdots = \\frac{1}{(1-p)}" + index_map = { + 0 : 0, + # 0 : -1, #(1-p) + 1 : 1, #+ + 2 : 2, #p + # 3 : -1, #(1-p) + 4 : 3, #+ + 5 : 4, #p^2 + # 6 : -1, #(1-p) + 7 : 5, #+ + 8 : 6, #p^3 + # 9 : -1, #(1-p) + 10: 7, #+ + 11: 8, #\\cdots + 12: 9, #= + 13: 10, #1 + } + def start_transform(mob): + return mob.scale(1.3).shift(2*UP) + def end_transform(mob): + return mob.scale(1.3).shift(DOWN) + + RearrangeEquation.construct( + self, + start_terms.split(" "), end_terms.split(" "), + index_map, size = "\\large", + path = counterclockwise_path, + start_transform = start_transform, + end_transform = end_transform, + leave_start_terms = True, + transform_kwargs = {"run_time" : 2.0} + ) + +class PointNineRepeating(RearrangeEquation): + def construct(self): + start_terms = [ + "\\frac{9}{10}", + "+", + "\\frac{9}{100}", + "+", + "\\frac{9}{1000}", + "+", + "\\cdots=1", + ] + end_terms = "0 . 9 9 9 \\cdots=1".split(" ") + index_map = { + 0 : 2, + 2 : 3, + 4 : 4, + 6 : 5, + } + for term in tex_mobject(start_terms).split(): + self.add(term) + self.dither(0.5) + self.clear() + RearrangeEquation.construct( + self, + start_terms, + end_terms, + index_map, + path = straight_path + ) + + +class PlugNumbersIntoRightside(Scene): + def construct(self): + scale_factor = 1.5 + lhs, rhs = tex_mobject( + ["1 + p + p^2 + p^3 + \\cdots = ", "\\frac{1}{1-p}"], + size = "\\large" + ).scale(scale_factor).split() + rhs = tex_mobject( + ["1 \\over 1 - ", "p"], + size = "\\large" + ).replace(rhs).split() + num_strings = [ + "0.5", "3", "\pi", "(-1)", "3.7", "2", + "0.2", "27", "i" + ] + nums = [ + tex_mobject(num_string, size="\\large") + for num_string in num_strings + ] + for num, num_string in zip(nums, num_strings): + num.scale(scale_factor) + num.shift(rhs[1].get_center()) + num.shift(0.1*RIGHT + 0.08*UP) + num.highlight("green") + if num_string == "(-1)": + num.shift(0.3*RIGHT) + right_words = text_mobject( + "This side makes sense for almost any value of $p$," + ).shift(2*UP) + left_words = text_mobject( + "even if it seems like this side will not." + ).shift(2*DOWN) + right_words.add(Arrow( + rhs[0].get_center(), + tail = right_words.get_center()+DOWN+RIGHT + ).nudge(0.5)) + left_words.add(Arrow( + lhs.get_center() + 0.3*DOWN, + tail = left_words.get_center() + 0.3*UP + )) + right_words.highlight("green") + left_words.highlight("yellow") + + + self.add(lhs, *rhs) + self.dither() + self.play(FadeIn(right_words)) + curr = rhs[1] + for num, count in zip(nums, it.count()): + self.play(CounterclockwiseTransform(curr, num)) + self.dither() + if count == 2: + self.play(FadeIn(left_words)) + + +class PlugInNegativeOne(RearrangeEquation): + def construct(self): + num_written_terms = 4 + start_terms = reduce(op.add, [ + ["(-", "1", ")^%d"%n, "+"] + for n in range(num_written_terms) + ]) + ["\\cdots=", "\\frac{1}{1-(-1)}"] + end_terms = "1 - 1 + 1 - 1 + \\cdots= \\frac{1}{2}".split(" ") + index_map = dict([ + (4*n + 1, 2*n) + for n in range(num_written_terms) + ]+[ + (4*n + 3, 2*n + 1) + for n in range(num_written_terms) + ]) + index_map[-2] = -2 + index_map[-1] = -1 + RearrangeEquation.construct( + self, + start_terms, + end_terms, + index_map, + path = straight_path, + start_transform = lambda m : m.shift(2*UP), + leave_start_terms = True, + ) + +class PlugInTwo(RearrangeEquation): + def construct(self): + num_written_terms = 4 + start_terms = reduce(op.add, [ + ["2", "^%d"%n, "+"] + for n in range(num_written_terms) + ]) + ["\\cdots=", "\\frac{1}{1-2}"] + end_terms = "1 + 2 + 4 + 8 + \\cdots= -1".split(" ") + index_map = dict([ + (3*n, 2*n) + for n in range(num_written_terms) + ]+[ + (3*n + 2, 2*n + 1) + for n in range(num_written_terms) + ]) + index_map[-2] = -2 + index_map[-1] = -1 + RearrangeEquation.construct( + self, + start_terms, + end_terms, + index_map, + size = "\\Huge", + path = straight_path, + start_transform = lambda m : m.shift(2*UP), + leave_start_terms = True, + ) + +class ListPartialDivergentSums(Scene): + args_list = [ + ( + lambda n : "1" if n%2 == 0 else "(-1)", + lambda n : "1" if n%2 == 0 else "0" + ), + ( + lambda n : "2^%d"%n if n > 1 else ("1" if n==0 else "2"), + lambda n : str(2**(n+1)-1) + ) + ] + @staticmethod + def args_to_string(*args): + if args[0](1) == "(-1)": + return "Negative1" + else: + return args[0](1) + def construct(self, term_func, partial_sum_func): + num_lines = 8 + rhss = [ + partial_sum_func(n) + for n in range(num_lines) + ] + lhss = [ + "&=" + \ + "+".join([term_func(k) for k in range(n+1)]) + \ + "\\\\" + for n in range(num_lines) + ] + terms = tex_mobject( + list(it.chain.from_iterable(zip(rhss, lhss))) + ["\\vdots&", ""], + size = "\\large" + ).shift(RIGHT).split() + words = text_mobject("These numbers don't \\\\ approach anything") + words.to_edge(LEFT) + arrow = Arrow(3*DOWN+2*LEFT, direction = DOWN, length = 6) + + for x in range(0, len(terms), 2): + self.play(FadeIn(terms[x]), FadeIn(terms[x+1])) + self.play(FadeIn(words), ShowCreation(arrow)) + for x in range(0, len(terms), 2): + self.play( + ApplyMethod(terms[x].highlight, "green"), + run_time = 0.1 + ) + self.dither() + +class NotARobot(Scene): + def construct(self): + you = draw_you().center() + top_words = text_mobject("You are a mathematician,") + low_words = text_mobject("not a robot.") + top_words.shift(1.5*UP) + low_words.shift(1.5*DOWN) + + self.add(you) + self.play(ShimmerIn(top_words)) + self.play(ShimmerIn(low_words)) + + +class SumPowersOfTwoAnimation(Scene): + def construct(self): + iterations = 5 + dot = Dot(density = 3*DEFAULT_POINT_DENSITY_1D).scale(1.5) + dot_width = dot.get_width()*RIGHT + dot_buff = 0.2*RIGHT + left = (SPACE_WIDTH-1)*LEFT + right = left + 2*dot_width + dot_buff + top_brace_left = left+dot_width+dot_buff+0.3*DOWN + bottom_brace_left = left + 0.3*DOWN + circle = Circle().scale(dot_width[0]/2).shift(left+dot_width/2) + curr_dots = deepcopy(dot).shift(left+1.5*dot_width+dot_buff) + topbrace = underbrace(top_brace_left, right).rotate(np.pi, RIGHT) + bottombrace = underbrace(bottom_brace_left, right) + colors = Color("yellow").range_to("purple", iterations) + curr_dots.highlight(colors.next()) + equation = tex_mobject( + "1+2+4+\\cdots+2^n=2^{n+1} - 1", + size = "\\Huge" + ).shift(3*UP) + full_top_sum = tex_mobject(["1", "+2", "+4", "+8", "+16"]).split() + + self.add(equation) + self.dither() + self.add(circle, curr_dots, topbrace, bottombrace) + for n in range(1,iterations): + bottom_num = tex_mobject(str(2**n)) + new_bottom_num = tex_mobject(str(2**(n+1))) + bottom_num.shift(bottombrace.get_center()+0.5*DOWN) + + top_sum = CompoundMobject(*full_top_sum[:n]).center() + top_sum_end = deepcopy(full_top_sum[n]).center() + top_sum.shift(topbrace.get_center()+0.5*UP) + new_top_sum = CompoundMobject(*full_top_sum[:(n+1)]).center() + self.add(top_sum, bottom_num) + + if n == iterations: + continue + new_dot = deepcopy(dot).shift(circle.get_center()) + shift_val = (2**n)*(dot_width+dot_buff) + right += shift_val + new_dots = CompoundMobject(new_dot, curr_dots) + new_dots.highlight(colors.next()).shift(shift_val) + alt_bottombrace = deepcopy(bottombrace).shift(shift_val) + alt_bottom_num = deepcopy(bottom_num).shift(shift_val) + alt_topbrace = deepcopy(alt_bottombrace).rotate(np.pi, RIGHT) + top_sum_end.shift(alt_topbrace.get_center()+0.5*UP) + new_topbrace = underbrace(top_brace_left, right).rotate(np.pi, RIGHT) + new_bottombrace = underbrace(bottom_brace_left, right) + new_bottom_num.shift(new_bottombrace.get_center()+0.5*DOWN) + new_top_sum.shift(new_topbrace.get_center()+0.5*UP) + for exp, brace in [ + (top_sum, topbrace), + (top_sum_end, alt_topbrace), + (new_top_sum, new_topbrace), + ]: + if exp.get_width() > brace.get_width(): + exp.stretch_to_fit_width(brace.get_width()) + new_top_sum = new_top_sum.split() + new_top_sum_start = CompoundMobject(*new_top_sum[:-1]) + new_top_sum_end = new_top_sum[-1] + + self.dither() + self.play(*[ + FadeIn(mob) + for mob in [ + new_dots, + alt_topbrace, + alt_bottombrace, + top_sum_end, + alt_bottom_num, + ] + ]) + self.dither() + self.play( + Transform(topbrace, new_topbrace), + Transform(alt_topbrace, new_topbrace), + Transform(bottombrace, new_bottombrace), + Transform(alt_bottombrace, new_bottombrace), + Transform(bottom_num, new_bottom_num), + Transform(alt_bottom_num, new_bottom_num), + Transform(top_sum, new_top_sum_start), + Transform(top_sum_end, new_top_sum_end) + ) + self.remove( + bottom_num, alt_bottom_num, top_sum, + top_sum_end, new_top_sum_end, + alt_topbrace, alt_bottombrace + ) + curr_dots = CompoundMobject(curr_dots, new_dots) + + +class PretendTheyDoApproachNegativeOne(RearrangeEquation): + def construct(self): + num_lines = 6 + da = "\\downarrow" + columns = [ + tex_mobject("\\\\".join([ + n_func(n) + for n in range(num_lines) + ]+last_bits), size = "\\Large").to_corner(UP+LEFT) + for n_func, last_bits in [ + (lambda n : str(2**(n+1)-1), ["\\vdots", da, "-1"]), + (lambda n : "+1", ["", "", "+1"]), + (lambda n : "=", ["", "", "="]), + (lambda n : str(2**(n+1)), ["\\vdots", da, "0"]), + ] + ] + columns[-1].highlight() + columns[2].shift(0.2*DOWN) + shift_val = 3*RIGHT + for column in columns: + column.shift(shift_val) + shift_val = shift_val + (column.get_width()+0.2)*RIGHT + self.play(ShimmerIn(columns[0])) + self.dither() + self.add(columns[1]) + self.dither() + self.play( + DelayByOrder(Transform(deepcopy(columns[0]), columns[-1])), + FadeIn(columns[2]) + ) + self.dither() + +class DistanceBetweenRationalNumbers(Scene): + def construct(self): + locii = [2*LEFT, 2*RIGHT] + nums = [ + tex_mobject(s).shift(1.3*d) + for s, d in zip(["\\frac{1}{2}", "3"], locii) + ] + arrows = [ + Arrow(direction, tail = ORIGIN) + for direction in locii + ] + dist = tex_mobject("\\frac{5}{2}").scale(0.5).shift(0.5*UP) + text = text_mobject("How we define distance between rational numbers") + text.to_edge(UP) + self.add(text, *nums) + self.play(*[ShowCreation(arrow) for arrow in arrows]) + self.play(ShimmerIn(dist)) + self.dither() + +class NotTheOnlyWayToOrganize(Scene): + def construct(self): + self.play(ShowCreation(NumberLine().add_numbers())) + self.dither() + words = "Is there any other reasonable way to organize numbers?" + self.play(FadeIn(text_mobject(words).shift(2*UP))) + self.dither() + +class DistanceIsAFunction(Scene): + args_list = [ + ("Euclidian",), + ("Random",), + ("2adic",), + ] + @staticmethod + def args_to_string(word): + return word + + def construct(self, mode): + if mode == "Euclidian": + dist_text = "dist" + elif mode == "Random": + dist_text = "random\\_dist" + elif mode == "2adic": + dist_text = "2\\_adic\\_dist" + dist, r_paren, arg0, comma, arg1, l_paren, equals, result = text_mobject([ + dist_text, "(", "000", ",", "000", ")", "=", "000" + ]).split() + point_origin = comma.get_center()+0.2*UP + if mode == "Random": + examples = [ + ("2", "3", "7"), + ("\\frac{1}{2}", "100", "\\frac{4}{5}"), + ] + dist.highlight("orange") + self.add(dist) + self.dither() + elif mode == "Euclidian": + examples = [ + ("1", "5", "4"), + ("\\frac{1}{2}", "3", "\\frac{5}{2}"), + ("-3", "3", "6"), + ("2", "3", "1"), + ("0", "4", "x"), + ("1", "5", "x"), + ("2", "6", "x"), + ] + elif mode == "2adic": + examples = [ + ("2", "0", "\\frac{1}{2}"), + ("-1", "15", "\\frac{1}{16}"), + ("3", "7", "\\frac{1}{4}"), + ("\\frac{3}{2}", "1", "2"), + ] + dist.highlight("green") + self.add(dist) + self.dither() + example_mobs = [ + ( + tex_mobject(tup[0]).shift(arg0.get_center()), + tex_mobject(tup[1]).shift(arg1.get_center()), + tex_mobject(tup[2]).shift(result.get_center()) + ) + for tup in examples + ] + + self.add(dist, r_paren, comma, l_paren, equals) + previous = None + kwargs = {"run_time" : 0.5} + for mobs in example_mobs: + if previous: + self.play(*[ + DelayByOrder(Transform(prev, mob, **kwargs)) + for prev, mob in zip(previous, mobs)[:-1] + ]) + self.play(DelayByOrder(Transform( + previous[-1], mobs[-1], **kwargs + ))) + self.remove(*previous) + self.add(*mobs) + previous = mobs + self.dither() + +class ShiftInvarianceNumberLine(Scene): + def construct(self): + number_line = NumberLine().add_numbers() + topbrace = underbrace(ORIGIN, 2*RIGHT).rotate(np.pi, RIGHT) + dist0 = text_mobject(["dist(", "$0$", ",", "$2$",")"]) + dist1 = text_mobject(["dist(", "$2$", ",", "$4$",")"]) + for dist in dist0, dist1: + dist.shift(topbrace.get_center()+0.3*UP) + dist1.shift(2*RIGHT) + footnote = text_mobject(""" + \\begin{flushleft} + *yeah yeah, I know I'm still drawing them on a line, + but until a few minutes from now I have no other way + to draw them + \\end{flushright} + """).scale(0.5).to_corner(DOWN+RIGHT) + + self.add(number_line, topbrace, dist0, footnote) + self.dither() + self.remove(dist0) + self.play( + ApplyMethod(topbrace.shift, 2*RIGHT), + *[ + Transform(*pair) + for pair in zip(dist0.split(), dist1.split()) + ] + ) + self.dither() + +class NameShiftInvarianceProperty(Scene): + def construct(self): + prop = text_mobject([ + "dist($A$, $B$) = dist(", + "$A+x$, $B+x$", + ") \\quad for all $x$" + ]) + mid_part = prop.split()[1] + u_brace = underbrace( + mid_part.get_boundary_point(DOWN+LEFT), + mid_part.get_boundary_point(DOWN+RIGHT) + ).shift(0.3*DOWN) + label = text_mobject("Shifted values") + label.shift(u_brace.get_center()+0.5*DOWN) + name = text_mobject("``Shift Invariance''") + name.highlight("green").to_edge(UP) + for mob in u_brace, label: + mob.highlight("yellow") + + self.add(prop) + self.play(ShimmerIn(label), ShimmerIn(u_brace)) + self.dither() + self.play(ShimmerIn(name)) + self.dither() + + +class TriangleInequality(Scene): + def construct(self): + symbols = ["A", "B", "C"] + locations = [2*(DOWN+LEFT), UP, 4*RIGHT] + ab, plus, bc, greater_than, ac = text_mobject([ + "dist($A$, $B$)", + "$+$", + "dist($B$, $C$)", + "$>$", + "dist($A$, $C$)", + ]).to_edge(UP).split() + all_dists = [ab, ac, bc] + ab_line, ac_line, bc_line = all_lines = [ + Line(*pair).scale_in_place(0.8) + for pair in it.combinations(locations, 2) + ] + def put_on_line(mob, line): + result = deepcopy(mob).center().scale(0.5) + result.rotate(np.arctan(line.get_slope())) + result.shift(line.get_center()+0.2*UP) + return result + ab_copy, ac_copy, bc_copy = all_copies = [ + put_on_line(dist, line) + for dist, line in zip(all_dists, all_lines) + ] + for symbol, loc in zip(symbols, locations): + self.add(tex_mobject(symbol).shift(loc)) + self.play(ShowCreation(ac_line), FadeIn(ac_copy)) + self.dither() + self.play(*[ + ShowCreation(line) for line in ab_line, bc_line + ]+[ + FadeIn(dist) for dist in ab_copy, bc_copy + ]) + self.dither() + self.play(*[ + Transform(*pair) + for pair in zip(all_copies, all_dists) + ]+[ + FadeIn(mob) + for mob in plus, greater_than + ]) + self.dither() + + + +class StruggleToFindFrameOfMind(Scene): + def construct(self): + you, bubble = draw_you(with_bubble = True) + questions = text_mobject("???", size = "\\Huge").scale(1.5) + contents = [ + tex_mobject("2, 4, 8, 16, 32, \\dots \\rightarrow 0"), + text_mobject("dist(0, 2) $<$ dist(0, 64)"), + NumberLine().sort_points(lambda p : -p[1]).add( + text_mobject("Not on a line?").shift(UP) + ), + ] + kwargs = {"run_time" : 0.5} + self.add(you, bubble) + bubble.add_content(questions) + for mob in contents: + curr = bubble.content + self.remove(curr) + bubble.add_content(mob) + for first, second in [(curr, questions), (questions, mob)]: + copy = deepcopy(first) + self.play(DelayByOrder(Transform( + copy, second, **kwargs + ))) + self.remove(copy) + self.add(mob) + self.dither() + + +class RoomsAndSubrooms(Scene): + def construct(self): + colors = get_room_colors() + a_set = [3*RIGHT, 3*LEFT] + b_set = [1.5*UP, 1.5*DOWN] + c_set = [LEFT, RIGHT] + rectangle_groups = [ + [Rectangle(7, 12).highlight(colors[0])], + [ + Rectangle(6, 5).shift(a).highlight(colors[1]) + for a in a_set + ], + [ + Rectangle(2, 4).shift(a + b).highlight(colors[2]) + for a in a_set + for b in b_set + ], + [ + Rectangle(1, 1).shift(a+b+c).highlight(colors[3]) + for a in a_set + for b in b_set + for c in c_set + ] + ] + + for group in rectangle_groups: + mob = CompoundMobject(*group) + mob.sort_points(np.linalg.norm) + self.play(ShowCreation(mob)) + + self.dither() + + +class RoomsAndSubroomsWithNumbers(Scene): + def construct(self): + zero_local = (SPACE_WIDTH-0.5)*LEFT + zero_one_width = SPACE_WIDTH-0.3 + + zero, power_mobs = self.draw_numbers(zero_local, zero_one_width) + self.dither() + rectangles = self.draw_first_rectangles(zero_one_width) + rect_clusters = self.draw_remaining_rectangles(rectangles) + self.adjust_power_mobs(zero, power_mobs, rect_clusters[-1]) + self.dither() + num_mobs = self.draw_remaining_numbers( + zero, power_mobs, rect_clusters + ) + self.dither() + self.add_negative_one(num_mobs) + self.dither() + self.show_distances(num_mobs, rect_clusters) + + + def draw_numbers(self, zero_local, zero_one_width): + num_numbers = 5 + zero = tex_mobject("0").shift(zero_local) + self.add(zero) + nums = [] + for n in range(num_numbers): + num = tex_mobject(str(2**n)) + num.scale(1.0/(n+1)) + num.shift( + zero_local+\ + RIGHT*zero_one_width/(2.0**n)+\ + LEFT*0.05*n+\ + (0.4*RIGHT if n == 0 else ORIGIN) #Stupid + ) + self.play(FadeIn(num, run_time = 0.5)) + nums.append(num) + return zero, nums + + def draw_first_rectangles(self, zero_one_width): + side_buff = 0.05 + upper_buff = 0.5 + colors = get_room_colors() + rectangles = [] + for n in range(4): + rect = Rectangle( + 2*SPACE_HEIGHT-(n+2)*upper_buff, + zero_one_width/(2**n)-0.85*(n+1)*side_buff + ) + rect.sort_points(np.linalg.norm) + rect.to_edge(LEFT, buff = 0.2).shift(n*side_buff*RIGHT) + rect.highlight(colors[n]) + rectangles.append(rect) + for rect in rectangles: + self.play(ShowCreation(rect)) + self.dither() + return rectangles + + def draw_remaining_rectangles(self, rectangles): + clusters = [] + centers = [ORIGIN] + map(Mobject.get_center, rectangles) + shift_vals = [ + 2*(c2 - c1)[0]*RIGHT + for c1, c2 in zip(centers[1:], centers) + ] + for rectangle, count in zip(rectangles, it.count(1)): + cluster = [rectangle] + for shift_val in shift_vals[:count]: + cluster += map( + lambda mob : mob.shift(shift_val), + deepcopy(cluster) + ) + clusters.append(cluster) + for rect in cluster[1:]: + self.play(FadeIn(rect, run_time = 0.6**(count-1))) + return clusters + + def adjust_power_mobs(self, zero, power_mobs, small_rects): + new_zero = deepcopy(zero) + self.center_in_closest_rect(new_zero, small_rects) + new_power_mobs = deepcopy(power_mobs) + for mob, count in zip(new_power_mobs, it.count(1)): + self.center_in_closest_rect(mob, small_rects) + new_power_mobs[-1].shift(DOWN) + dots = tex_mobject("\\vdots") + dots.scale(0.5).shift(new_zero.get_center()+0.5*DOWN) + self.play( + Transform(zero, new_zero), + FadeIn(dots), + *[ + Transform(old, new) + for old, new in zip(power_mobs, new_power_mobs) + ] + ) + + def draw_remaining_numbers(self, zero, power_mobs, rect_clusters): + small_rects = rect_clusters[-1] + max_width = 0.8*small_rects[0].get_width() + max_power = 4 + num_mobs = [None]*(2**max_power + 1) + num_mobs[0] = zero + powers = [2**k for k in range(max_power+1)] + for p, index in zip(powers, it.count()): + num_mobs[p] = power_mobs[index] + for power, count in zip(powers[1:-1], it.count(1)): + zero_copy = deepcopy(zero) + power_mob_copy = deepcopy(num_mobs[power]) + def transform(mob): + self.center_in_closest_rect(mob, small_rects) + mob.shift(UP) + return mob + self.play(*[ + ApplyFunction(transform, mob) + for mob in zero_copy, power_mob_copy + ]) + last_left_mob = zero + for n in range(power+1, 2*power): + left_mob = num_mobs[n-power] + shift_val = left_mob.get_center()-last_left_mob.get_center() + self.play(*[ + ApplyMethod(mob.shift, shift_val) + for mob in zero_copy, power_mob_copy + ]) + num_mobs[n] = tex_mobject(str(n)) + num_mobs[n].scale(1.0/(power_of_divisor(n, 2)+1)) + width_ratio = max_width / num_mobs[n].get_width() + if width_ratio < 1: + num_mobs[n].scale(width_ratio) + num_mobs[n].shift(power_mob_copy.get_center()+DOWN) + self.center_in_closest_rect(num_mobs[n], small_rects) + point = Point(power_mob_copy.get_center()) + self.play(Transform(point, num_mobs[n])) + self.remove(point) + self.add(num_mobs[n]) + last_left_mob = left_mob + self.remove(zero_copy, power_mob_copy) + self.dither() + return num_mobs + + @staticmethod + def center_in_closest_rect(mobject, rectangles): + center = mobject.get_center() + diffs = [r.get_center()-center for r in rectangles] + mobject.shift(diffs[np.argmin(map(np.linalg.norm, diffs))]) + + def add_negative_one(self, num_mobs): + neg_one = tex_mobject("-1").scale(0.5) + shift_val = num_mobs[15].get_center()-neg_one.get_center() + neg_one.shift(UP) + self.play(ApplyMethod(neg_one.shift, shift_val)) + + def show_distances(self, num_mobs, rect_clusters): + self.remove(*[r for c in rect_clusters for r in c]) + text = None + for cluster, count in zip(rect_clusters, it.count()): + if text is not None: + self.remove(text) + if count == 0: + dist_string = "1" + else: + dist_string = "$\\frac{1}{%d}$"%(2**count) + text = text_mobject( + "Any of these pairs are considered to be a distance " +\ + dist_string +\ + " away from each other" + ).shift(2*UP) + self.add(text) + self.clear_way_for_text(text, cluster) + self.add(*cluster) + pairs = filter( + lambda (a, b) : (a-b)%(2**count) == 0 and (a-b)%(2**(count+1)) != 0, + it.combinations(range(16), 2) + ) + for pair in sample(pairs, min(10, len(pairs))): + for index in pair: + num_mobs[index].highlight("green") + self.play(*[ + ApplyMethod( + num_mobs[index].rotate_in_place, np.pi/10, + alpha_func = wiggle + ) + for index in pair + ]) + self.dither() + for index in pair: + num_mobs[index].highlight("white") + + @staticmethod + def clear_way_for_text(text, mobjects): + right, top, null = np.max(text.points, 0) + left, bottom, null = np.min(text.points, 0) + def filter_func((x, y, z)): + return x>left and xbottom and y 1: + self.remove(texts[n-2]) + else: + self.remove(u_brace, *texts) + self.remove(*last_args) + self.add(*new_args) + self.dither(rest_time) + last_args = new_args + + +class OtherRationalNumbers(Scene): + def construct(self): + import random + self.add(text_mobject("Where do other \\\\ rational numbers fall?")) + pairs = [ + (1, 2), + (1, 3), + (4, 9), + (-7, 13), + (3, 1001), + ] + locii = [ + 4*LEFT+2*UP, + 4*RIGHT, + 5*RIGHT+UP, + 4*LEFT+2*DOWN, + 3*DOWN, + ] + for pair, locus in zip(pairs, locii): + fraction = tex_mobject("\\frac{%d}{%d}"%pair).shift(locus) + self.play(ShimmerIn(fraction)) + self.dither() + +class PAdicMetric(Scene): + def construct(self): + p_str, text = text_mobject(["$p$", "-adic metric"]).shift(2*UP).split() + primes = [tex_mobject(str(p)) for p in [2, 3, 5, 7, 11, 13, 17, 19, 23]] + p_str.highlight("yellow") + colors = Color("green").range_to("skyblue", len(primes)) + new_numbers = text_mobject("Completely new types of numbers!") + new_numbers.highlight("skyblue").shift(2.3*DOWN) + arrow = Arrow(2*DOWN, tail = 1.7*UP) + + curr = deepcopy(p_str) + self.add(curr, text) + self.dither() + for prime, count in zip(primes, it.count()): + prime.scale(1.0).highlight(colors.next()) + prime.shift(center_of_mass([p_str.get_top(), p_str.get_center()])) + self.play(DelayByOrder(Transform(curr, prime))) + self.dither() + if count == 2: + self.spill(CompoundMobject(curr, text), arrow, new_numbers) + self.remove(curr) + curr = prime + self.play(DelayByOrder(Transform(curr, p_str))) + self.dither() + + def spill(self, start, arrow, end): + start.sort_points(lambda p : p[1]) + self.play( + ShowCreation( + arrow, + alpha_func = squish_alpha_func(smooth, 0.5, 1.0) + ), + DelayByOrder(Transform( + start, + Point(arrow.points[0]).highlight("white") + )) + ) + self.play(ShimmerIn(end)) + + +class FuzzyDiscoveryToNewMath(Scene): + def construct(self): + fuzzy = text_mobject("Fuzzy Discovery") + fuzzy.to_edge(UP).shift(SPACE_WIDTH*LEFT/2) + new_math = text_mobject("New Math") + new_math.to_edge(UP).shift(SPACE_WIDTH*RIGHT/2) + lines = CompoundMobject( + Line(DOWN*SPACE_HEIGHT, UP*SPACE_HEIGHT), + Line(3*UP+LEFT*SPACE_WIDTH, 3*UP+RIGHT*SPACE_WIDTH) + ) + fuzzy_discoveries = [ + tex_mobject("a^2 + b^2 = c^2"), + tex_mobject("".join(CONVERGENT_SUM_TEXT)), + tex_mobject("".join(DIVERGENT_SUM_TEXT)), + tex_mobject("e^{\pi i} = -1"), + ] + triangle_lines = [ + Line(ORIGIN, LEFT), + Line(LEFT, UP), + Line(UP, ORIGIN), + ] + for line, char in zip(triangle_lines, ["a", "c", "b"]): + line.highlight("blue") + char_mob = tex_mobject(char).scale(0.25) + line.add(char_mob.shift(line.get_center())) + triangle = CompoundMobject(*triangle_lines) + triangle.center().shift(1.5*fuzzy_discoveries[0].get_right()) + how_length = text_mobject("But how is length defined?").scale(0.5) + how_length.shift(0.75*DOWN) + fuzzy_discoveries[0].add(triangle, how_length) + new_maths = [ + text_mobject(""" + Define distance between points $(x_0, y_0)$ and + $(x_1, y_1)$ as $\\sqrt{(x_1-x_0)^2 + (y_1-y_0)^2}$ + """), + text_mobject("Define ``approach'' and infinite sums"), + text_mobject("Discover $2$-adic numbers"), + text_mobject( + "Realize exponentiation is doing something much \ + different from repeated multiplication" + ) + ] + midpoints = [] + triplets = zip(fuzzy_discoveries, new_maths, it.count(2, -1.75)) + for disc, math, count in triplets: + math.scale(0.65) + for mob in disc, math: + mob.to_edge(LEFT).shift(count*UP) + math.shift(SPACE_WIDTH*RIGHT) + midpoints.append(count*UP) + + self.add(fuzzy, lines) + self.play(*map(ShimmerIn, fuzzy_discoveries)) + self.dither() + self.play(DelayByOrder(Transform( + deepcopy(fuzzy), new_math + ))) + self.play(*[ + DelayByOrder(Transform(deepcopy(disc), math)) + for disc, math in zip(fuzzy_discoveries, new_maths) + ]) + self.dither() + + +class DiscoveryAndInvention(Scene): + def construct(self): + invention, vs, discovery = text_mobject([ + "Invention ", "vs. ", "Discovery" + ]).split() + nrd = text_mobject( + "Non-rigorous truths" + ).shift(2*UP) + rt = text_mobject( + "Rigorous terms" + ).shift(2*DOWN) + + arrows = [] + self.add(discovery, vs, invention) + self.dither() + arrow = Arrow( + nrd.get_bottom(), + tail = discovery.get_top() + ) + self.play( + FadeIn(nrd), + ShowCreation(arrow) + ) + arrows.append(arrow) + self.dither() + arrow = Arrow( + invention.get_top(), + tail = nrd.get_bottom() + ) + self.play(ShowCreation(arrow)) + arrows.append(arrow) + self.dither() + arrow = Arrow( + rt.get_top(), + tail = invention.get_bottom() + ) + self.play( + FadeIn(rt), + ShowCreation(arrow) + ) + arrows.append(arrow) + self.dither() + arrow = Arrow( + discovery.get_bottom(), + tail = rt.get_top() + ) + self.play(ShowCreation(arrow)) + self.dither() + arrows.append(arrow) + for color in Color("yellow").range_to("red", 4): + for arrow in arrows: + self.play( + ShowCreation(deepcopy(arrow).highlight(color)), + run_time = 0.25 + ) + + + + +if __name__ == "__main__": + command_line_create_scene(MOVIE_PREFIX) + diff --git a/scripts/old_scripts/music_and_measure.py b/scripts/old_scripts/music_and_measure.py index 3fcac944..e2f4feee 100644 --- a/scripts/old_scripts/music_and_measure.py +++ b/scripts/old_scripts/music_and_measure.py @@ -94,7 +94,7 @@ class OpenInterval(CompoundMobject): class Piano(ImageMobject): DEFAULT_CONFIG = { - "should_buffer_points" : False, + "point_thickness" : 1, "invert" : False, "scale_value" : 0.5 } @@ -112,7 +112,7 @@ class Piano(ImageMobject): for count in range(14): key = Mobject( color = "white", - should_buffer_points = False + point_thickness = 1 ) x0 = left + count*self.ivory_jump x1 = x0 + self.ivory_jump @@ -663,7 +663,7 @@ class ConstructPiano(Scene): askew = deepcopy(keys[-1]) keys[-1].rotate_in_place(np.pi/5) for key in keys: - key.should_buffer_points = False + key.point_thickness = 1 key_copy = deepcopy(key).to_corner(DOWN+LEFT) key_copy.scale_in_place(0.25) key_copy.shift(1.8*random.random()*SPACE_WIDTH*RIGHT) diff --git a/scripts/pythagorean_proof.py b/scripts/pythagorean_proof.py new file mode 100644 index 00000000..bca5fdf1 --- /dev/null +++ b/scripts/pythagorean_proof.py @@ -0,0 +1,523 @@ +#!/usr/bin/env python + +import numpy as np +import itertools as it +from copy import deepcopy +import sys + + +from animation import * +from mobject import * +from constants import * +from region import * +from scene import Scene +from script_wrapper import command_line_create_scene + +MOVIE_PREFIX = "pythagorean_proof" + +A_COLOR = "skyblue" +B_COLOR = "pink" +C_COLOR = "yellow" +TEX_MOB_SCALE_VAL = 0.5 +POINTS = np.array([ + DOWN, + 2*UP, + DOWN+RIGHT, + 2*DOWN, + 2*DOWN+RIGHT, + DOWN+3*LEFT, + 2*UP+3*LEFT, + 4*RIGHT, + 3*UP+3*RIGHT, +]) + +class Triangle(Polygon): + def __init__(self, **kwargs): + kwargs["color"] = C_COLOR + Polygon.__init__( + self, + *POINTS[[0, 1, 2]], + edge_colors = [B_COLOR, C_COLOR, A_COLOR], + **kwargs + ) + nudge = 0.2 + target = POINTS[0]+nudge*(UP+RIGHT) + for direction in UP, RIGHT: + self.add_line(POINTS[0]+nudge*direction, target, color = "white") + + + def add_all_letters(self): + for char in "abc": + self.add_letter(char) + return self + + def add_letter(self, char, nudge = 0.2): + mob = tex_mobject(char).scale(TEX_MOB_SCALE_VAL) + if char == "a": + points = self.get_vertices()[[0, 2, 1]] + elif char == "b": + points = self.get_vertices()[[1, 0, 2]] + elif char == "c": + points = self.get_vertices()[[2, 1, 0]] + center = 0.5*sum(points[:2]) #average of first two points + mob.shift(center) + normal_dir = center-points[2] + normal_dir /= np.linalg.norm(normal_dir) + mob.shift(nudge*normal_dir) + self.add(mob) + return self + + def place_hypotenuse_on(self, point1, point2): + #self.vertices[1], self.vertices[2] + start1, start2 = self.get_vertices()[[1, 2]] + target_vect = np.array(point2)-np.array(point1) + curr_vect = start2-start1 + self.scale(np.linalg.norm(target_vect)/np.linalg.norm(curr_vect)) + self.rotate(angle_of_vector(target_vect)-angle_of_vector(curr_vect)) + self.shift(point1-self.get_vertices()[1]) + return self + + + +def a_square(**kwargs): + return Polygon(*POINTS[[0, 2, 4, 3]], color = A_COLOR, **kwargs) + +def b_square(**kwargs): + return Polygon(*POINTS[[1, 0, 5, 6]], color = B_COLOR, **kwargs) + +def c_square(**kwargs): + return Polygon(*POINTS[[1, 2, 7, 8]], color = C_COLOR, **kwargs) + + +class DrawPointsReference(Scene): + def construct(self): + for point, count in zip(POINTS, it.count()): + mob = tex_mobject(str(count)).scale(TEX_MOB_SCALE_VAL) + mob.shift(POINTS[count]) + self.add(mob) + +class DrawTriangle(Scene): + def construct(self): + self.add(Triangle().add_all_letters()) + +class DrawAllThreeSquares(Scene): + def construct(self): + a = a_square() + b = b_square() + c = c_square() + self.add(Triangle(), a, b, c) + for letter, mob in zip("abc", [a, b, c]): + char_mob = tex_mobject(letter+"^2").scale(TEX_MOB_SCALE_VAL) + char_mob.shift(mob.get_center()) + self.add(char_mob) + + +class AddParallelLines(DrawAllThreeSquares): + args_list = [ + (1, False), + (2, False), + (3, False), + (3, True), + ] + @staticmethod + def args_to_string(num, trim): + return str(num) + ("Trimmed" if trim else "") + + def construct(self, num, trim): + DrawAllThreeSquares.construct(self) + shift_pairs = [ + (4*RIGHT, 3*UP), + (ORIGIN, DOWN), + (3*LEFT, 2*DOWN) + ] + for side_shift, vert_shift in shift_pairs[:num]: + line1 = Line(BOTTOM, TOP, color = "white") + line1.shift(side_shift) + line2 = Line(LEFT_SIDE, RIGHT_SIDE, color = "white") + line2.shift(vert_shift) + self.add(line1, line2) + if trim: + for mob in self.mobjects: + mob.filter_out(lambda p : p[0] > 4) + mob.filter_out(lambda p : p[0] < -3) + mob.filter_out(lambda p : p[1] > 3) + mob.filter_out(lambda p : p[1] < -2) + +class HighlightEmergentTriangles(AddParallelLines): + args_list = [(3,True)] + def construct(self, *args): + AddParallelLines.construct(self, *args) + triplets = [ + [(0, 2), (0, -1), (1, -1)], + [(1, -1), (4, -1), (4, 0)], + [(4, 0), (4, 3), (3, 3)], + [(3, 3), (0, 3), (0, 2)], + ] + for triplet in triplets: + self.highlight_region( + region_from_polygon_vertices(*triplet), + color = "green" + ) + +class IndicateTroublePointFromParallelLines(AddParallelLines): + args_list = [(3,True)] + def construct(self, *args): + AddParallelLines.construct(self, *args) + circle = Circle(radius = 0.25) + circle.shift(DOWN+RIGHT) + vect = DOWN+RIGHT + arrow = Arrow(circle.get_center()+2*vect, circle.get_boundary_point(vect)) + arrow.highlight(circle.get_color()) + self.add_local_mobjects() + + +class DrawAllThreeSquaresWithMoreTriangles(DrawAllThreeSquares): + args_list = [ + (1, True), + (2, True), + (3, True), + (4, True), + (5, True), + (6, True), + (7, True), + (8, True), + (9, True), + (10, True), + (10, False) + ] + @staticmethod + def args_to_string(num, fill): + fill_string = "" if fill else "HollowTriangles" + return str(num) + fill_string + + def construct(self, num, fill): + DrawAllThreeSquares.construct(self) + pairs = [ + ((0, 2, 0), (1, -1, 0)), + ((-3, -1, 0), (0, -2, 0)), + ((4, -1, 0), (1, -2, 0)), + ((0, -2, 0), (-3, -1, 0)), + ((1, -2, 0), (4, -1, 0)), + ((1, -1, 0), (4, 0, 0)), + ((4, 0, 0), (3, 3, 0)), + ((3, 3, 0), (0, 2, 0)), + ((-3, 3, 0), (0, 2, 0)), + ((0, 2, 0), (-3, 3, 0)) + ] + to_flip = [1, 3, 8, 9] + for n in range(num): + triangle = Triangle() + if n in to_flip: + triangle.rotate(np.pi, UP) + self.add(triangle.place_hypotenuse_on(*pairs[n])) + vertices = list(triangle.get_vertices()) + if n not in to_flip: + vertices.reverse() + if fill: + self.highlight_region( + region_from_polygon_vertices(*vertices), + color = "green" + ) + +class IndicateBigRectangleTroublePoint(DrawAllThreeSquaresWithMoreTriangles): + args_list = [(10, False)] + def construct(self, *args): + DrawAllThreeSquaresWithMoreTriangles.construct(self, *args) + circle = Circle(radius = 0.25, color = "white") + circle.shift(4*RIGHT) + vect = DOWN+RIGHT + arrow = Arrow(circle.get_center()+vect, circle.get_boundary_point(vect)) + self.add_local_mobjects() + +class ShowBigRectangleDimensions(DrawAllThreeSquaresWithMoreTriangles): + args_list = [(10, False)] + def construct(self, num, fill): + DrawAllThreeSquaresWithMoreTriangles.construct(self, num, fill) + u_brace = underbrace((-3, -2, 0), (4, -2, 0)) + side_brace = underbrace((-3, -3, 0), (2, -3, 0)) + for brace in u_brace, side_brace: + brace.shift(0.2*DOWN) + side_brace.rotate(-np.pi/2) + a_plus_2b = tex_mobject("a+2b").scale(TEX_MOB_SCALE_VAL) + b_plus_2a = tex_mobject("b+2a").scale(TEX_MOB_SCALE_VAL) + a_plus_2b.next_to(u_brace, DOWN) + b_plus_2a.next_to(side_brace, LEFT) + self.add_local_mobjects() + +class FillInAreaOfBigRectangle(DrawAllThreeSquaresWithMoreTriangles): + args_list = [(10, False)] + def construct(self, *args): + DrawAllThreeSquaresWithMoreTriangles.construct(self, *args) + args_list = [(10, False)] + color = Color("yellow") + color.set_rgb(0.3*np.array(color.get_rgb())) + self.highlight_region( + region_from_polygon_vertices( + (-3, 3), + (-3, -2), + (4, -2), + (4, 3) + ), + color = color + ) + +class DrawOnlyABSquares(Scene): + def construct(self): + a = a_square() + b = b_square() + for char, mob in zip("ab", [a, b]): + symobl = tex_mobject(char+"^2").scale(TEX_MOB_SCALE_VAL) + symobl.shift(mob.get_center()) + self.add(symobl) + triangle = Triangle() + self.add_local_mobjects() + +class AddTriangleCopyToABSquares(DrawOnlyABSquares): + def construct(self): + DrawOnlyABSquares.construct(self) + triangle = Triangle() + triangle.rotate(np.pi, UP) + triangle.place_hypotenuse_on(3*LEFT+DOWN, 2*DOWN) + self.add(triangle) + self.highlight_triangles() + + def highlight_triangles(self): + for mob in self.mobjects: + if isinstance(mob, Triangle): + vertices = list(mob.get_vertices()) + for x in range(2): + self.highlight_region(region_from_polygon_vertices( + *vertices + ), color = "green") + vertices.reverse()#silly hack + +class AddAllTrianglesToABSquares(AddTriangleCopyToABSquares): + def construct(self): + AddTriangleCopyToABSquares.construct(self) + self.add(Triangle().place_hypotenuse_on(RIGHT+DOWN, 2*UP)) + triangle = Triangle() + triangle.rotate(np.pi, UP) + triangle.place_hypotenuse_on(2*DOWN, 3*LEFT+DOWN) + self.add(triangle) + self.highlight_triangles() + + + +class DrawNakedCSqurae(Scene): + def construct(self): + c = c_square().center() + triangle = Triangle().place_hypotenuse_on(*c.get_vertices()[[0,1]]) + triangle.add_all_letters() + self.add(triangle, c) + + +class DrawCSquareWithAllTraingles(Scene): + args_list = [ + (False, False, False, False), + (False, True, False, True), + (True, True, False, False), + (False, True, True, False), + ] + @staticmethod + def args_to_string(*toggle_vector): + return "".join(map(str, map(int, toggle_vector))) + + def construct(self, *toggle_vector): + if len(toggle_vector) == 0: + toggle_vector = [False]*4 + self.c_square = c_square().center() + vertices = it.cycle(self.c_square.get_vertices()) + last_vertex = vertices.next() + have_letters = False + self.triangles = [] + for vertex, should_flip in zip(vertices, toggle_vector): + triangle = Triangle() + pair = np.array([last_vertex, vertex]) + if should_flip: + triangle.rotate(np.pi, UP) + pair = pair[[1, 0]] + triangle.place_hypotenuse_on(*pair) + if not have_letters: + triangle.add_all_letters() + have_letters = True + self.triangles.append(triangle) + self.add(triangle) + last_vertex = vertex + self.add(self.c_square) + +class HighlightCSquareInBigSquare(DrawCSquareWithAllTraingles): + args_list = [tuple([False]*4)] + def construct(self, *args): + DrawCSquareWithAllTraingles.construct(self, *args) + self.highlight_region(region_from_polygon_vertices( + *c_square().center().get_vertices() + ), color = "yellow") + +class IndicateCSquareTroublePoint(DrawCSquareWithAllTraingles): + def construct(self, *toggle_vector): + DrawCSquareWithAllTraingles.construct(self, *toggle_vector) + circle = Circle(color = "white") + circle.scale(0.25) + vertex = self.c_square.get_vertices()[1] + circle.shift(vertex) + vect = 2*RIGHT+DOWN + arrow = Arrow(vertex+vect, circle.get_boundary_point(vect)) + self.add(circle, arrow) + + +class ZoomInOnTroublePoint(Scene): + args_list = list(it.product([True, False], [True, False])) + + @staticmethod + def args_to_string(with_labels, rotate): + label_string = "WithLabels" if with_labels else "WithoutLabels" + rotate_string = "Rotated" if rotate else "" + return label_string + rotate_string + + def construct(self, with_labels, rotate): + zoom_factor = 10 + density = zoom_factor*DEFAULT_POINT_DENSITY_1D + c = c_square(density = density) + c.shift(-c.get_vertices()[1]) + c.scale(zoom_factor) + vertices = c.get_vertices() + for index in 0, 1: + triangle = Triangle(density = density) + triangle.place_hypotenuse_on(vertices[index], vertices[index+1]) + self.add(triangle) + circle = Circle(radius = 2.5, color = "white") + angle1_arc = Circle(color = "white") + angle2_arc = Circle(color = "white").scale(0.5) + angle1_arc.filter_out(lambda (x, y, z) : not (x > 0 and y > 0 and y < x/3)) + angle2_arc.filter_out(lambda (x, y, z) : not (x < 0 and y > 0 and y < -3*x)) + + self.add_local_mobjects() + self.add_elbow() + if rotate: + for mob in self.mobjects: + mob.rotate(np.pi/2) + if with_labels: + alpha = tex_mobject("\\alpha").scale(TEX_MOB_SCALE_VAL) + beta = tex_mobject("90-\\alpha").scale(TEX_MOB_SCALE_VAL) + if rotate: + alpha.next_to(angle1_arc, UP+0.1*LEFT) + beta.next_to(angle2_arc, DOWN+0.5*LEFT) + else: + alpha.next_to(angle1_arc, RIGHT) + beta.next_to(angle2_arc, LEFT) + self.add(alpha, beta) + + + + def add_elbow(self): + c = 0.1 + p1 = c*LEFT + 3*c*UP + p2 = 3*c*RIGHT + c*UP + p3 = 2*c*RIGHT + 4*c*UP + self.add(Line(p1, p3, color = "white")) + self.add(Line(p2, p3, color = "white")) + + +class DrawTriangleWithAngles(Scene): + def construct(self): + triangle = Triangle(density = 2*DEFAULT_POINT_DENSITY_1D) + triangle.scale(2).center().add_all_letters() + vertices = triangle.get_vertices() + kwargs = {"color" : "white"} + angle1_arc = Circle(radius = 0.4, **kwargs).filter_out( + lambda (x, y, z) : not(x > 0 and y < 0 and y < -3*x) + ).shift(vertices[1]) + angle2_arc = Circle(radius = 0.2, **kwargs).filter_out( + lambda (x, y, z) : not(x < 0 and y > 0 and y < -3*x) + ).shift(vertices[2]) + alpha = tex_mobject("\\alpha") + beta = tex_mobject("90-\\alpha") + alpha.shift(vertices[1]+3*RIGHT+DOWN) + beta.shift(vertices[2]+3*RIGHT+UP) + arrow1 = Arrow(alpha, angle1_arc) + arrow2 = Arrow(beta, angle2_arc) + + self.add(triangle, angle1_arc, angle2_arc, alpha, beta, arrow1, arrow2) + + +class LabelLargeSquare(DrawCSquareWithAllTraingles): + args_list = [] + def construct(self): + DrawCSquareWithAllTraingles.construct(self) + everything = CompoundMobject(*self.mobjects) + u_brace = underbrace(2*(DOWN+LEFT), 2*(DOWN+RIGHT)) + u_brace.shift(0.2*DOWN) + side_brace = deepcopy(u_brace).rotate(np.pi/2) + upper_brace = deepcopy(u_brace).rotate(np.pi) + a_plus_b = tex_mobject("a+b").scale(TEX_MOB_SCALE_VAL) + upper_brace.add(a_plus_b.next_to(upper_brace, UP)) + side_brace.add(a_plus_b.next_to(side_brace, RIGHT)) + self.add(upper_brace, side_brace) + +class CompletelyFillLargeSquare(LabelLargeSquare): + def construct(self): + LabelLargeSquare.construct(self) + vertices = [2*(DOWN+LEFT), 2*(DOWN+RIGHT), 2*(UP+RIGHT), 2*(UP+LEFT)] + vertices.append(vertices[0]) + pairs = zip(vertices, vertices[1:]) + self.highlight_region(region_from_line_boundary(*pairs), color = "blue") + + +class FillComponentsOfLargeSquare(LabelLargeSquare): + def construct(self): + LabelLargeSquare.construct(self) + points = np.array([ + 2*UP+2*LEFT, + UP+2*LEFT, + 2*DOWN+2*LEFT, + 2*DOWN+LEFT, + 2*DOWN+2*RIGHT, + DOWN+2*RIGHT, + 2*UP+2*RIGHT, + RIGHT+2*UP + ]) + for triplet in [[0, 1, 7], [2, 3, 1], [4, 5, 3], [6, 7, 5]]: + triplet.append(triplet[0]) + self.highlight_region(region_from_line_boundary(*[ + [points[i], points[j]] + for i, j in zip(triplet, triplet[1:]) + ]), color = "green") + vertices = points[[1, 3, 5, 7, 1]] + self.highlight_region(region_from_line_boundary(*[ + [p1, p2] + for p1, p2 in zip(vertices, vertices[1:]) + ]), color = "yellow") + +class ShowRearrangementInBigSquare(DrawCSquareWithAllTraingles): + args_list = [] + def construct(self): + self.add(Square(side_length = 4, color = "white")) + DrawCSquareWithAllTraingles.construct(self) + self.remove(self.c_square) + self.triangles[1].shift(LEFT) + for i, j in [(0, 2), (3, 1)]: + self.triangles[i].place_hypotenuse_on( + *self.triangles[j].get_vertices()[[2, 1]] + ) + + +class ShowRearrangementInBigSquareWithRegions(ShowRearrangementInBigSquare): + def construct(self): + ShowRearrangementInBigSquare.construct(self) + self.highlight_region(region_from_polygon_vertices( + 2*(LEFT+UP), 2*LEFT+DOWN, RIGHT+DOWN, RIGHT+2*UP + ), color = B_COLOR) + self.highlight_region(region_from_polygon_vertices( + RIGHT+DOWN, RIGHT+2*DOWN, 2*RIGHT+2*DOWN, 2*RIGHT+DOWN + ), color = A_COLOR) + + + + + + + + + +if __name__ == "__main__": + command_line_create_scene(MOVIE_PREFIX) \ No newline at end of file