From d7a405ec47bad1da959d01ee53c624bc0d8057fe Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 7 Nov 2016 11:06:40 -0800 Subject: [PATCH] Moved topology/patreon animations to old_projects --- old_projects/patreon.py | 783 ++++++++++++++ old_projects/wcat.py | 2141 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 2924 insertions(+) create mode 100644 old_projects/patreon.py create mode 100644 old_projects/wcat.py diff --git a/old_projects/patreon.py b/old_projects/patreon.py new file mode 100644 index 00000000..efccf8d3 --- /dev/null +++ b/old_projects/patreon.py @@ -0,0 +1,783 @@ +from helpers import * + +from mobject.tex_mobject import TexMobject +from mobject import Mobject +from mobject.image_mobject import ImageMobject +from mobject.vectorized_mobject import * + +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.fractals import * +from topics.number_line import * +from topics.combinatorics import * +from topics.numerals import * +from topics.three_dimensions import * +from scene import Scene +from camera import Camera +from mobject.svg_mobject import * +from mobject.tex_mobject import * + + +class Clock(VMobject): + CONFIG = { + "propogate_style_to_family" : True, + } + def __init__(self, **kwargs): + circle = Circle() + ticks = [] + for x in range(12): + alpha = x/12. + point = complex_to_R3( + np.exp(2*np.pi*alpha*complex(0, 1)) + ) + length = 0.2 if x%3 == 0 else 0.1 + ticks.append( + Line(point, (1-length)*point) + ) + self.hour_hand = Line(ORIGIN, 0.3*UP) + self.minute_hand = Line(ORIGIN, 0.6*UP) + for hand in self.hour_hand, self.minute_hand: + #Balance out where the center is + hand.add(VectorizedPoint(-hand.get_end())) + + VMobject.__init__( + self, circle, + self.hour_hand, self.minute_hand, + *ticks + ) + +class ClockPassesTime(Animation): + CONFIG = { + "run_time" : 5, + "rate_func" : None, + } + def __init__(self, clock, **kwargs): + assert(isinstance(clock, Clock)) + rot_kwargs = { + "axis" : OUT, + "in_place" : True + } + self.hour_rotation = Rotating( + clock.hour_hand, + radians = -2*np.pi, + **rot_kwargs + ) + self.minute_rotation = Rotating( + clock.minute_hand, + radians = -12*2*np.pi, + **rot_kwargs + ) + Animation.__init__(self, clock, **kwargs) + + def update_mobject(self, alpha): + for rotation in self.hour_rotation, self.minute_rotation: + rotation.update_mobject(alpha) + +class SideGigToFullTime(Scene): + def construct(self): + morty = Mortimer() + morty.next_to(ORIGIN, DOWN) + self.add(morty) + + self.side_project(morty) + self.income(morty) + self.full_time(morty) + + def side_project(self, morty): + rect = PictureInPictureFrame() + rect.next_to(morty, UP+LEFT) + side_project = TextMobject("Side project") + side_project.next_to(rect, UP) + dollar_sign = TexMobject("\\$") + cross = VGroup(*[ + Line(vect, -vect, color = RED) + for vect in UP+RIGHT, UP+LEFT + ]) + cross.scale_to_fit_height(dollar_sign.get_height()) + no_money = VGroup(dollar_sign, cross) + no_money.next_to(rect, DOWN) + + self.play( + morty.change_mode, "raise_right_hand", + morty.look_at, rect + ) + self.play( + Write(side_project), + ShowCreation(rect) + ) + self.dither() + self.play(Blink(morty)) + self.dither() + self.play(Write(dollar_sign)) + self.play(ShowCreation(cross)) + + self.screen_title = side_project + self.cross = cross + + def income(self, morty): + dollar_signs = VGroup(*[ + TexMobject("\\$") + for x in range(10) + ]) + dollar_signs.arrange_submobjects(RIGHT, buff = LARGE_BUFF) + dollar_signs.highlight(BLACK) + dollar_signs.next_to(morty.eyes, RIGHT, buff = 2*LARGE_BUFF) + + self.play( + morty.change_mode, "happy", + morty.look_at, dollar_signs, + dollar_signs.shift, LEFT, + dollar_signs.highlight, GREEN + ) + for x in range(5): + last_sign = dollar_signs[0] + dollar_signs.remove(last_sign) + self.play( + FadeOut(last_sign), + dollar_signs.shift, LEFT + ) + random.shuffle(dollar_signs.submobjects) + self.play( + ApplyMethod( + dollar_signs.shift, + (SPACE_HEIGHT+1)*DOWN, + submobject_mode = "lagged_start" + ), + morty.change_mode, "guilty", + morty.look, DOWN+RIGHT + ) + self.play(Blink(morty)) + + def full_time(self, morty): + new_title = TextMobject("Full time") + new_title.move_to(self.screen_title) + q_mark = TexMobject("?") + q_mark.next_to(self.cross) + q_mark.highlight(GREEN) + + self.play(morty.look_at, q_mark) + self.play(Transform(self.screen_title, new_title)) + self.play( + Transform(self.cross, q_mark), + morty.change_mode, "confused" + ) + self.play(Blink(morty)) + self.dither() + self.play( + morty.change_mode, "happy", + morty.look, UP+RIGHT + ) + self.play(Blink(morty)) + self.dither() + +class TakesTime(Scene): + def construct(self): + rect = PictureInPictureFrame(height = 4) + rect.to_edge(RIGHT, buff = LARGE_BUFF) + clock = Clock() + clock.hour_hand.highlight(BLUE_C) + clock.minute_hand.highlight(BLUE_D) + clock.next_to(rect, LEFT, buff = LARGE_BUFF) + self.add(rect) + self.play(ShowCreation(clock)) + for x in range(3): + self.play(ClockPassesTime(clock)) + +class GrowingToDoList(Scene): + def construct(self): + morty = Mortimer() + morty.flip() + morty.next_to(ORIGIN, DOWN+LEFT) + title = TextMobject("3blue1brown to-do list") + title.next_to(ORIGIN, RIGHT) + title.to_edge(UP) + underline = Line(title.get_left(), title.get_right()) + underline.next_to(title, DOWN) + + lines = VGroup(*map(TextMobject, [ + "That one on topology", + "Something with quaternions", + "Solving puzzles with binary counting", + "Tatoos on math", + "Laplace stuffs", + "The role of memorization in math", + "Strangeness of the axiom of choice", + "Tensors", + "Different view of $e^{\\pi i}$", + "Quadratic reciprocity", + "Fourier stuffs", + "$1+2+3+\\cdots = -\\frac{1}{12}$", + "Understanding entropy", + ])) + lines.scale(0.65) + lines.arrange_submobjects(DOWN, buff = MED_BUFF, aligned_edge = LEFT) + lines.gradient_highlight(BLUE_C, YELLOW) + lines.next_to(title, DOWN, buff = LARGE_BUFF/2.) + lines.to_edge(RIGHT) + + self.play( + Write(title), + morty.look_at, title + ) + self.play( + Write(lines[0]), + morty.change_mode, "erm", + run_time = 1 + ) + for line in lines[1:3]: + self.play( + Write(line), + morty.look_at, line, + run_time = 1 + ) + self.play( + morty.change_mode, "pleading", + morty.look_at, lines, + Write( + VGroup(*lines[3:]), + lag_factor = 7 + ) + ) + +class TwoTypesOfVideos(Scene): + def construct(self): + morty = Mortimer().shift(2*DOWN) + stand_alone = TextMobject("Standalone videos") + stand_alone.shift(SPACE_WIDTH*LEFT/2) + stand_alone.to_edge(UP) + series = TextMobject("Series") + series.shift(SPACE_WIDTH*RIGHT/2) + series.to_edge(UP) + box = Rectangle(width = 16, height = 9, color = WHITE) + box.scale_to_fit_height(3) + box.next_to(stand_alone, DOWN) + series_list = VGroup(*[ + TextMobject("Essence of %s"%s) + for s in [ + "linear algebra", + "calculus", + "probability", + "real analysis", + "complex analysis", + "ODEs", + ] + ]) + series_list.arrange_submobjects(DOWN, aligned_edge = LEFT, buff = MED_BUFF) + series_list.scale_to_fit_width(SPACE_WIDTH-2) + series_list.next_to(series, DOWN, buff = MED_BUFF) + series_list.to_edge(RIGHT) + + fridays = TextMobject("Every other friday") + when_done = TextMobject("When series is done") + for words, vect in (fridays, LEFT), (when_done, RIGHT): + words.highlight(YELLOW) + words.next_to( + morty, vect, + buff = MED_BUFF, + aligned_edge = UP + ) + unless = TextMobject(""" + Unless you're + a patron \\dots + """) + unless.next_to(when_done, DOWN, buff = MED_BUFF) + + self.add(morty) + self.play(Blink(morty)) + self.play( + morty.change_mode, "raise_right_hand", + morty.look_at, stand_alone, + Write(stand_alone, run_time = 2), + ) + self.play( + morty.change_mode, "raise_left_hand", + morty.look_at, series, + Write(series, run_time = 2), + ) + self.play(Blink(morty)) + self.dither() + self.play( + morty.change_mode, "raise_right_hand", + morty.look_at, box, + ShowCreation(box) + ) + for x in range(3): + self.dither(2) + self.play(Blink(morty)) + self.play( + morty.change_mode, "raise_left_hand", + morty.look_at, series + ) + for i, words in enumerate(series_list): + self.play(Write(words), run_time = 1) + self.play(Blink(morty)) + self.dither() + self.play(series_list[1].highlight, BLUE) + self.dither(2) + self.play(Blink(morty)) + self.dither() + pairs = [ + (fridays, "speaking"), + (when_done, "wave_2") , + (unless, "surprised"), + ] + for words, mode in pairs: + self.play( + Write(words), + morty.change_mode, mode, + morty.look_at, words + ) + self.dither() + +class ClassWatching(TeacherStudentsScene): + def construct(self): + rect = PictureInPictureFrame(height = 4) + rect.next_to(self.get_teacher(), UP, buff = LARGE_BUFF/2.) + rect.to_edge(RIGHT) + self.add(rect) + for pi in self.get_students(): + pi.look_at(rect) + + self.random_blink(5) + self.change_student_modes( + "raise_left_hand", + "raise_right_hand", + "sassy", + ) + self.play(self.get_teacher().change_mode, "pondering") + self.random_blink(3) + +class RandolphWatching(Scene): + def construct(self): + randy = Randolph() + randy.shift(2*LEFT) + randy.look(RIGHT) + + self.add(randy) + self.dither() + self.play(Blink(randy)) + self.dither() + self.play( + randy.change_mode, "pondering", + randy.look, RIGHT + ) + self.play(Blink(randy)) + self.dither() + +class RandolphWatchingWithLaptop(Scene): + pass + +class GrowRonaksSierpinski(Scene): + CONFIG = { + "colors" : [BLUE, YELLOW, BLUE_C, BLUE_E], + "dot_radius" : 0.08, + "n_layers" : 64, + } + def construct(self): + sierp = self.get_ronaks_sierpinski(self.n_layers) + dots = self.get_dots(self.n_layers) + self.triangle = VGroup(sierp, dots) + self.triangle.scale(1.5) + self.triangle.shift(3*UP) + sierp_layers = sierp.submobjects + dot_layers = dots.submobjects + + last_dot_layer = dot_layers[0] + self.play(ShowCreation(last_dot_layer)) + run_time = 1 + for n, sierp_layer, dot_layer in zip(it.count(1), sierp_layers, dot_layers[1:]): + self.play( + ShowCreation(sierp_layer, submobject_mode = "one_at_a_time"), + Animation(last_dot_layer), + run_time = run_time + ) + self.play(ShowCreation( + dot_layer, + run_time = run_time, + submobject_mode = "all_at_once" + )) + # if n == 2: + # dot = dot_layer[1] + # words = TextMobject("Stop growth at pink") + # words.next_to(dot, DOWN, 2) + # arrow = Arrow(words, dot) + # self.play( + # Write(words), + # ShowCreation(arrow) + # ) + # self.dither() + # self.play(*map(FadeOut, [words, arrow])) + log2 = np.log2(n) + if n > 2 and log2-np.round(log2) == 0 and n < self.n_layers: + self.dither() + self.rescale() + run_time /= 1.3 + last_dot_layer = dot_layer + + def rescale(self): + shown_mobs = VGroup(*self.get_mobjects()) + shown_mobs_copy = shown_mobs.copy() + self.remove(shown_mobs) + self.add(shown_mobs_copy) + top = shown_mobs.get_top() + self.triangle.scale(0.5) + self.triangle.move_to(top, aligned_edge = UP) + self.play(Transform(shown_mobs_copy, shown_mobs)) + self.remove(shown_mobs_copy) + self.add(shown_mobs) + + def get_pascal_point(self, n, k): + return n*rotate_vector(RIGHT, -2*np.pi/3) + k*RIGHT + + def get_lines_at_layer(self, n): + lines = VGroup() + for k in range(n+1): + if choose(n, k)%2 == 1: + p1 = self.get_pascal_point(n, k) + p2 = self.get_pascal_point(n+1, k) + p3 = self.get_pascal_point(n+1, k+1) + lines.add(Line(p1, p2), Line(p1, p3)) + return lines + + def get_dot_layer(self, n): + dots = VGroup() + for k in range(n+1): + p = self.get_pascal_point(n, k) + dot = Dot(p, radius = self.dot_radius) + if choose(n, k)%2 == 0: + if choose(n-1, k)%2 == 0: + continue + dot.highlight(PINK) + else: + dot.highlight(WHITE) + dots.add(dot) + return dots + + def get_ronaks_sierpinski(self, n_layers): + ronaks_sierpinski = VGroup() + for n in range(n_layers): + ronaks_sierpinski.add(self.get_lines_at_layer(n)) + ronaks_sierpinski.gradient_highlight(*self.colors) + ronaks_sierpinski.set_stroke(width = 0)##TODO + return ronaks_sierpinski + + def get_dots(self, n_layers): + dots = VGroup() + for n in range(n_layers+1): + dots.add(self.get_dot_layer(n)) + return dots + +class PatreonLogo(Scene): + def construct(self): + words1 = TextMobject( + "Support future\\\\", + "3blue1brown videos" + ) + words2 = TextMobject( + "Early access to\\\\", + "``Essence of'' series" + ) + for words in words1, words2: + words.scale(2) + words.to_edge(DOWN) + self.play(Write(words1)) + self.dither(2) + self.play(Transform(words1, words2)) + self.dither(2) + +class PatreonLogin(Scene): + pass + +class PythagoreanTransformation(Scene): + def construct(self): + tri1 = VGroup( + Line(ORIGIN, 2*RIGHT, color = BLUE), + Line(2*RIGHT, 3*UP, color = YELLOW), + Line(3*UP, ORIGIN, color = MAROON_B), + ) + tri1.shift(2.5*(DOWN+LEFT)) + tri2, tri3, tri4 = copies = [ + tri1.copy().rotate(-i*np.pi/2) + for i in range(1, 4) + ] + a = TexMobject("a").next_to(tri1[0], DOWN, buff = MED_BUFF) + b = TexMobject("b").next_to(tri1[2], LEFT, buff = MED_BUFF) + c = TexMobject("c").next_to(tri1[1].get_center(), UP+RIGHT) + + c_square = Polygon(*[ + tri[1].get_end() + for tri in [tri1] + copies + ]) + c_square.set_stroke(width = 0) + c_square.set_fill(color = YELLOW, opacity = 0.5) + c_square_tex = TexMobject("c^2") + big_square = Polygon(*[ + tri[0].get_start() + for tri in [tri1] + copies + ]) + big_square.highlight(WHITE) + a_square = Square(side_length = 2) + a_square.shift(1.5*(LEFT+UP)) + a_square.set_stroke(width = 0) + a_square.set_fill(color = BLUE, opacity = 0.5) + a_square_tex = TexMobject("a^2") + a_square_tex.move_to(a_square) + b_square = Square(side_length = 3) + b_square.move_to( + a_square.get_corner(DOWN+RIGHT), + aligned_edge = UP+LEFT + ) + b_square.set_stroke(width = 0) + b_square.set_fill(color = MAROON_B, opacity = 0.5) + b_square_tex = TexMobject("b^2") + b_square_tex.move_to(b_square) + + self.play(ShowCreation(tri1, run_time = 2)) + self.play(*map(Write, [a, b, c])) + self.dither() + self.play( + FadeIn(c_square), + Animation(c) + ) + self.play(Transform(c, c_square_tex)) + self.dither(2) + mover = tri1.copy() + for copy in copies: + self.play(Transform( + mover, copy, + path_arc = -np.pi/2 + )) + self.add(copy) + self.remove(mover) + self.add(big_square, *[tri1]+copies) + self.dither(2) + self.play(*map(FadeOut, [a, b, c, c_square])) + self.play( + tri3.shift, + tri1.get_corner(UP+LEFT) -\ + tri3.get_corner(UP+LEFT) + ) + self.play(tri2.shift, 2*RIGHT) + self.play(tri4.shift, 3*UP) + self.dither() + self.play(FadeIn(a_square)) + self.play(FadeIn(b_square)) + self.play(Write(a_square_tex)) + self.play(Write(b_square_tex)) + self.dither(2) + +class KindWordsOnEoLA(TeacherStudentsScene): + def construct(self): + rect = Rectangle(width = 16, height = 9, color = WHITE) + rect.scale_to_fit_height(4) + title = TextMobject("Essence of linear algebra") + title.to_edge(UP) + rect.next_to(title, DOWN) + self.play( + Write(title), + ShowCreation(rect), + *[ + ApplyMethod(pi.look_at, rect) + for pi in self.get_everyone() + ], + run_time = 2 + ) + self.random_blink() + self.change_student_modes(*["hooray"]*3) + self.random_blink() + self.play(self.get_teacher().change_mode, "happy") + self.random_blink() + +class MakeALotOfPiCreaturesHappy(Scene): + def construct(self): + width = 7 + height = 4 + pis = VGroup(*[ + VGroup(*[ + Randolph() + for x in range(7) + ]).arrange_submobjects(RIGHT, buff = 2*MED_BUFF) + for x in range(4) + ]).arrange_submobjects(DOWN, buff = 2*MED_BUFF) + + pi_list = list(it.chain(*[ + layer.submobjects + for layer in pis.submobjects + ])) + random.shuffle(pi_list) + colors = color_gradient([BLUE_D, GREY_BROWN], len(pi_list)) + for pi, color in zip(pi_list, colors): + pi.highlight(color) + pis = VGroup(*pi_list) + pis.scale_to_fit_height(6) + + self.add(pis) + pis.generate_target() + self.dither() + for pi, color in zip(pis.target, colors): + pi.change_mode("hooray") + # pi.scale_in_place(1) + pi.highlight(color) + self.play( + MoveToTarget( + pis, + run_time = 2, + submobject_mode = "lagged_start", + lag_factor = 5, + ) + ) + for x in range(10): + pi = random.choice(pi_list) + self.play(Blink(pi)) + + +class IntegrationByParts(Scene): + def construct(self): + rect = Rectangle(width = 5, height = 3) + # f = lambda t : 4*np.sin(t*np.pi/2) + f = lambda t : 4*t + g = lambda t : 3*smooth(t) + curve = ParametricFunction(lambda t : f(t)*RIGHT + g(t)*DOWN) + curve.highlight(YELLOW) + curve.center() + rect = Rectangle() + rect.replace(curve, stretch = True) + + regions = [] + for vect, color in (UP+RIGHT, BLUE), (DOWN+LEFT, GREEN): + region = curve.copy() + region.add_control_points(3*[rect.get_corner(vect)]) + region.set_stroke(width = 0) + region.set_fill(color = color, opacity = 0.5) + regions.append(region) + upper_right, lower_left = regions + + v_lines, h_lines = VGroup(), VGroup() + for alpha in np.linspace(0, 1, 30): + point = curve.point_from_proportion(alpha) + top_point = curve.points[0][1]*UP + point[0]*RIGHT + left_point = curve.points[0][0]*RIGHT + point[1]*UP + v_lines.add(Line(top_point, point)) + h_lines.add(Line(left_point, point)) + v_lines.highlight(BLUE_E) + h_lines.highlight(GREEN_E) + + equation = TexMobject( + "\\int_0^1 g\\,df", + "+\\int_0^1 f\\,dg", + "= \\big(fg \\big)_0^1" + ) + equation.to_edge(UP) + equation.highlight_by_tex( + "\\int_0^1 g\\,df", + upper_right.get_color() + ) + equation.highlight_by_tex( + "+\\int_0^1 f\\,dg", + lower_left.get_color() + ) + + left_brace = Brace(rect, LEFT) + down_brace = Brace(rect, DOWN) + g_T = left_brace.get_text("$g(t)\\big|_0^1$") + f_T = down_brace.get_text("$f(t)\\big|_0^1$") + + self.draw_curve(curve) + self.play(ShowCreation(rect)) + self.play(*map(Write, [down_brace, left_brace, f_T, g_T])) + self.dither() + self.play(FadeIn(upper_right)) + self.play( + ShowCreation( + v_lines, + submobjects = "one_at_a_time", + run_time = 2 + ), + Animation(curve), + Animation(rect) + ) + self.play(Write(equation[0])) + self.dither() + self.play(FadeIn(lower_left)) + self.play( + ShowCreation( + h_lines, + submobjects = "one_at_a_time", + run_time = 2 + ), + Animation(curve), + Animation(rect) + ) + self.play(Write(equation[1])) + self.dither() + self.play(Write(equation[2])) + self.dither() + + def draw_curve(self, curve): + lp, lnum, comma, rnum, rp = coords = TexMobject( + "\\big(f(", "t", "), g(", "t", ")\\big)" + ) + coords.highlight_by_tex("0.00", BLACK) + dot = Dot(radius = 0.1) + dot.move_to(curve.points[0]) + coords.next_to(dot, UP+RIGHT) + self.play( + ShowCreation(curve), + UpdateFromFunc( + dot, + lambda d : d.move_to(curve.points[-1]) + ), + MaintainPositionRelativeTo(coords, dot), + run_time = 5, + rate_func = None + ) + self.dither() + self.play(*map(FadeOut, [coords, dot])) + +class EndScreen(TeacherStudentsScene): + def construct(self): + self.teacher_says( + """ + See you every + other friday! + """, + target_mode = "hooray" + ) + self.change_student_modes(*["happy"]*3) + self.random_blink() + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/old_projects/wcat.py b/old_projects/wcat.py new file mode 100644 index 00000000..a48f6516 --- /dev/null +++ b/old_projects/wcat.py @@ -0,0 +1,2141 @@ +from helpers import * + +from mobject.tex_mobject import TexMobject +from mobject import Mobject +from mobject.image_mobject import ImageMobject +from mobject.vectorized_mobject import * + +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.fractals import * +from topics.number_line import * +from topics.combinatorics import * +from topics.numerals import * +from topics.three_dimensions import * +from topics.objects import * +from scene import Scene +from camera import Camera +from mobject.svg_mobject import * +from mobject.tex_mobject import * + + +class ClosedLoopScene(Scene): + CONFIG = { + "loop_anchor_points" : [ + 3*RIGHT, + 2*RIGHT+UP, + 3*RIGHT + 3*UP, + UP, + 2*UP+LEFT, + 2*LEFT + 2*UP, + 3*LEFT, + 2*LEFT+DOWN, + 3*LEFT+2*DOWN, + 2*DOWN+RIGHT, + LEFT+DOWN, + ], + "square_vertices" : [ + 2*RIGHT+UP, + 2*UP+LEFT, + 2*LEFT+DOWN, + 2*DOWN+RIGHT + ], + "rect_vertices" : [ + 0*RIGHT + 1*UP, + -1*RIGHT + 2*UP, + -3*RIGHT + 0*UP, + -2*RIGHT + -1*UP, + ], + "dot_color" : YELLOW, + "connecting_lines_color" : BLUE, + "pair_colors" : [MAROON_B, PURPLE_B], + } + def setup(self): + self.dots = VGroup() + self.connecting_lines = VGroup() + self.add_loop() + + def add_loop(self): + self.loop = self.get_default_loop() + self.add(self.loop) + + def get_default_loop(self): + loop = VMobject() + loop.set_points_smoothly( + self.loop_anchor_points + [self.loop_anchor_points[0]] + ) + return loop + + def get_square(self): + return Polygon(*self.square_vertices) + + def get_rect_vertex_dots(self, square = False): + if square: + vertices = self.square_vertices + else: + vertices = self.rect_vertices + dots = VGroup(*[Dot(v) for v in vertices]) + dots.highlight(self.dot_color) + return dots + + def get_rect_alphas(self, square = False): + #Inefficient and silly, but whatever. + dots = self.get_rect_vertex_dots(square = square) + return self.get_dot_alphas(dots) + + def add_dot(self, dot): + self.add_dots(dot) + + def add_dots(self, *dots): + self.dots.add(*dots) + self.add(self.dots) + + def add_rect_dots(self, square = False): + self.add_dots(*self.get_rect_vertex_dots(square = square)) + + def add_dots_at_alphas(self, *alphas): + self.add_dots(*[ + Dot( + self.loop.point_from_proportion(alpha), + color = self.dot_color + ) + for alpha in alphas + ]) + + def add_connecting_lines(self, cyclic = False): + if cyclic: + pairs = adjascent_pairs(self.dots) + else: + n_pairs = len(list(self.dots))/2 + pairs = zip(self.dots[:n_pairs], self.dots[n_pairs:]) + for d1, d2 in pairs: + line = Line(d1.get_center(), d2.get_center()) + line.start_dot = d1 + line.end_dot = d2 + line.update_anim = UpdateFromFunc( + line, + lambda l : l.put_start_and_end_on( + l.start_dot.get_center(), + l.end_dot.get_center() + ) + ) + line.highlight(d1.get_color()) + self.connecting_lines.add(line) + if cyclic: + self.connecting_lines.highlight(self.connecting_lines_color) + self.connecting_lines.set_stroke(width = 6) + self.add(self.connecting_lines, self.dots) + + def get_line_anims(self): + return [ + line.update_anim + for line in self.connecting_lines + ] + [Animation(self.dots)] + + def get_dot_alphas(self, dots = None, precision = 0.005): + if dots == None: + dots = self.dots + alphas = [] + alpha_range = np.arange(0, 1, precision) + loop_points = np.array(map(self.loop.point_from_proportion, alpha_range)) + for dot in dots: + vects = loop_points - dot.get_center() + norms = np.apply_along_axis(np.linalg.norm, 1, vects) + index = np.argmin(norms) + alphas.append(alpha_range[index]) + return alphas + + def let_dots_wonder(self, run_time = 5, random_seed = None, added_anims = []): + if random_seed is not None: + np.random.seed(random_seed) + start_alphas = self.get_dot_alphas() + alpha_rates = 0.05 + 0.1*np.random.random(len(list(self.dots))) + def generate_rate_func(start, rate): + return lambda t : (start + t*rate*run_time)%1 + anims = [ + MoveAlongPath( + dot, + self.loop, + rate_func = generate_rate_func(start, rate) + ) + for dot, start, rate in zip(self.dots, start_alphas, alpha_rates) + ] + anims += self.get_line_anims() + anims += added_anims + self.play(*anims, run_time = run_time) + + def move_dots_to_alphas(self, alphas, run_time = 3): + assert(len(alphas) == len(list(self.dots))) + start_alphas = self.get_dot_alphas() + def generate_rate_func(start_alpha, alpha): + return lambda t : interpolate(start_alpha, alpha, smooth(t)) + anims = [ + MoveAlongPath( + dot, self.loop, + rate_func = generate_rate_func(sa, a), + run_time = run_time, + ) + for dot, sa, a in zip(self.dots, start_alphas, alphas) + ] + anims += self.get_line_anims() + self.play(*anims) + + def transform_loop(self, target_loop, added_anims = [], **kwargs): + alphas = self.get_dot_alphas() + dot_anims = [] + for dot, alpha in zip(self.dots, alphas): + dot.generate_target() + dot.target.move_to(target_loop.point_from_proportion(alpha)) + dot_anims.append(MoveToTarget(dot)) + self.play( + Transform(self.loop, target_loop), + *dot_anims + self.get_line_anims() + added_anims, + **kwargs + ) + + def highlight_dots_by_pair(self): + n_pairs = len(list(self.dots))/2 + for d1, d2, c in zip(self.dots[:n_pairs], self.dots[n_pairs:], self.pair_colors): + VGroup(d1, d2).highlight(c) + + def find_square(self): + alpha_quads = list(it.combinations( + np.arange(0, 1, 0.02) , 4 + )) + quads = np.array([ + [ + self.loop.point_from_proportion(alpha) + for alpha in quad + ] + for quad in alpha_quads + ]) + scores = self.square_scores(quads) + index = np.argmin(scores) + return quads[index] + + def square_scores(self, all_quads): + midpoint_diffs = np.apply_along_axis( + np.linalg.norm, 1, + 0.5*(all_quads[:,0] + all_quads[:,2]) - 0.5*(all_quads[:,1] + all_quads[:,3]) + ) + vects1 = all_quads[:,0] - all_quads[:,2] + vects2 = all_quads[:,1] - all_quads[:,3] + distances1 = np.apply_along_axis(np.linalg.norm, 1, vects1) + distances2 = np.apply_along_axis(np.linalg.norm, 1, vects2) + distance_diffs = np.abs(distances1 - distances2) + midpoint_diffs /= distances1 + distance_diffs /= distances2 + + buffed_d1s = np.repeat(distances1, 3).reshape(vects1.shape) + buffed_d2s = np.repeat(distances2, 3).reshape(vects2.shape) + unit_v1s = vects1/buffed_d1s + unit_v2s = vects2/buffed_d2s + dots = np.abs(unit_v1s[:,0]*unit_v2s[:,0] + unit_v1s[:,1]*unit_v2s[:,1] + unit_v1s[:,2]*unit_v2s[:,2]) + + return midpoint_diffs + distance_diffs + dots + + +############################# + +class Introduction(TeacherStudentsScene): + def construct(self): + self.play(self.get_teacher().change_mode, "hooray") + self.random_blink() + self.teacher_says("") + for pi in self.get_students(): + pi.generate_target() + pi.target.change_mode("happy") + pi.target.look_at(self.get_teacher().bubble) + self.play(*map(MoveToTarget, self.get_students())) + self.random_blink(3) + self.teacher_says( + "Here's why \\\\ I'm excited...", + target_mode = "hooray" + ) + for pi in self.get_students(): + pi.target.look_at(self.get_teacher().eyes) + self.play(*map(MoveToTarget, self.get_students())) + self.dither() + +class WhenIWasAKid(TeacherStudentsScene): + def construct(self): + children = self.get_children() + speaker = self.get_speaker() + + self.prepare_everyone(children, speaker) + self.state_excitement(children, speaker) + self.students = children + self.teacher = speaker + self.run_class() + self.grow_up() + + def state_excitement(self, children, speaker): + self.teacher_says( + """ + Here's why + I'm excited! + """, + target_mode = "hooray" + ) + self.change_student_modes(*["happy"]*3) + self.dither() + + speaker.look_at(children) + me = children[-1] + self.play( + FadeOut(self.get_students()), + FadeOut(self.get_teacher().bubble), + FadeOut(self.get_teacher().bubble.content), + Transform(self.get_teacher(), me) + ) + self.remove(self.get_teacher()) + self.add(me) + self.play(*map(FadeIn, children[:-1] + [speaker])) + self.random_blink() + + def run_class(self): + children = self.students + speaker = self.teacher + title = TextMobject("Topology") + title.to_edge(UP) + pi1, pi2, pi3, me = children + + self.random_blink() + self.teacher_says( + """ + Math! Excitement! + You are the future! + """, + target_mode = "hooray" + ) + self.play( + pi1.look_at, pi2.eyes, + pi1.change_mode, "erm", + pi2.look_at, pi1.eyes, + pi2.change_mode, "surprised", + ) + self.play( + pi3.look_at, me.eyes, + pi3.change_mode, "sassy", + me.look_at, pi3.eyes, + ) + self.random_blink(2) + + self.play( + self.teacher.change_mode, "speaking", + FadeOut(self.teacher.bubble), + FadeOut(self.teacher.bubble.content), + ) + self.play(Write(title)) + self.random_blink() + + self.play(pi1.change_mode, "raise_right_hand") + self.random_blink() + self.play( + pi2.change_mode, "confused", + pi3.change_mode, "happy", + pi2.look_at, pi3.eyes, + pi3.look_at, pi2.eyes, + ) + self.random_blink() + self.play(me.change_mode, "pondering") + self.dither() + self.random_blink(2) + self.play(pi1.change_mode, "raise_left_hand") + self.dither() + self.play(pi2.change_mode, "erm") + self.random_blink() + self.student_says( + "How is this math?", + student_index = -1, + target_mode = "pleading", + width = 5, + height = 3, + direction = RIGHT + ) + self.play( + pi1.change_mode, "pondering", + pi2.change_mode, "pondering", + pi3.change_mode, "pondering", + ) + self.play(speaker.change_mode, "pondering") + self.random_blink() + + def grow_up(self): + me = self.students[-1] + self.students.remove(me) + morty = Mortimer(mode = "pondering") + morty.flip() + morty.move_to(me, aligned_edge = DOWN) + morty.to_edge(LEFT) + morty.look(RIGHT) + + self.play( + Transform(me, morty), + *map(FadeOut, [ + self.students, self.teacher, + me.bubble, me.bubble.content + ]) + ) + self.remove(me) + self.add(morty) + self.play(Blink(morty)) + self.dither() + self.play(morty.change_mode, "hooray") + self.dither() + + + def prepare_everyone(self, children, speaker): + self.everyone = list(children) + [speaker] + for pi in self.everyone: + pi.bubble = None + + def get_children(self): + colors = [MAROON_E, YELLOW_D, PINK, GREY_BROWN] + children = VGroup(*[ + BabyPiCreature(color = color) + for color in colors + ]) + children.arrange_submobjects(RIGHT) + children.to_edge(DOWN, buff = LARGE_BUFF) + children.to_edge(LEFT) + return children + + def get_speaker(self): + speaker = Mathematician(mode = "happy") + speaker.flip() + speaker.to_edge(DOWN, buff = LARGE_BUFF) + speaker.to_edge(RIGHT) + return speaker + + def get_everyone(self): + if hasattr(self, "everyone"): + return self.everyone + else: + return TeacherStudentsScene.get_everyone(self) + +class FormingTheMobiusStrip(Scene): + def construct(self): + pass + +class DrawLineOnMobiusStrip(Scene): + def construct(self): + pass + +class MugIntoTorus(Scene): + def construct(self): + pass + +class DefineInscribedSquareProblem(ClosedLoopScene): + def construct(self): + self.draw_loop() + self.cycle_through_shapes() + self.ask_about_rectangles() + + def draw_loop(self): + self.title = TextMobject("Inscribed", "square", "problem") + self.title.to_edge(UP) + + #Draw loop + self.remove(self.loop) + self.play(Write(self.title)) + self.dither() + self.play(ShowCreation( + self.loop, + run_time = 5, + rate_func = None + )) + self.dither() + self.add_rect_dots(square = True) + self.play(ShowCreation(self.dots, run_time = 2)) + self.dither() + self.add_connecting_lines(cyclic = True) + self.play( + ShowCreation( + self.connecting_lines, + submobject_mode = "all_at_once", + run_time = 2 + ), + Animation(self.dots) + ) + self.dither(2) + + def cycle_through_shapes(self): + circle = Circle(radius = 2.5, color = WHITE) + ellipse = circle.copy() + ellipse.stretch(1.5, 0) + ellipse.stretch(0.7, 1) + ellipse.rotate(-np.pi/2) + ellipse.scale_to_fit_height(4) + pi_loop = TexMobject("\\pi")[0] + pi_loop.set_fill(opacity = 0) + pi_loop.set_stroke( + color = WHITE, + width = DEFAULT_POINT_THICKNESS + ) + pi_loop.scale_to_fit_height(4) + randy = Randolph() + randy.look(DOWN) + randy.scale_to_fit_width(pi_loop.get_width()) + randy.move_to(pi_loop, aligned_edge = DOWN) + randy.body.set_fill(opacity = 0) + randy.mouth.set_stroke(width = 0) + + self.transform_loop(circle) + self.remove(self.loop) + self.loop = circle + self.add(self.loop, self.connecting_lines, self.dots) + self.dither() + odd_eigths = np.linspace(1./8, 7./8, 4) + self.move_dots_to_alphas(odd_eigths) + self.dither() + for nudge in 0.1, -0.1, 0: + self.move_dots_to_alphas(odd_eigths+nudge) + self.dither() + self.transform_loop(ellipse) + self.dither() + nudge = 0.055 + self.move_dots_to_alphas( + odd_eigths + [nudge, -nudge, nudge, -nudge] + ) + self.dither(2) + self.transform_loop(pi_loop) + self.let_dots_wonder() + randy_anims = [ + FadeIn(randy), + Animation(randy), + Blink(randy), + Animation(randy), + Blink(randy), + Animation(randy), + Blink(randy, rate_func = smooth) + ] + for anim in randy_anims: + self.let_dots_wonder( + run_time = 1.5, + random_seed = 0, + added_anims = [anim] + ) + self.remove(randy) + self.transform_loop(self.get_default_loop()) + + def ask_about_rectangles(self): + morty = Mortimer() + morty.next_to(ORIGIN, DOWN) + morty.to_edge(RIGHT) + + new_title = TextMobject("Inscribed", "rectangle", "problem") + new_title.highlight_by_tex("rectangle", YELLOW) + new_title.to_edge(UP) + rect_dots = self.get_rect_vertex_dots() + rect_alphas = self.get_dot_alphas(rect_dots) + + self.play(FadeIn(morty)) + self.play(morty.change_mode, "speaking") + self.play(Transform(self.title, new_title)) + self.move_dots_to_alphas(rect_alphas) + self.dither() + self.play(morty.change_mode, "hooray") + self.play(Blink(morty)) + self.dither() + self.play(FadeOut(self.connecting_lines)) + self.connecting_lines = VGroup() + self.play(morty.change_mode, "plain") + + dot_pairs = [ + VGroup(self.dots[i], self.dots[j]) + for i, j in (0, 2), (1, 3) + ] + pair_colors = MAROON_B, PURPLE_B + diag_lines = [ + Line(d1.get_center(), d2.get_center(), color = c) + for (d1, d2), c in zip(dot_pairs, pair_colors) + ] + + for pair, line in zip(dot_pairs, diag_lines): + self.play( + FadeIn(line), + pair.highlight, line.get_color(), + ) + +class RectangleProperties(Scene): + def construct(self): + rect = Rectangle(color = BLUE) + vertex_dots = VGroup(*[ + Dot(anchor, color = YELLOW) + for anchor in rect.get_anchors_and_handles()[0] + ]) + dot_pairs = [ + VGroup(vertex_dots[i], vertex_dots[j]) + for i, j in (0, 2), (1, 3) + ] + colors = [MAROON_B, PURPLE_B] + diag_lines = [ + Line(d1.get_center(), d2.get_center(), color = c) + for (d1, d2), c in zip(dot_pairs, colors) + ] + braces = [Brace(rect).next_to(ORIGIN, DOWN) for x in range(2)] + for brace, line in zip(braces, diag_lines): + brace.stretch_to_fit_width(line.get_length()) + brace.rotate(line.get_angle()) + a, b, c, d = labels = VGroup(*[ + TexMobject(s).next_to(dot, dot.get_center(), buff = SMALL_BUFF) + for s, dot in zip("abcd", vertex_dots) + ]) + midpoint = Dot(ORIGIN, color = RED) + + + self.play(ShowCreation(rect)) + self.dither() + self.play( + ShowCreation(vertex_dots), + Write(labels) + ) + self.dither() + mob_lists = [ + (a, c, dot_pairs[0]), + (b, d, dot_pairs[1]), + ] + for color, mob_list in zip(colors, mob_lists): + self.play(*[ + ApplyMethod(mob.highlight, color) + for mob in mob_list + ]) + self.dither() + for line, brace in zip(diag_lines, braces): + self.play( + ShowCreation(line), + GrowFromCenter(brace) + ) + self.dither() + self.play(FadeOut(brace)) + self.play(FadeIn(midpoint)) + self.dither() + +class PairOfPairBecomeRectangle(Scene): + def construct(self): + dots = VGroup( + Dot(4*RIGHT+0.5*DOWN, color = MAROON_B), + Dot(5*RIGHT+3*UP, color = MAROON_B), + Dot(LEFT+0.1*DOWN, color = PURPLE_B), + Dot(2*LEFT+UP, color = PURPLE_B) + ) + labels = VGroup() + for dot, char in zip(dots, "acbd"): + label = TexMobject(char) + y_coord = dot.get_center()[1] + label.next_to(dot, np.sign(dot.get_center()[1])*UP) + label.highlight(dot.get_color()) + labels.add(label) + lines = [ + Line( + dots[i].get_center(), + dots[j].get_center(), + color = dots[i].get_color() + ) + for i, j in (0, 1), (2, 3) + ] + groups = [ + VGroup(dots[0], dots[1], labels[0], labels[1], lines[0]), + VGroup(dots[2], dots[3], labels[2], labels[3], lines[1]), + ] + midpoint = Dot(LEFT, color = RED) + + words = VGroup(*map(TextMobject, [ + "Common midpoint", + "Same distance apart", + "$\\Downarrow$", + "Rectangle", + ])) + words.arrange_submobjects(DOWN) + words.to_edge(RIGHT) + words[-1].highlight(BLUE) + + self.play( + ShowCreation(dots), + Write(labels) + ) + self.play(*map(ShowCreation, lines)) + self.dither() + self.play(*[ + ApplyMethod( + group.shift, + -group[-1].get_center()+midpoint.get_center() + ) + for group in groups + ]) + self.play( + ShowCreation(midpoint), + Write(words[0]) + ) + factor = lines[0].get_length()/lines[1].get_length() + grower = groups[1].copy() + new_line = grower[-1] + new_line.scale_in_place(factor) + grower[0].move_to(new_line.get_start()) + grower[2].next_to(grower[0], DOWN) + grower[1].move_to(new_line.get_end()) + grower[3].next_to(grower[1], UP) + + self.play(Transform(groups[1], grower)) + self.play(Write(words[1])) + self.dither() + + rectangle = Polygon(*[ + dots[i].get_center() + for i in 0, 2, 1, 3 + ]) + rectangle.highlight(BLUE) + self.play( + ShowCreation(rectangle), + Animation(dots) + ) + self.play(*map(Write, words[2:])) + self.dither() + +class SearchForRectangleOnLoop(ClosedLoopScene): + def construct(self): + self.add_dots_at_alphas(*np.linspace(0.2, 0.8, 4)) + self.highlight_dots_by_pair() + rect_alphas = self.get_rect_alphas() + + self.play(ShowCreation(self.dots)) + self.add_connecting_lines() + self.play(ShowCreation(self.connecting_lines)) + self.let_dots_wonder(2) + self.move_dots_to_alphas(rect_alphas) + + midpoint = Dot( + center_of_mass([d.get_center() for d in self.dots]), + color = RED + ) + self.play(ShowCreation(midpoint)) + self.dither() + angles = [line.get_angle() for line in self.connecting_lines] + angle_mean = np.mean(angles) + self.play( + *[ + ApplyMethod(line.rotate_in_place, angle_mean-angle) + for line, angle in zip(self.connecting_lines, angles) + ] + [Animation(midpoint)], + rate_func = there_and_back + ) + self.add(self.connecting_lines.copy(), midpoint) + self.connecting_lines = VGroup() + self.dither() + self.add_connecting_lines(cyclic = True) + self.play( + ShowCreation(self.connecting_lines), + Animation(self.dots) + ) + self.dither() + +class DeclareFunction(ClosedLoopScene): + def construct(self): + self.add_dots_at_alphas(0.2, 0.8) + self.highlight_dots_by_pair() + self.add_connecting_lines() + VGroup( + self.loop, self.dots, self.connecting_lines + ).scale(0.7).to_edge(LEFT).shift(DOWN) + arrow = Arrow(LEFT, RIGHT).next_to(self.loop) + self.add(arrow) + + self.add_tex() + self.let_dots_wonder(10) + + def add_tex(self): + tex = TexMobject("f", "(A, B)", "=", "(x, y, z)") + tex.to_edge(UP) + tex.shift(LEFT) + + ab_brace = Brace(tex[1]) + xyz_brace = Brace(tex[-1], RIGHT) + ab_brace.add(ab_brace.get_text("Pair of points on the loop")) + xyz_brace.add(xyz_brace.get_text("Point in 3d space")) + ab_brace.gradient_highlight(MAROON_B, PURPLE_B) + xyz_brace.highlight(BLUE) + + self.add(tex) + self.play(Write(ab_brace)) + self.dither() + self.play(Write(xyz_brace)) + self.dither() + +class DefinePairTo3dFunction(Scene): + def construct(self): + pass + +class LabelMidpoint(Scene): + def construct(self): + words = TextMobject("Midpoint $M$") + words.highlight(RED) + words.scale(2) + self.play(Write(words, run_time = 1)) + self.dither() + +class LabelDistance(Scene): + def construct(self): + words = TextMobject("Distance $d$") + words.highlight(MAROON_B) + words.scale(2) + self.play(Write(words, run_time = 1)) + self.dither() + +class DrawingOneLineOfTheSurface(Scene): + def construct(self): + pass + +class FunctionSurface(Scene): + def construct(self): + pass + +class PointPairApprocahingEachother3D(Scene): + def construct(self): + pass + +class InputPairToFunction(Scene): + def construct(self): + tex = TexMobject("f(X, X)", "=X") + tex.highlight_by_tex("=X", BLUE) + tex.scale(2) + self.play(Write(tex[0])) + self.dither(2) + self.play(Write(tex[1])) + self.dither(2) + +class WigglePairUnderSurface(Scene): + def construct(self): + pass + +class WriteContinuous(Scene): + def construct(self): + self.play(Write(TextMobject("Continuous").scale(2))) + self.dither(2) + +class DistinctPairCollisionOnSurface(Scene): + def construct(self): + pass + +class PairsOfPointsOnLoop(ClosedLoopScene): + def construct(self): + self.add_dots_at_alphas(0.2, 0.5) + self.dots.highlight(MAROON_B) + self.add_connecting_lines() + self.let_dots_wonder(run_time = 10) + +class PairOfRealsToPlane(Scene): + def construct(self): + r1, r2 = numbers = -3, 2 + colors = GREEN, RED + dot1, dot2 = dots = VGroup(*[Dot(color = c) for c in colors]) + for dot, number in zip(dots, numbers): + dot.move_to(number*RIGHT) + pair_label = TexMobject("(", str(r1), ",", str(r2), ")") + for number, color in zip(numbers, colors): + pair_label.highlight_by_tex(str(number), color) + pair_label.next_to(dots, UP, buff = 2) + arrows = VGroup(*[ + Arrow(pair_label[i], dot, color = dot.get_color()) + for i, dot in zip([1, 3], dots) + ]) + two_d_point = Dot(r1*RIGHT + r2*UP, color = YELLOW) + pair_label.add_background_rectangle() + + x_axis = NumberLine(color = BLUE) + y_axis = NumberLine(color = BLUE) + plane = NumberPlane().fade() + + self.add(x_axis, y_axis, dots, pair_label) + self.play(ShowCreation(arrows, run_time = 2)) + self.dither() + self.play( + pair_label.next_to, two_d_point, UP+LEFT, SMALL_BUFF, + Rotate(y_axis, np.pi/2), + Rotate(dot2, np.pi/2), + FadeOut(arrows) + ) + lines = VGroup(*[ + DashedLine(dot, two_d_point, color = dot.get_color()) + for dot in dots + ]) + self.play(*map(ShowCreation, lines)) + self.play(ShowCreation(two_d_point)) + everything = VGroup(*self.get_mobjects()) + self.play( + FadeIn(plane), + Animation(everything), + Animation(dot2) + ) + self.dither() + +class SeekSurfaceForPairs(ClosedLoopScene): + def construct(self): + self.loop.to_edge(LEFT) + self.add_dots_at_alphas(0.2, 0.3) + self.highlight_dots_by_pair() + self.add_connecting_lines() + + arrow = Arrow(LEFT, RIGHT).next_to(self.loop) + words = TextMobject("Some 2d surface") + words.next_to(arrow, RIGHT) + + anims = [ + ShowCreation(arrow), + Write(words) + ] + for anim in anims: + self.let_dots_wonder( + random_seed = 1, + added_anims = [anim], + run_time = anim.run_time + ) + self.let_dots_wonder(random_seed = 1, run_time = 10) + +class AskAbouPairType(TeacherStudentsScene): + def construct(self): + self.student_says(""" + Do you mean ordered + or unordered pairs? + """) + self.play(*[ + ApplyMethod(self.get_students()[i].change_mode, "confused") + for i in 0, 2 + ]) + self.random_blink(3) + +class DefineOrderedPair(ClosedLoopScene): + def construct(self): + title = TextMobject("Ordered pairs") + title.to_edge(UP) + subtitle = TexMobject( + "(", "a", ",", "b", ")", + "\\ne", + "(", "b", ",", "a", ")" + ) + labels_start = VGroup(subtitle[1], subtitle[3]) + labels_end = VGroup(subtitle[9], subtitle[7]) + subtitle.next_to(title, DOWN) + colors = GREEN, RED + for char, color in zip("ab", colors): + subtitle.highlight_by_tex(char, color) + self.loop.next_to(subtitle, DOWN) + self.add(title, subtitle) + + self.add_dots_at_alphas(0.5, 0.6) + dots = self.dots + for dot, color, char in zip(dots, colors, "ab"): + dot.highlight(color) + label = TexMobject(char) + label.highlight(color) + label.next_to(dot, RIGHT, buff = SMALL_BUFF) + dot.label = label + self.dots[1].label.shift(0.3*UP) + first = TextMobject("First") + first.next_to(self.dots[0], UP+2*LEFT, LARGE_BUFF) + arrow = Arrow(first.get_bottom(), self.dots[0], color = GREEN) + + self.dither() + self.play(*[ + Transform(label.copy(), dot.label) + for label, dot in zip(labels_start, dots) + ]) + self.remove(*self.get_mobjects_from_last_animation()) + self.add(*[d.label for d in dots]) + self.dither() + self.play( + Write(first), + ShowCreation(arrow) + ) + self.dither() + +class DefineUnorderedPair(ClosedLoopScene): + def construct(self): + title = TextMobject("Unordered pairs") + title.to_edge(UP) + subtitle = TexMobject( + "\\{a,b\\}", + "=", + "\\{b,a\\}", + ) + subtitle.next_to(title, DOWN) + for char in "ab": + subtitle.highlight_by_tex(char, PURPLE_B) + self.loop.next_to(subtitle, DOWN) + self.add(title, subtitle) + + self.add_dots_at_alphas(0.5, 0.6) + dots = self.dots + dots.highlight(PURPLE_B) + + labels = VGroup(*[subtitle[i].copy() for i in 0, 2]) + for label, vect in zip(labels, [LEFT, RIGHT]): + label.next_to(dots, vect, LARGE_BUFF) + arrows = [ + Arrow(*pair, color = PURPLE_B) + for pair in it.product(labels, dots) + ] + arrow_pairs = [VGroup(*arrows[:2]), VGroup(*arrows[2:])] + + for label, arrow_pair in zip(labels, arrow_pairs): + self.play(*map(FadeIn, [label, arrow_pair])) + self.dither() + for x in range(2): + self.play( + dots[0].move_to, dots[1], + dots[1].move_to, dots[0], + path_arc = np.pi/2 + ) + self.dither() + +class BeginWithOrdered(TeacherStudentsScene): + def construct(self): + self.teacher_says(""" + One must know order + before he can ignore it. + """) + self.random_blink(3) + +class DeformToInterval(ClosedLoopScene): + def construct(self): + interval = UnitInterval(color = WHITE) + interval.shift(2*DOWN) + numbers = interval.get_number_mobjects(0, 1) + line = Line(interval.get_left(), interval.get_right()) + line.insert_n_anchor_points(self.loop.get_num_anchor_points()) + line.make_smooth() + + self.loop.scale(0.7) + self.loop.to_edge(UP) + original_loop = self.loop.copy() + cut_loop = self.loop.copy() + cut_loop.points[0] += 0.3*(UP+RIGHT) + cut_loop.points[-1] += 0.3*(DOWN+RIGHT) + + #Unwrap loop + self.transform_loop(cut_loop, path_arc = np.pi) + self.dither() + self.transform_loop( + line, + run_time = 3, + path_arc = np.pi/2 + ) + self.dither() + self.play(ShowCreation(interval)) + self.play(Write(numbers)) + self.dither() + + #Follow points + self.loop = original_loop.copy() + self.play(FadeIn(self.loop)) + self.add(original_loop) + self.add_dots_at_alphas(*np.linspace(0, 1, 20)) + self.dots.gradient_highlight(BLUE, MAROON_C, BLUE) + dot_at_1 = self.dots[-1] + dot_at_1.generate_target() + dot_at_1.target.move_to(interval.get_right()) + dots_copy = self.dots.copy() + fading_dots = VGroup(*list(self.dots)+list(dots_copy)) + end_dots = VGroup( + self.dots[0], self.dots[-1], + dots_copy[0], dots_copy[-1] + ) + fading_dots.remove(*end_dots) + + self.play(Write(self.dots)) + self.add(dots_copy) + self.dither() + self.transform_loop( + line, + added_anims = [MoveToTarget(dot_at_1)], + run_time = 3 + ) + self.dither() + self.loop = original_loop + self.dots = dots_copy + dot_at_1 = self.dots[-1] + dot_at_1.target.move_to(cut_loop.points[-1]) + self.transform_loop( + cut_loop, + added_anims = [MoveToTarget(dot_at_1)] + ) + self.dither() + fading_dots.generate_target() + fading_dots.target.set_fill(opacity = 0.3) + self.play(MoveToTarget(fading_dots)) + self.play( + end_dots.shift, 0.2*UP, + rate_func = wiggle + ) + self.dither() + +class RepresentPairInUnitSquare(ClosedLoopScene): + def construct(self): + interval = UnitInterval(color = WHITE) + interval.shift(2.5*DOWN) + interval.shift(LEFT) + numbers = interval.get_number_mobjects(0, 1) + line = Line(interval.get_left(), interval.get_right()) + line.insert_n_anchor_points(self.loop.get_num_anchor_points()) + line.make_smooth() + vert_interval = interval.copy() + square = Square() + square.scale_to_fit_width(interval.get_width()) + square.set_stroke(width = 0) + square.set_fill(color = BLUE, opacity = 0.3) + square.move_to( + interval.get_left(), + aligned_edge = DOWN+LEFT + ) + + right_words = VGroup(*[ + TextMobject("Pair of\\\\ loop points"), + TexMobject("\\Downarrow"), + TextMobject("Point in \\\\ unit square") + ]) + right_words.arrange_submobjects(DOWN) + right_words.to_edge(RIGHT) + + dot_coords = (0.3, 0.7) + self.loop.scale(0.7) + self.loop.to_edge(UP) + self.add_dots_at_alphas(*dot_coords) + self.dots.gradient_highlight(GREEN, RED) + + self.play( + Write(self.dots), + Write(right_words[0]) + ) + self.dither() + self.transform_loop(line) + self.play( + ShowCreation(interval), + Write(numbers), + Animation(self.dots) + ) + self.dither() + self.play(*[ + Rotate(mob, np.pi/2, about_point = interval.get_left()) + for mob in vert_interval, self.dots[1] + ]) + + #Find interior point + point = self.dots[0].get_center()[0]*RIGHT + point += self.dots[1].get_center()[1]*UP + inner_dot = Dot(point, color = YELLOW) + dashed_lines = VGroup(*[ + DashedLine(dot, inner_dot, color = dot.get_color()) + for dot in self.dots + ]) + self.play(ShowCreation(dashed_lines)) + self.play(ShowCreation(inner_dot)) + self.play( + FadeIn(square), + Animation(self.dots), + *map(Write, right_words[1:]) + ) + self.dither() + + #Shift point in square + + movers = list(dashed_lines)+list(self.dots)+[inner_dot] + for mob in movers: + mob.generate_target() + shift_vals = [ + RIGHT+DOWN, + LEFT+DOWN, + LEFT+2*UP, + 3*DOWN, + 2*RIGHT+UP, + RIGHT+UP, + 3*LEFT+3*DOWN + ] + for shift_val in shift_vals: + inner_dot.target.shift(shift_val) + self.dots[0].target.shift(shift_val[0]*RIGHT) + self.dots[1].target.shift(shift_val[1]*UP) + for line, dot in zip(dashed_lines, self.dots): + line.target.put_start_and_end_on( + dot.target.get_center(), + inner_dot.target.get_center() + ) + self.play(*map(MoveToTarget, movers)) + self.dither() + self.play(*map(FadeOut, [dashed_lines, self.dots])) + +class EdgesOfSquare(Scene): + def construct(self): + square = self.add_square() + x_edges, y_edges = self.get_edges(square) + label_groups = self.get_coordinate_labels(square) + arrow_groups = self.get_arrows(x_edges, y_edges) + + for edge in list(x_edges) + list(y_edges): + self.play(ShowCreation(edge)) + self.dither() + for label_group in label_groups: + for label in label_group[:3]: + self.play(FadeIn(label)) + self.dither() + self.play(Write(VGroup(*label_group[3:]))) + self.dither() + self.play(FadeOut(VGroup(*label_groups))) + for arrows in arrow_groups: + self.play(ShowCreation(arrows, run_time = 2)) + self.dither() + self.play(*[ + ApplyMethod( + n.next_to, + square.get_corner(vect+LEFT), + LEFT, + MED_BUFF, + path_arc = np.pi/2 + ) + for n, vect in zip(self.numbers, [DOWN, UP]) + ]) + self.dither() + + def add_square(self): + interval = UnitInterval(color = WHITE) + interval.shift(2.5*DOWN) + bottom_left = interval.get_left() + for tick in interval.tick_marks: + height = tick.get_height() + tick.scale_in_place(0.5) + tick.shift(height*DOWN/4.) + self.numbers = interval.get_number_mobjects(0, 1) + vert_interval = interval.copy() + vert_interval.rotate(np.pi, axis = UP+RIGHT, about_point = bottom_left) + square = Square() + square.scale_to_fit_width(interval.get_width()) + square.set_stroke(width = 0) + square.set_fill(color = BLUE, opacity = 0.3) + square.move_to( + bottom_left, + aligned_edge = DOWN+LEFT + ) + self.add(interval, self.numbers, vert_interval, square) + return square + + def get_edges(self, square): + y_edges = VGroup(*[ + Line( + square.get_corner(vect+LEFT), + square.get_corner(vect+RIGHT), + ) + for vect in DOWN, UP + ]) + y_edges.highlight(BLUE) + x_edges = VGroup(*[ + Line( + square.get_corner(vect+DOWN), + square.get_corner(vect+UP), + ) + for vect in LEFT, RIGHT + ]) + x_edges.highlight(MAROON_B) + return x_edges, y_edges + + def get_coordinate_labels(self, square): + alpha_range = np.arange(0, 1.1, 0.1) + dot_groups = [ + VGroup(*[ + Dot(interpolate( + square.get_corner(DOWN+vect), + square.get_corner(UP+vect), + alpha + )) + for alpha in alpha_range + ]) + for vect in LEFT, RIGHT + ] + for group in dot_groups: + group.gradient_highlight(YELLOW, PURPLE_B) + label_groups = [ + VGroup(*[ + TexMobject("(%s, %s)"%(a, b)).scale(0.7) + for b in alpha_range + ]) + for a in 0, 1 + ] + for dot_group, label_group in zip(dot_groups, label_groups): + for dot, label in zip(dot_group, label_group): + label[1].highlight(MAROON_B) + label.next_to(dot, RIGHT*np.sign(dot.get_center()[0])) + label.add(dot) + return label_groups + + def get_arrows(self, x_edges, y_edges): + alpha_range = np.linspace(0, 1, 4) + return [ + VGroup(*[ + VGroup(*[ + Arrow( + edge.point_from_proportion(a1), + edge.point_from_proportion(a2), + buff = 0 + ) + for a1, a2 in zip(alpha_range, alpha_range[1:]) + ]) + for edge in edges + ]).highlight(edges.get_color()) + for edges in x_edges, y_edges + ] + +class EndpointsGluedTogether(ClosedLoopScene): + def construct(self): + interval = UnitInterval(color = WHITE) + interval.shift(2*DOWN) + numbers = interval.get_number_mobjects(0, 1) + line = Line(interval.get_left(), interval.get_right()) + line.insert_n_anchor_points(self.loop.get_num_anchor_points()) + line.make_smooth() + + self.loop.scale(0.7) + self.loop.to_edge(UP) + original_loop = self.loop + self.remove(original_loop) + + self.loop = line + dots = VGroup(*[ + Dot(line.get_critical_point(vect)) + for vect in LEFT, RIGHT + ]) + dots.highlight(BLUE) + + self.add(interval, dots) + self.play(dots.rotate_in_place, np.pi/20, rate_func = wiggle) + self.dither() + self.transform_loop( + original_loop, + added_anims = [ + ApplyMethod(dot.move_to, original_loop.points[0]) + for dot in dots + ], + run_time = 3 + ) + self.dither() + +class WrapUpToTorus(Scene): + def construct(self): + pass + +class TorusPlaneAnalogy(ClosedLoopScene): + def construct(self): + top_arrow = DoubleArrow(LEFT, RIGHT) + top_arrow.to_edge(UP, buff = 2*LARGE_BUFF) + single_pointed_top_arrow = Arrow(LEFT, RIGHT) + single_pointed_top_arrow.to_edge(UP, buff = 2*LARGE_BUFF) + low_arrow = DoubleArrow(LEFT, RIGHT).shift(2*DOWN) + self.loop.scale(0.5) + self.loop.next_to(top_arrow, RIGHT) + self.loop.shift_onto_screen() + self.add_dots_at_alphas(0.3, 0.5) + self.dots.gradient_highlight(GREEN, RED) + + plane = NumberPlane() + plane.scale(0.3).next_to(low_arrow, LEFT) + number_line = NumberLine() + number_line.scale(0.3) + number_line.next_to(low_arrow, RIGHT) + number_line.add( + Dot(number_line.number_to_point(3), color = GREEN), + Dot(number_line.number_to_point(-2), color = RED), + ) + + self.dither() + self.play(ShowCreation(single_pointed_top_arrow)) + self.dither() + self.play(ShowCreation(top_arrow)) + self.dither() + self.play(ShowCreation(plane)) + self.play(ShowCreation(low_arrow)) + self.play(ShowCreation(number_line)) + self.dither() + +class WigglingPairOfPoints(ClosedLoopScene): + def construct(self): + alpha_pairs = [ + (0.4, 0.6), + (0.42, 0.62), + ] + self.add_dots_at_alphas(*alpha_pairs[-1]) + self.add_connecting_lines() + self.dots.gradient_highlight(GREEN, RED) + self.connecting_lines.highlight(YELLOW) + for x, pair in zip(range(20), it.cycle(alpha_pairs)): + self.move_dots_to_alphas(pair, run_time = 0.3) + + +class WigglingTorusPoint(Scene): + def construct(self): + pass + +class WhatAboutUnordered(TeacherStudentsScene): + def construct(self): + self.student_says( + "What about \\\\ unordered pairs?" + ) + self.play(self.get_teacher().change_mode, "pondering") + self.random_blink(2) + +class TrivialPairCollision(ClosedLoopScene): + def construct(self): + self.loop.to_edge(RIGHT) + self.add_dots_at_alphas(0.35, 0.55) + self.dots.gradient_highlight(BLUE, YELLOW) + a, b = self.dots + a_label = TexMobject("a").next_to(a, RIGHT) + a_label.highlight(a.get_color()) + b_label = TexMobject("b").next_to(b, LEFT) + b_label.highlight(b.get_color()) + line = Line( + a.get_corner(DOWN+LEFT), + b.get_corner(UP+RIGHT), + color = MAROON_B + ) + midpoint = Dot(self.dots.get_center(), color = RED) + randy = Randolph(mode = "pondering") + randy.next_to(self.loop, LEFT, aligned_edge = DOWN) + randy.look_at(b) + self.add(randy) + + for label in a_label, b_label: + self.play( + Write(label, run_time = 1), + randy.look_at, label + ) + self.play(Blink(randy)) + self.dither() + swappers = [a, b, a_label, b_label] + for mob in swappers: + mob.save_state() + self.play( + a.move_to, b, + b.move_to, a, + a_label.next_to, b, LEFT, + b_label.next_to, a, RIGHT, + randy.look_at, a, + path_arc = np.pi + ) + self.play(ShowCreation(midpoint)) + self.play(ShowCreation(line), Animation(midpoint)) + self.play(randy.change_mode, "erm", randy.look_at, b) + self.play( + randy.look_at, a, + *[m.restore for m in swappers], + path_arc = -np.pi + ) + self.play(Blink(randy)) + self.dither() + +class NotHelpful(Scene): + def construct(self): + morty = Mortimer() + morty.next_to(ORIGIN, DOWN) + bubble = morty.get_bubble("speech", width = 4, height = 3) + bubble.write("Not helpful!") + + self.add(morty) + self.play( + FadeIn(bubble), + FadeIn(bubble.content), + morty.change_mode, "angry", + morty.look, OUT + ) + self.play(Blink(morty)) + self.dither() + +class FoldUnitSquare(EdgesOfSquare): + def construct(self): + self.add_triangles() + self.add_arrows() + self.show_points_to_glue() + self.perform_fold() + self.show_singleton_pairs() + self.ask_about_gluing() + self.clarify_edge_gluing() + + def add_triangles(self): + square = self.add_square() + triangles = VGroup(*[ + Polygon(*[square.get_corner(vect) for vect in vects]) + for vects in [ + (DOWN+LEFT, UP+RIGHT, UP+LEFT), + (DOWN+LEFT, UP+RIGHT, DOWN+RIGHT), + ] + ]) + triangles.set_stroke(width = 0) + triangles.set_fill( + color = square.get_color(), + opacity = square.get_fill_opacity() + ) + self.remove(square) + self.square = square + self.add(triangles) + self.triangles = triangles + + def add_arrows(self): + start_arrows = VGroup() + end_arrows = VGroup() + colors = MAROON_B, BLUE + for a in 0, 1: + for color in colors: + b_range = np.linspace(0, 1, 4) + for b1, b2 in zip(b_range, b_range[1:]): + arrow = Arrow( + self.get_point_from_coords(a, b1), + self.get_point_from_coords(a, b2), + buff = 0, + color = color + ) + if color is BLUE: + arrow.rotate( + -np.pi/2, + about_point = self.square.get_center() + ) + if (a is 0): + start_arrows.add(arrow) + else: + end_arrows.add(arrow) + self.add(start_arrows, end_arrows) + self.start_arrows = start_arrows + self.end_arrows = VGroup(*list(end_arrows[3:])+list(end_arrows[:3])).copy() + self.end_arrows.highlight( + color_gradient([MAROON_B, BLUE], 3)[1] + ) + + def show_points_to_glue(self): + colors = YELLOW, MAROON_B, PINK + pairs = [(0.2, 0.3), (0.5, 0.7), (0.25, 0.6)] + unit = self.square.get_width() + + start_dots = VGroup() + end_dots = VGroup() + for (x, y), color in zip(pairs, colors): + old_x_line, old_y_line = None, None + for (a, b) in (x, y), (y, x): + point = self.get_point_from_coords(a, b) + dot = Dot(point) + dot.highlight(color) + if color == colors[-1]: + s = "(x, y)" if a < b else "(y, x)" + label = TexMobject(s) + else: + label = TexMobject("(%.01f, %.01f)"%(a, b)) + vect = UP+RIGHT if a < b else DOWN+RIGHT + label.next_to(dot, vect, buff = SMALL_BUFF) + + self.play(*map(FadeIn, [dot, label])) + x_line = Line(point+a*unit*LEFT, point) + y_line = Line(point+b*unit*DOWN, point) + x_line.highlight(GREEN) + y_line.highlight(RED) + if old_x_line is None: + self.play(ShowCreation(x_line), Animation(dot)) + self.play(ShowCreation(y_line), Animation(dot)) + old_x_line, old_y_line = y_line, x_line + else: + self.play(Transform(old_x_line, x_line), Animation(dot)) + self.play(Transform(old_y_line, y_line), Animation(dot)) + self.remove(old_x_line, old_y_line) + self.add(x_line, y_line, dot) + self.dither(2) + self.play(FadeOut(label)) + if a < b: + start_dots.add(dot) + else: + end_dots.add(dot) + self.play(*map(FadeOut, [x_line, y_line])) + self.start_dots, self.end_dots = start_dots, end_dots + + def perform_fold(self): + diag_line = DashedLine( + self.square.get_corner(DOWN+LEFT), + self.square.get_corner(UP+RIGHT), + color = RED + ) + + self.play(ShowCreation(diag_line)) + self.dither() + self.play( + Transform(*self.triangles), + Transform(self.start_dots, self.end_dots), + Transform(self.start_arrows, self.end_arrows), + ) + self.dither() + self.diag_line = diag_line + + def show_singleton_pairs(self): + xs = [0.7, 0.4, 0.5] + old_label = None + old_dot = None + for x in xs: + point = self.get_point_from_coords(x, x) + dot = Dot(point) + if x is xs[-1]: + label = TexMobject("(x, x)") + else: + label = TexMobject("(%.1f, %.1f)"%(x, x)) + label.next_to(dot, UP+LEFT, buff = SMALL_BUFF) + VGroup(dot, label).highlight(RED) + if old_label is None: + self.play( + ShowCreation(dot), + Write(label) + ) + old_label = label + old_dot = dot + else: + self.play( + Transform(old_dot, dot), + Transform(old_label, label), + ) + self.dither() + #Some strange bug necesitating this + self.remove(old_label) + self.add(label) + + def ask_about_gluing(self): + keepers = VGroup( + self.triangles[0], + self.start_arrows, + self.diag_line + ).copy() + faders = VGroup(*self.get_mobjects()) + randy = Randolph() + randy.next_to(ORIGIN, DOWN) + bubble = randy.get_bubble(height = 4, width = 6) + bubble.write("How do you \\\\ glue those arrows?") + + self.play( + FadeOut(faders), + Animation(keepers) + ) + self.play( + keepers.scale, 0.6, + keepers.shift, 4*RIGHT + UP, + FadeIn(randy) + ) + self.play( + randy.change_mode, "pondering", + randy.look_at, keepers, + ShowCreation(bubble), + Write(bubble.content) + ) + self.play(Blink(randy)) + self.dither() + self.randy = randy + + def clarify_edge_gluing(self): + dots = VGroup(*[ + Dot(self.get_point_from_coords(*coords), radius = 0.1) + for coords in [ + (0.1, 0), + (1, 0.1), + (0.9, 0), + (1, 0.9), + ] + ]) + dots.scale(0.6) + dots.shift(4*RIGHT + UP) + for dot in dots[:2]: + dot.highlight(YELLOW) + self.play( + ShowCreation(dot), + self.randy.look_at, dot + ) + self.dither() + for dot in dots[2:]: + dot.highlight(MAROON_B) + self.play( + ShowCreation(dot), + self.randy.look_at, dot + ) + self.play(Blink(self.randy)) + self.dither() + + def get_point_from_coords(self, x, y): + left, right, bottom, top = [ + self.triangles.get_edge_center(vect) + for vect in LEFT, RIGHT, DOWN, UP + ] + x_point = interpolate(left, right, x) + y_point = interpolate(bottom, top, y) + return x_point[0]*RIGHT + y_point[1]*UP + +class PrepareForMobiusStrip(Scene): + def construct(self): + self.add_triangles() + self.perform_cut() + self.rearrange_pieces() + + def add_triangles(self): + triangles = VGroup( + Polygon( + DOWN+LEFT, + DOWN+RIGHT, + ORIGIN, + ), + Polygon( + DOWN+RIGHT, + UP+RIGHT, + ORIGIN, + ), + ) + triangles.set_fill(color = BLUE, opacity = 0.6) + triangles.set_stroke(width = 0) + triangles.center() + triangles.scale(2) + arrows_color = color_gradient([PINK, BLUE], 3)[1] + for tri in triangles: + anchors = tri.get_anchors_and_handles()[0] + alpha_range = np.linspace(0, 1, 4) + arrows = VGroup(*[ + Arrow( + interpolate(anchors[0], anchors[1], a), + interpolate(anchors[0], anchors[1], b), + buff = 0, + color = arrows_color + ) + for a, b in zip(alpha_range, alpha_range[1:]) + ]) + tri.original_arrows = arrows + tri.add(arrows) + i, j, k = (0, 2, 1) if tri is triangles[0] else (1, 2, 0) + dashed_line = DashedLine( + anchors[i], anchors[j], + color = RED + ) + tri.add(dashed_line) + + #Add but don't draw cut_arrows + start, end = anchors[j], anchors[k] + cut_arrows = VGroup(*[ + Arrow( + interpolate(start, end, a), + interpolate(start, end, b), + buff = 0, + color = YELLOW + ) + for a, b in zip(alpha_range, alpha_range[1:]) + ]) + tri.cut_arrows = cut_arrows + self.add(triangles) + self.triangles = triangles + + def perform_cut(self): + tri1, tri2 = self.triangles + + + self.play(ShowCreation(tri1.cut_arrows)) + for tri in self.triangles: + tri.add(tri.cut_arrows) + self.dither() + self.play( + tri1.shift, (DOWN+LEFT)/2., + tri2.shift, (UP+RIGHT)/2., + ) + self.dither() + + def rearrange_pieces(self): + tri1, tri2 = self.triangles + self.play( + tri1.rotate, np.pi, UP+RIGHT, + tri1.next_to, ORIGIN, RIGHT, + tri2.next_to, ORIGIN, LEFT, + ) + self.dither() + self.play(*[ + ApplyMethod(tri.shift, tri.points[0][0]*LEFT) + for tri in self.triangles + ]) + self.play(*[ + FadeOut(tri.original_arrows) + for tri in self.triangles + ]) + for tri in self.triangles: + tri.remove(tri.original_arrows) + self.dither() + # self.play(*[ + # ApplyMethod(tri.rotate, -np.pi/4) + # for tri in self.triangles + # ]) + # self.dither() + +class FoldToMobius(Scene): + def construct(self): + pass + +class MobiusPlaneAnalogy(ClosedLoopScene): + def construct(self): + top_arrow = Arrow(LEFT, RIGHT) + top_arrow.to_edge(UP, buff = 2*LARGE_BUFF) + low_arrow = Arrow(LEFT, RIGHT).shift(2*DOWN) + self.loop.scale(0.5) + self.loop.next_to(top_arrow, RIGHT) + self.loop.shift_onto_screen() + self.add_dots_at_alphas(0.3, 0.5) + self.dots.highlight(PURPLE_B) + + plane = NumberPlane() + plane.scale(0.3).next_to(low_arrow, LEFT) + number_line = NumberLine() + number_line.scale(0.3) + number_line.next_to(low_arrow, RIGHT) + number_line.add( + Dot(number_line.number_to_point(3), color = GREEN), + Dot(number_line.number_to_point(-2), color = RED), + ) + + self.dither() + self.play(ShowCreation(top_arrow)) + self.dither() + self.play(ShowCreation(plane)) + self.play(ShowCreation(low_arrow)) + self.play(ShowCreation(number_line)) + self.dither() + +class DrawRightArrow(Scene): + CONFIG = { + "tex" : "\\Rightarrow" + } + def construct(self): + arrow = TexMobject(self.tex) + arrow.scale(4) + self.play(Write(arrow)) + self.dither() + +class DrawLeftrightArrow(DrawRightArrow): + CONFIG = { + "tex" : "\\Leftrightarrow" + } + +class MobiusToPairToSurface(ClosedLoopScene): + def construct(self): + self.loop.scale(0.5) + self.loop.next_to(ORIGIN, RIGHT) + self.loop.to_edge(UP) + self.add_dots_at_alphas(0.4, 0.6) + self.dots.highlight(MAROON_B) + self.add_connecting_lines() + strip_dot = Dot().next_to(self.loop, LEFT, buff = 2*LARGE_BUFF) + surface_dot = Dot().next_to(self.loop, DOWN, buff = 2*LARGE_BUFF) + + top_arrow = Arrow(strip_dot, self.loop) + right_arrow = Arrow(self.loop, surface_dot) + diag_arrow = Arrow(strip_dot, surface_dot) + + randy = self.randy = Randolph(mode = "pondering") + randy.next_to(ORIGIN, DOWN+LEFT) + + self.look_at(strip_dot) + self.play( + ShowCreation(top_arrow), + randy.look_at, self.loop + ) + self.dither() + self.look_at(strip_dot, surface_dot) + self.play(ShowCreation(diag_arrow)) + self.play(Blink(randy)) + self.look_at(strip_dot, self.loop) + self.dither() + self.play( + ShowCreation(right_arrow), + randy.look_at, surface_dot + ) + self.play(Blink(randy)) + self.play(randy.change_mode, "happy") + self.play(Blink(randy)) + self.dither() + + + def look_at(self, *things): + for thing in things: + self.play(self.randy.look_at, thing) + +class MapMobiusStripOntoSurface(Scene): + def construct(self): + pass + +class StripMustIntersectItself(TeacherStudentsScene): + def construct(self): + self.teacher_says( + """ + The strip must + intersect itself + during this process + """, + width = 4 + ) + dot = Dot(2*UP + 4*LEFT) + for student in self.get_students(): + student.generate_target() + student.target.change_mode("pondering") + student.target.look_at(dot) + self.play(*map(MoveToTarget, self.get_students())) + self.random_blink(4) + +class PairOfMobiusPointsLandOnEachother(Scene): + def construct(self): + pass + +class ThatsTheProof(TeacherStudentsScene): + def construct(self): + self.teacher_says( + """ + Bada boom + bada bang! + """, + target_mode = "hooray", + width = 4 + ) + self.change_student_modes(*["hooray"]*3) + self.random_blink() + self.change_student_modes( + "confused", "sassy", "erm" + ) + self.teacher_says( + """ + If you trust + the mobius strip + fact... + """, + target_mode = "guilty", + width = 4, + ) + self.random_blink() + +class TryItYourself(TeacherStudentsScene): + def construct(self): + self.teacher_says(""" + It's actually an + edifying exercise. + """) + self.random_blink() + self.change_student_modes(*["pondering"]*3) + self.random_blink(2) + + pi = self.get_students()[1] + bubble = pi.get_bubble( + "thought", + width = 4, height = 4, + direction = RIGHT + ) + bubble.set_fill(BLACK, opacity = 1) + bubble.write("Orientation seem\\\\ to matter...") + self.play( + FadeIn(bubble), + Write(bubble.content) + ) + self.random_blink(3) + +class OneMoreAnimation(TeacherStudentsScene): + def construct(self): + self.teacher_says(""" + One more animation, + but first... + """) + self.change_student_modes(*["happy"]*3) + self.random_blink() + +class PatreonThanks(Scene): + CONFIG = { + "specific_patrons" : [ + "Loo Yu Jun", + "Tom", + "Othman Alikhan", + "Juan Batiz-Benet", + "Markus Persson", + "Joseph John Cox", + "Achille Brighton", + "Kirk Werklund", + "Luc Ritchie", + "Ripta Pasay", + "PatrickJMT ", + "Felipe Diniz", + ] + } + def construct(self): + morty = Mortimer() + morty.next_to(ORIGIN, DOWN) + + n_patrons = len(self.specific_patrons) + special_thanks = TextMobject("Special thanks to:") + special_thanks.highlight(YELLOW) + special_thanks.shift(2*UP) + + left_patrons = VGroup(*map(TextMobject, + self.specific_patrons[:n_patrons/2] + )) + right_patrons = VGroup(*map(TextMobject, + self.specific_patrons[n_patrons/2:] + )) + for patrons, vect in (left_patrons, LEFT), (right_patrons, RIGHT): + patrons.arrange_submobjects(DOWN, aligned_edge = LEFT) + patrons.next_to(special_thanks, DOWN) + patrons.to_edge(vect, buff = LARGE_BUFF) + + self.play(morty.change_mode, "gracious") + self.play(Write(special_thanks, run_time = 1)) + self.play( + Write(left_patrons), + morty.look_at, left_patrons + ) + self.play( + Write(right_patrons), + morty.look_at, right_patrons + ) + self.play(Blink(morty)) + for patrons in left_patrons, right_patrons: + for index in 0, -1: + self.play(morty.look_at, patrons[index]) + self.dither() + +class CreditTWo(Scene): + def construct(self): + morty = Mortimer() + morty.next_to(ORIGIN, DOWN) + morty.to_edge(RIGHT) + + brother = PiCreature(color = GOLD_E) + brother.next_to(morty, LEFT) + brother.look_at(morty.eyes) + + headphones = Headphones(height = 1) + headphones.move_to(morty.eyes, aligned_edge = DOWN) + headphones.shift(0.1*DOWN) + + url = TextMobject("www.audible.com/3b1b") + url.to_corner(UP+RIGHT, buff = LARGE_BUFF) + + self.add(morty) + self.play(Blink(morty)) + self.play( + FadeIn(headphones), + Write(url), + Animation(morty) + ) + self.play(morty.change_mode, "happy") + self.dither() + self.play(Blink(morty)) + self.dither() + self.play( + FadeIn(brother), + morty.look_at, brother.eyes + ) + self.play(brother.change_mode, "surprised") + self.play(Blink(brother)) + self.dither() + self.play( + morty.look, LEFT, + brother.change_mode, "happy", + brother.look, LEFT + ) + self.play(Blink(morty)) + self.dither() + +class CreditThree(Scene): + def construct(self): + logo_dot = Dot().to_edge(UP).shift(3*RIGHT) + randy = Randolph() + randy.next_to(ORIGIN, DOWN) + randy.to_edge(LEFT) + randy.look(RIGHT) + self.add(randy) + bubble = randy.get_bubble(width = 2, height = 2) + + domains = VGroup(*map(TextMobject, [ + "visualnumbertheory.com", + "buymywidgets.com", + "learnwhatilearn.com", + ])) + domains.arrange_submobjects(DOWN, aligned_edge = LEFT) + domains.next_to(randy, UP, buff = LARGE_BUFF) + domains.shift_onto_screen() + + promo_code = TextMobject("Promo code: TOPOLOGY") + promo_code.shift(3*RIGHT) + self.add(promo_code) + whois = TextMobject("Free WHOIS privacy") + whois.next_to(promo_code, DOWN, buff = LARGE_BUFF) + + self.play(Blink(randy)) + self.play( + randy.change_mode, "happy", + randy.look_at, logo_dot + ) + self.dither() + self.play( + ShowCreation(bubble), + randy.change_mode, "pondering", + run_time = 2 + ) + self.play(Blink(randy)) + self.play( + Transform(bubble, VectorizedPoint(randy.get_corner(UP+LEFT))), + randy.change_mode, "sad" + ) + self.dither() + self.play( + Write(domains, run_time = 5, lag_factor = 5), + randy.look_at, domains + ) + self.dither() + self.play(Blink(randy)) + self.play( + randy.change_mode, "hooray", + randy.look_at, logo_dot, + FadeOut(domains) + ) + self.dither() + self.play( + Write(whois), + randy.change_mode, "confused", + randy.look_at, whois + ) + self.dither(2) + self.play(randy.change_mode, "sassy") + self.dither(2) + self.play( + randy.change_mode, "happy", + randy.look_at, logo_dot + ) + self.play(Blink(randy)) + self.dither() + + +class ShiftingLoopPairSurface(Scene): + def construct(self): + pass + +class ThumbnailImage(ClosedLoopScene): + def construct(self): + self.add_rect_dots(square = True) + for dot in self.dots: + dot.scale_in_place(1.5) + self.add_connecting_lines(cyclic = True) + self.connecting_lines.set_stroke(width = 10) + self.loop.add(self.connecting_lines, self.dots) + + title = TextMobject("Unsolved") + title.scale(2.5) + title.to_edge(UP) + title.gradient_highlight(YELLOW, MAROON_B) + self.add(title) + self.loop.next_to(title, DOWN, buff = MED_BUFF) + self.loop.shift(2*LEFT) + + + + + + + + +