From af1ea057ade58b1bcf5175a8462a893bf3dadd55 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 23 Apr 2016 23:36:05 -0700 Subject: [PATCH] Beginnings of TOP project --- animation/simple_animations.py | 2 +- camera.py | 2 +- mobject/mobject.py | 16 +- mobject/svg_mobject.py | 12 +- mobject/tex_mobject.py | 23 +- mobject/vectorized_mobject.py | 56 ++++- topics/characters.py | 7 +- topics/combinatorics.py | 76 +++--- topics/geometry.py | 8 + topics/number_line.py | 1 + triangle_of_power/__init__.py | 0 triangle_of_power/intro.py | 446 +++++++++++++++++++++++++++++++++ 12 files changed, 579 insertions(+), 70 deletions(-) create mode 100644 triangle_of_power/__init__.py create mode 100644 triangle_of_power/intro.py diff --git a/animation/simple_animations.py b/animation/simple_animations.py index 0ae54a2f..6a09d62d 100644 --- a/animation/simple_animations.py +++ b/animation/simple_animations.py @@ -66,7 +66,7 @@ class ShowCreation(ShowPartial): class ShowCreationPerSubmobject(ShowCreation): CONFIG = { "submobject_mode" : "one_at_a_time", - "run_time" : 3 + "run_time" : 1 } class Write(ShowCreation): diff --git a/camera.py b/camera.py index d50dcbe3..736303ca 100644 --- a/camera.py +++ b/camera.py @@ -114,7 +114,7 @@ class Camera(object): def get_pen_and_fill(self, vmobject): pen = aggdraw.Pen( vmobject.get_stroke_color().get_hex_l(), - vmobject.stroke_width + max(vmobject.stroke_width, 0) ) fill = aggdraw.Brush( vmobject.get_fill_color().get_hex_l(), diff --git a/mobject/mobject.py b/mobject/mobject.py index 199c0546..1268a174 100644 --- a/mobject/mobject.py +++ b/mobject/mobject.py @@ -440,9 +440,9 @@ class Mobject(object): for mob in self, mobject ] if self_has_points and not mob_has_points: - self.push_self_into_submobjects() + mobject.null_point_align(self) elif mob_has_points and not self_has_points: - mob.push_self_into_submobjects() + self.null_point_align(mobject) self_count = len(self.submobjects) mob_count = len(mobject.submobjects) diff = abs(self_count-mob_count) @@ -452,6 +452,18 @@ class Mobject(object): mobject.add_n_more_submobjects(diff) return self + def null_point_align(self, mobject): + """ + If self has no points, but needs to align + with mobject, which has points + """ + if self.submobjects: + mobject.push_self_into_submobjects() + else: + self.points = np.array([mobject.points[0]]) + return self + + def push_self_into_submobjects(self): copy = self.copy() copy.submobjects = [] diff --git a/mobject/svg_mobject.py b/mobject/svg_mobject.py index e2776a06..4cd97041 100644 --- a/mobject/svg_mobject.py +++ b/mobject/svg_mobject.py @@ -6,6 +6,10 @@ from topics.geometry import Rectangle, Circle from helpers import * class SVGMobject(VMobject): + CONFIG = { + "initial_scale_val" : 1, + "should_center" : True, + } def __init__(self, svg_file, **kwargs): digest_config(self, kwargs, locals()) VMobject.__init__(self, **kwargs) @@ -87,7 +91,7 @@ class SVGMobject(VMobject): fill_color = WHITE, fill_opacity = 1.0 ) - mob.shift(mob.get_center()-mob.get_corner(DOWN+LEFT)) + mob.shift(mob.get_center()-mob.get_corner(UP+LEFT)) return mob def handle_transforms(self, element, mobject): @@ -110,7 +114,11 @@ class SVGMobject(VMobject): self.ref_to_element.update(new_refs) def move_into_position(self): - pass #subclasses should tweak as needed + if self.should_center: + self.center() + self.scale_in_place(self.initial_scale_val) + + class VMobjectFromSVGPathstring(VMobject): diff --git a/mobject/tex_mobject.py b/mobject/tex_mobject.py index 4dc09a5a..d690309f 100644 --- a/mobject/tex_mobject.py +++ b/mobject/tex_mobject.py @@ -3,7 +3,7 @@ from svg_mobject import SVGMobject, VMobjectFromSVGPathstring from helpers import * TEX_MOB_SCALE_VAL = 0.1 -TEXT_MOB_SCALE_VAL = 0.2 +TEXT_MOB_SCALE_VAL = 0.05 class TexSymbol(VMobjectFromSVGPathstring): @@ -32,8 +32,9 @@ class TexMobject(SVGMobject): "fill_color" : WHITE, "should_center" : True, "next_to_direction" : RIGHT, - "next_to_buff" : 0.2, + "next_to_buff" : 0.25, "initial_scale_val" : TEX_MOB_SCALE_VAL, + "propogate_style_to_family" : True, } def __init__(self, expression, **kwargs): digest_config(self, kwargs, locals()) @@ -62,14 +63,17 @@ class TexMobject(SVGMobject): #TODO, next_to not sufficient? subs = [ # TexMobject(expr) - self.__class__(expr) + self.__class__( + expr + ) for expr in self.expression ] + self.initial_scale_val = 1 for sm1, sm2 in zip(subs, subs[1:]): sm2.next_to( sm1, self.next_to_direction, - self.next_to_buff + buff = self.next_to_buff ) self.submobjects = subs return self @@ -79,17 +83,12 @@ class TexMobject(SVGMobject): lambda m1, m2 : int((m1.get_left()-m2.get_left())[0]) ) - def move_into_position(self): - self.center() - self.scale(self.initial_scale_val) - self.init_colors() - class TextMobject(TexMobject): CONFIG = { "template_tex_file" : TEMPLATE_TEXT_FILE, - "initial_scale_val" : TEXT_MOB_SCALE_VAL, + "initial_scale_val" : TEXT_MOB_SCALE_VAL } @@ -97,7 +96,7 @@ class Brace(TexMobject): CONFIG = { "buff" : 0.2, } - TEX_STRING = "\\underbrace{%s}"%(14*"\\quad") + TEX_STRING = "\\underbrace{%s}"%(3*"\\qquad") def __init__(self, mobject, direction = DOWN, **kwargs): TexMobject.__init__(self, self.TEX_STRING, **kwargs) angle = -np.arctan2(*direction[:2]) + np.pi @@ -105,7 +104,7 @@ class Brace(TexMobject): left = mobject.get_corner(DOWN+LEFT) right = mobject.get_corner(DOWN+RIGHT) self.stretch_to_fit_width(right[0]-left[0]) - self.shift(left - self.points[0] + self.buff*DOWN) + self.shift(left - self.get_corner(UP+LEFT) + self.buff*DOWN) for mob in mobject, self: mob.rotate(angle) diff --git a/mobject/vectorized_mobject.py b/mobject/vectorized_mobject.py index 6aba9548..cbdaba78 100644 --- a/mobject/vectorized_mobject.py +++ b/mobject/vectorized_mobject.py @@ -13,32 +13,61 @@ class VMobject(Mobject): "is_subpath" : False, "close_new_points" : False, "mark_paths_closed" : False, + "propogate_style_to_family" : False, } def __init__(self, *args, **kwargs): Mobject.__init__(self, *args, **kwargs) + VMobject.init_colors(self) ## Colors def init_colors(self): - self.set_stroke(self.color, self.stroke_width) - self.set_fill(self.fill_color, self.fill_opacity) + self.set_style_data( + stroke_color = self.color, + stroke_width = self.stroke_width, + fill_color = self.fill_color, + fill_opacity = self.fill_opacity, + family = self.propogate_style_to_family + ) return self def set_family_attr(self, attr, value): for mob in self.submobject_family(): setattr(mob, attr, value) - def set_fill(self, color = None, opacity = 1.0): - if color is not None: - self.set_family_attr("fill_rgb", color_to_rgb(color)) - self.set_family_attr("fill_opacity", opacity) + def set_style_data(self, + stroke_color = None, + stroke_width = None, + fill_color = None, + fill_opacity = None, + family = True): + if family: + change_func = self.set_family_attr + else: + change_func = lambda attr, value : setattr(self, attr, value) + + if stroke_color is not None: + change_func("stroke_rgb", color_to_rgb(stroke_color)) + if stroke_width is not None: + change_func("stroke_width", stroke_width) + if fill_color is not None: + change_func("fill_rgb", color_to_rgb(fill_color)) + if fill_opacity is not None: + change_func("fill_opacity", fill_opacity) return self - def set_stroke(self, color = None, width = None): - if color is not None: - self.set_family_attr("stroke_rgb", color_to_rgb(color)) - if width is not None: - self.set_family_attr("stroke_width", width) - return self + def set_fill(self, color = None, opacity = 1.0, family = True): + return self.set_style_data( + fill_color = color, + fill_opacity = opacity, + family = family + ) + + def set_stroke(self, color = None, width = None, family = True): + return self.set_style_data( + stroke_color = color, + stroke_width = width, + family = family + ) def highlight(self, color): self.set_fill(color = color) @@ -254,6 +283,9 @@ class VMobject(Mobject): getattr(mobject2, attr), alpha )) + if alpha == 1.0: + # print getattr(mobject2, attr) + setattr(self, attr, getattr(mobject2, attr)) def become_partial(self, mobject, a, b): assert(isinstance(mobject, VMobject)) diff --git a/topics/characters.py b/topics/characters.py index 76521664..f2d7f4ce 100644 --- a/topics/characters.py +++ b/topics/characters.py @@ -22,6 +22,7 @@ class PiCreature(SVGMobject): "color" : BLUE_E, "stroke_width" : 0, "fill_opacity" : 1.0, + "initial_scale_val" : 0.01, } def __init__(self, mode = "plain", **kwargs): self.parts_named = False @@ -33,10 +34,6 @@ class PiCreature(SVGMobject): SVGMobject.__init__(self, svg_file, **kwargs) self.init_colors() - def move_into_position(self): - self.scale_to_fit_height(4) - self.center() - def name_parts(self): self.mouth = self.submobjects[MOUTH_INDEX] self.body = self.submobjects[BODY_INDEX] @@ -53,7 +50,7 @@ class PiCreature(SVGMobject): self.parts_named = True def init_colors(self): - VMobject.init_colors(self) + self.set_stroke(color = BLACK, width = self.stroke_width) if not self.parts_named: self.name_parts() self.mouth.set_fill(BLACK) diff --git a/topics/combinatorics.py b/topics/combinatorics.py index cbace7fe..aac0668c 100644 --- a/topics/combinatorics.py +++ b/topics/combinatorics.py @@ -1,5 +1,8 @@ from helpers import * +from mobject.vectorized_mobject import VMobject +from mobject.tex_mobject import TexMobject + from scene import Scene @@ -81,39 +84,29 @@ class CountingScene(Scene): self.number = num_mob return self - -BIG_N_PASCAL_ROWS = 11 -N_PASCAL_ROWS = 7 - -class PascalsTriangleScene(Scene): - args_list = [ - (N_PASCAL_ROWS,), - (BIG_N_PASCAL_ROWS,), - ] - @staticmethod - def args_to_string(*args): - return str(args[0]) - - def __init__(self, nrows, *args, **kwargs): - Scene.__init__(self, *args, **kwargs) - self.nrows = nrows - self.diagram_height = 2*SPACE_HEIGHT - 1 - self.diagram_width = 1.5*SPACE_WIDTH - self.cell_height = self.diagram_height / nrows - self.cell_width = self.diagram_width / nrows - self.portion_to_fill = 0.7 - self.bottom_left = np.array( - (-self.cell_width * nrows / 2.0, -self.cell_height * nrows / 2.0, 0) - ) +class PascalsTriangle(VMobject): + CONFIG = { + "nrows" : 7, + "height" : 2*SPACE_HEIGHT - 1, + "width" : 1.5*SPACE_WIDTH, + "portion_to_fill" : 0.7 + } + def generate_points(self): + self.cell_height = self.height / self.nrows + self.cell_width = self.width / self.nrows + self.bottom_left = (self.cell_width * self.nrows / 2.0)*LEFT + \ + (self.cell_height * self.nrows / 2.0)*DOWN num_to_num_mob = {} self.coords_to_mobs = {} - self.coords = [(n, k) for n in range(nrows) for k in range(n+1)] + self.coords = [ + (n, k) + for n in range(self.nrows) + for k in range(n+1) + ] for n, k in self.coords: num = choose(n, k) center = self.coords_to_center(n, k) - if num not in num_to_num_mob: - num_to_num_mob[num] = TexMobject(str(num)) - num_mob = num_to_num_mob[num].copy() + num_mob = TexMobject(str(num)) scale_factor = min( 1, self.portion_to_fill * self.cell_height / num_mob.get_height(), @@ -123,14 +116,16 @@ class PascalsTriangleScene(Scene): if n not in self.coords_to_mobs: self.coords_to_mobs[n] = {} self.coords_to_mobs[n][k] = num_mob - self.add(*[self.coords_to_mobs[n][k] for n, k in self.coords]) + self.add(*[ + self.coords_to_mobs[n][k] + for n, k in self.coords + ]) + return self def coords_to_center(self, n, k): - return self.bottom_left + ( - self.cell_width * (k+self.nrows/2.0 - n/2.0), - self.cell_height * (self.nrows - n), - 0 - ) + x_offset = self.cell_width * (k+self.nrows/2.0 - n/2.0) + y_offset = self.cell_height * (self.nrows - n) + return self.bottom_left + x_offset*RIGHT + y_offset*UP def generate_n_choose_k_mobs(self): self.coords_to_n_choose_k = {} @@ -146,6 +141,17 @@ class PascalsTriangleScene(Scene): if n not in self.coords_to_n_choose_k: self.coords_to_n_choose_k[n] = {} self.coords_to_n_choose_k[n][k] = nck_mob + return self + + def fill_with_n_choose_k(self): + if not hasattr(self, "coords_to_n_choose_k"): + self.generate_n_choose_k_mobs() + self.submobjects = [] + self.add(*[ + self.coords_to_n_choose_k[n][k] + for n, k in self.coords + ]) + return self def generate_sea_of_zeros(self): zero = TexMobject("0") @@ -158,7 +164,7 @@ class PascalsTriangleScene(Scene): mob.shift(self.coords_to_center(n, k)) self.coords_to_mobs[n][k] = mob self.add(mob) - + return self diff --git a/topics/geometry.py b/topics/geometry.py index 4918d8e3..3ac972ab 100644 --- a/topics/geometry.py +++ b/topics/geometry.py @@ -113,6 +113,7 @@ class Arrow(Line): "color" : YELLOW_C, "tip_length" : 0.25, "buff" : 0.3, + "propogate_style_to_family" : True, } def __init__(self, *args, **kwargs): if len(args) == 1: @@ -145,6 +146,13 @@ class Vector(Arrow): def __init__(self, start, direction, **kwargs): Arrow.__init__(self, start, end, **kwargs) +class DoubleArrow(Arrow): + def __init__(self, *args, **kwargs): + Arrow.__init__(self, *args, **kwargs) + self.start, self.end = self.end, self.start + self.add_tip() + self.start, self.end = self.end, self.start + class Cross(VMobject): CONFIG = { "color" : YELLOW, diff --git a/topics/number_line.py b/topics/number_line.py index 326f74be..ed208634 100644 --- a/topics/number_line.py +++ b/topics/number_line.py @@ -18,6 +18,7 @@ class NumberLine(VMobject): "numbers_with_elongated_ticks" : [0], "longer_tick_multiple" : 2, "number_at_center" : 0, + "propogate_style_to_family" : True } def __init__(self, **kwargs): digest_config(self, kwargs) diff --git a/triangle_of_power/__init__.py b/triangle_of_power/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/triangle_of_power/intro.py b/triangle_of_power/intro.py new file mode 100644 index 00000000..25357994 --- /dev/null +++ b/triangle_of_power/intro.py @@ -0,0 +1,446 @@ +from helpers import * + +from mobject.tex_mobject import TexMobject +from mobject import Mobject + +from animation.animation import Animation +from animation.transform import * +from animation.simple_animations import * +from animation.playground import * +from topics.geometry import * +from topics.characters import * +from topics.functions import * +from topics.number_line import * +from topics.combinatorics import PascalsTriangle +from scene import Scene +# from scene.zoomed_scene import ZoomedScene +from camera import Camera, MovingCamera +from mobject.svg_mobject import * + +from mobject.tex_mobject import * + + +class TrigAnimation(Animation): + CONFIG = { + "rate_func" : None, + "run_time" : 5, + "sin_color" : BLUE, + "cos_color" : RED, + "tan_color" : GREEN + } + def __init__(self, **kwargs): + digest_config(self, kwargs) + x_axis = NumberLine( + x_min = -3, + x_max = 3, + color = BLUE_E + ) + y_axis = x_axis.copy().rotate(np.pi/2) + circle = Circle(color = WHITE) + self.trig_lines = [ + Line(ORIGIN, RIGHT, color = color) + for color in self.sin_color, self.cos_color, self.tan_color + ] + mobject = VMobject( + x_axis, y_axis, circle, + *self.trig_lines + ) + mobject.to_edge(RIGHT) + self.center = mobject.get_center() + Animation.__init__(self, mobject, **kwargs) + + def update_mobject(self, alpha): + theta = 2*np.pi*alpha + circle_point = np.cos(theta)*RIGHT+np.sin(theta)*UP+self.center + points = [ + circle_point[0]*RIGHT, + circle_point[1]*UP+self.center, + ( + np.sign(np.cos(theta))*np.sqrt( + np.tan(theta)**2 - np.sin(theta)**2 + ) + np.cos(theta) + )*RIGHT + self.center, + ] + for line, point in zip(self.trig_lines, points): + line.set_anchor_points( + [circle_point, point], + mode = "corners" + ) + + + +class Notation(Scene): + def construct(self): + self.introduce_notation() + self.shift_to_good_and_back() + self.shift_to_visuals() + self.swipe_left() + + + def introduce_notation(self): + notation = TextMobject("Notation") + notation.to_edge(UP) + + self.sum1 = TexMobject("\\sum_{n=1}^\\infty \\dfrac{1}{n}") + self.prod1 = TexMobject("\\prod_{p\\text{ prime}}\\left(1-p^{-s}\\right)") + self.trigs1 = TexMobject([ + ["\\sin", "(x)"], + ["\\cos", "(x)"], + ["\\tan", "(x)"], + ], next_to_direction = DOWN) + self.func1 = TexMobject("f(x) = y") + symbols = [self.sum1, self.prod1, self.trigs1, self.func1] + for sym, vect in zip(symbols, compass_directions(4, UP+LEFT)): + sym.scale(0.5) + vect[0] *= 2 + sym.shift(vect) + self.symbols = VMobject(*symbols) + + self.play(Write(notation)) + self.play(Write(self.symbols)) + self.dither() + self.add(notation, self.symbols) + + + + def shift_to_good_and_back(self): + sum2 = self.sum1.copy() + sigma = sum2.submobjects[1] + plus = TexMobject("+").replace(sigma) + sum2.submobjects[1] = plus + + prod2 = self.prod1.copy() + pi = prod2.submobjects[0] + times = TexMobject("\\times").replace(pi) + prod2.submobjects[0] = times + + new_sin, new_cos, new_tan = [ + VMobject().set_anchor_points( + corners, mode = "corners" + ).replace(trig_part.split()[0]) + for corners, trig_part in zip( + [ + [RIGHT, RIGHT+UP, LEFT], + [RIGHT+UP, LEFT, RIGHT], + [RIGHT+UP, RIGHT, LEFT], + ], + self.trigs1.split() + ) + ] + x1, x2, x3 = [ + trig_part.split()[1] + for trig_part in self.trigs1.split() + ] + trigs2 = VMobject( + VMobject(new_sin, x1), + VMobject(new_cos, x2), + VMobject(new_tan, x3), + ) + + x, arrow, y = TexMobject("x \\rightarrow y").split() + f = TexMobject("f") + f.next_to(arrow, UP) + func2 = VMobject(f, VMobject(), x, VMobject(), arrow, y) + func2.scale(0.5) + func2.shift(self.func1.get_center()) + + good_symbols = VMobject(sum2, prod2, trigs2, func2) + bad_symbols = self.symbols.copy() + self.play(Transform( + self.symbols, good_symbols, + path_arc = np.pi + )) + self.dither(3) + self.play(Transform( + self.symbols, bad_symbols, + path_arc = np.pi + )) + self.dither() + + + def shift_to_visuals(self): + sigma, prod, trig, func = self.symbols.split() + new_trig = trig.copy() + sin, cos, tan = [ + trig_part.split()[0] + for trig_part in new_trig.split() + ] + trig_anim = TrigAnimation() + sin.highlight(trig_anim.sin_color) + cos.highlight(trig_anim.cos_color) + tan.highlight(trig_anim.tan_color) + new_trig.to_corner(UP+RIGHT) + sum_lines = self.get_harmonic_sum_lines() + + self.play( + Transform(trig, new_trig), + *it.starmap(ApplyMethod, [ + (sigma.to_corner, UP+LEFT), + (prod.shift, 15*LEFT), + (func.shift, 5*UP), + ]) + ) + sum_lines.next_to(sigma, DOWN) + self.remove(prod, func) + self.play( + trig_anim, + Write(sum_lines) + ) + self.play(trig_anim) + self.dither() + + def get_harmonic_sum_lines(self): + result = VMobject() + for n in range(1, 8): + big_line = NumberLine( + x_min = 0, + x_max = 1.01, + tick_frequency = 1./n, + numbers_with_elongated_ticks = [], + color = WHITE + ) + little_line = Line( + big_line.number_to_point(0), + big_line.number_to_point(1./n), + color = RED + ) + big_line.add(little_line) + big_line.shift(0.5*n*DOWN) + result.add(big_line) + return result + + + def swipe_left(self): + everyone = VMobject(*self.mobjects) + self.play(ApplyMethod(everyone.shift, 20*LEFT)) + + +class ButDots(Scene): + def construct(self): + but = TextMobject("but") + dots = TexMobject("\\dots") + dots.next_to(but, aligned_edge = DOWN) + but.shift(20*RIGHT) + self.play(ApplyMethod(but.shift, 20*LEFT)) + self.play(Write(dots, run_time = 5)) + self.dither() + + +class ThreesomeOfNotation(Scene): + def construct(self): + exp = TexMobject("x^y = z") + log = TexMobject("\\log_x(z) = y") + rad = TexMobject("\\sqrt[y]{z} = x") + exp.to_edge(LEFT).shift(2*UP) + rad.to_edge(RIGHT).shift(2*DOWN) + x1, y1, eq, z1 = exp.split() + l, o, g, x2, p, z2, p, eq, y2 = log.split() + y3, r, r, z3, eq, x3 = rad.split() + vars1 = VMobject(x1, y1, z1).copy() + vars2 = VMobject(x2, y2, z2) + vars3 = VMobject(x3, y3, z3) + + self.play(Write(exp)) + self.play(Transform(vars1, vars2, path_arc = -np.pi)) + self.play(Write(log)) + self.play(Transform(vars1, vars3, path_arc = -np.pi)) + self.play(Write(rad)) + self.dither() + + +class TwoThreeEightExample(Scene): + def construct(self): + start = TexMobject("2 \\cdot 2 \\cdot 2 = 8") + two1, dot1, two2, dot2, two3, eq, eight = start.split() + brace = Brace(VMobject(two1, two3), DOWN) + three = TexMobject("3").next_to(brace, DOWN, buff = 0.2) + rogue_two = two1.copy() + + self.add(two1) + self.play( + Transform(rogue_two, two2), + Write(dot1), + run_time = 0.5 + ) + self.add(two2) + self.play( + Transform(rogue_two, two3), + Write(dot2), + run_time = 0.5 + ) + self.add(two3) + self.remove(rogue_two) + self.play( + Write(eq), Write(eight), + GrowFromCenter(brace), + Write(three), + run_time = 1 + ) + self.dither() + + exp = TexMobject("2^3") + exp.next_to(eq, LEFT) + exp.shift(0.2*UP) + base_two, exp_three = exp.split() + self.play( + Transform( + VMobject(two1, dot1, two2, dot2, brace, three), + exp_three, + path_arc = -np.pi/2 + ), + Transform(two3, base_two) + ) + self.clear() + self.add(base_two, exp_three, eq, eight) + self.dither(3) + + rad_three, rad1, rad2, rad_eight, rad_eq, rad_two = \ + TexMobject("\\sqrt[3]{8} = 2").split() + self.play(*[ + Transform(*pair, path_arc = np.pi/2) + for pair in [ + (exp_three, rad_three), + (VMobject(), rad1), + (VMobject(), rad2), + (eight, rad_eight), + (eq, rad_eq), + (base_two, rad_two) + ] + ]) + self.dither() + self.play(ApplyMethod( + VMobject(rad1, rad2).highlight, RED, + rate_func = there_and_back, + run_time = 2 + )) + self.remove(rad1, rad2) + self.dither() + + l, o, g, log_two, p1, log_eight, p2, log_eq, log_three = \ + TexMobject("\\log_2(8) = 3").split() + self.clear() + self.play(*[ + Transform(*pair, path_arc = np.pi/2) + for pair in [ + (rad1, l), + (rad2, o), + (rad2.copy(), g), + (rad_two, log_two), + (VMobject(), p1), + (rad_eight, log_eight), + (VMobject(), p2), + (rad_eq, log_eq), + (rad_three, log_three) + ] + ]) + self.dither() + self.play(ApplyMethod( + VMobject(l, o, g).highlight, RED, + rate_func = there_and_back, + run_time = 2 + )) + self.dither() + +class WhatTheHell(Scene): + def construct(self): + randy = Randolph() + randy.to_corner(DOWN+LEFT) + exp, rad, log = map(TexMobject,[ + "2^3 = 8", + "\\sqrt[3]{8} = 2", + "\\log_2(8) = 3", + ]) + # exp.highlight(BLUE_D) + # rad.highlight(RED_D) + # log.highlight(GREEN_D) + arrow1 = DoubleArrow(DOWN, UP) + arrow2 = arrow1.copy() + last = exp + for mob in arrow1, rad, arrow2, log: + mob.next_to(last, DOWN) + last = mob + q_marks = VMobject(*[ + TexMobject("?!").next_to(arrow, RIGHT) + for arrow in arrow1, arrow2 + ]) + q_marks.highlight(RED_D) + everyone = VMobject(exp, rad, log, arrow1, arrow2, q_marks) + everyone.scale(0.7) + everyone.to_corner(UP+RIGHT) + phrases = [ + TextMobject( + ["Communicate with", words] + ).next_to(mob, LEFT, buff = 1) + for words, mob in [ + ("position", exp), + ("a new symbol", rad), + ("a word", log) + ] + ] + for phrase, color in zip(phrases, [BLUE, RED, GREEN]): + phrase.split()[1].highlight(color) + + self.play(ApplyMethod(randy.change_mode, "angry")) + self.play(FadeIn(VMobject(exp, rad, log))) + self.play( + ShowCreationPerSubmobject(arrow1), + ShowCreationPerSubmobject(arrow2) + ) + self.play(Write(q_marks)) + self.dither() + self.remove(randy) + self.play(Write(VMobject(*phrases))) + self.dither() + +class Countermathematical(Scene): + def construct(self): + counterintuitive = TextMobject("Counterintuitive") + mathematical = TextMobject("mathematical") + intuitive = VMobject(*counterintuitive.split()[7:]) + mathematical.shift(intuitive.get_left()-mathematical.get_left()) + + self.add(counterintuitive) + self.dither() + self.play(Transform(intuitive, mathematical)) + self.dither() + + +class PascalsCollision(Scene): + def construct(self): + pascals_triangle = PascalsTriangle() + pascals_triangle.scale(0.5) + final_triangle = PascalsTriangle() + final_triangle.fill_with_n_choose_k() + pascals_triangle.to_corner(UP+LEFT) + final_triangle.scale(0.7) + final_triangle.to_edge(UP) + equation = TexMobject([ + "{n \\choose k}", + " = \\dfrac{n!}{(n-k)!k!}" + ]) + equation.scale(0.5) + equation.to_corner(UP+RIGHT) + n_choose_k, formula = equation.split() + words = TextMobject("Seemingly unrelated") + words.shift(2*DOWN) + to_remove = VMobject(*words.split()[:-7]) + + self.add(pascals_triangle, n_choose_k, formula) + self.play(Write(words)) + self.dither() + self.play( + Transform(pascals_triangle, final_triangle), + Transform(n_choose_k, final_triangle), + FadeOut(formula), + ApplyMethod(to_remove.shift, 5*DOWN) + ) + self.dither() + + + + + + + + +