diff --git a/active_projects/windmill.py b/active_projects/windmill.py index 5addcb7e..06cff945 100644 --- a/active_projects/windmill.py +++ b/active_projects/windmill.py @@ -12,6 +12,7 @@ class IntroduceIMO(Scene): "random_seed": 6, "year": 2019, "n_flag_rows": 10, + "reorganize_students": True, } def construct(self): @@ -76,10 +77,10 @@ class IntroduceIMO(Scene): n_rows=self.n_flag_rows, buff=SMALL_BUFF, ) - student_groups.target[-9:].align_to(student_groups.target[0], LEFT) + # student_groups.target[-9:].align_to(student_groups.target[0], LEFT) student_groups.target.match_height(flags) student_groups.target.match_y(flags) - student_groups.target.to_edge(RIGHT, buff=1) + student_groups.target.to_edge(RIGHT, buff=0.25) self.play(LaggedStart( *[ @@ -92,12 +93,13 @@ class IntroduceIMO(Scene): lag_ratio=0.2, )) self.wait() - self.play( - MoveToTarget(student_groups), - flags.space_out_submobjects, 0.8, - flags.to_edge, LEFT, MED_SMALL_BUFF, - ) - self.wait() + if self.reorganize_students: + self.play( + MoveToTarget(student_groups), + flags.space_out_submobjects, 0.75, + flags.to_edge, LEFT, MED_SMALL_BUFF, + ) + self.wait() self.student_groups = student_groups @@ -180,6 +182,7 @@ class IntroduceIMO(Scene): else: color = random_bright_color() dots.set_color(color) + dots.set_stroke(WHITE, 1, background=True) return dots @@ -262,6 +265,37 @@ class IntroduceIMO(Scene): return images +class ShowTinyTao(IntroduceIMO): + CONFIG = { + "reorganize_students": False, + } + + def construct(self): + self.force_skipping() + self.add_title() + self.show_flags() + self.show_students() + self.revert_to_original_skipping_status() + + image = ImageMobject("TerryTaoIMO") + label = TextMobject("Terence Tao at 12") + label.match_width(image) + label.next_to(image, DOWN, SMALL_BUFF) + image.add(label) + + ausie = self.flags[17] + image.replace(ausie) + image.set_opacity(0) + + self.play(image.set_opacity, 1) + self.play( + image.set_height, 5, + image.to_corner, DR, {"buff": MED_SMALL_BUFF}, + ) + self.wait() + self.play(FadeOut(image)) + + class FootnoteToIMOIntro(Scene): def construct(self): words = TextMobject("$^*$Based on data from 2019 test") @@ -294,7 +328,7 @@ class ShowTest(Scene): day_labels.add(label[0]) hour_labels.add(label[1]) - # Problem desciptions + # Problem descriptions problem_rects = self.get_problem_rects(test.target[0]) proof_words = VGroup() for rect in problem_rects: @@ -314,11 +348,15 @@ class ShowTest(Scene): scores = VGroup() for word in proof_words: - score = VGroup(TexMobject("/"), Integer(0)) + score = VGroup(Integer(0), TexMobject("/"), Integer(7)) score.arrange(RIGHT, buff=SMALL_BUFF) score.scale(2) - score.next_to(word, RIGHT, buff=1.5) + score.move_to(word) + score.to_edge(RIGHT) scores.add(score) + score[0].add_updater(lambda m: m.set_color( + interpolate_color(RED, GREEN, m.get_value() / 7) + )) # Introduce test self.play( @@ -361,9 +399,9 @@ class ShowTest(Scene): self.play(FadeIn(scores)) self.play( LaggedStart(*[ - ChangeDecimalToValue(score[1], 7) + ChangeDecimalToValue(score[0], 7) for score in scores - ], lag_ratio=0, rate_func=rush_into) + ], lag_ratio=0.2, rate_func=rush_into) ) self.wait() @@ -403,7 +441,7 @@ class ShowTest(Scene): return rects -class USProcess(IntroduceIMO): +class USProcessAlt(IntroduceIMO): CONFIG = { } @@ -457,6 +495,7 @@ class USProcess(IntroduceIMO): ) ) amc, aime, usamo, mop = tests + arrows = VGroup() amc.to_corner(UR) top_point = amc.get_top() @@ -464,6 +503,7 @@ class USProcess(IntroduceIMO): last_arrow.to_corner(DL) next_anims = [] + self.force_skipping() for test in tests: test.move_to(top_point, UP) test.shift_onto_screen() @@ -504,8 +544,22 @@ class USProcess(IntroduceIMO): last_arrow = Vector(0.5 * RIGHT) last_arrow.set_color(WHITE) last_arrow.next_to(test.target, RIGHT, SMALL_BUFF) + arrows.add(last_arrow) self.play(*next_anims) + self.revert_to_original_skipping_status() + self.play( + LaggedStartMap( + FadeInFrom, tests, + lambda m: (m, LEFT), + ), + LaggedStartMap( + GrowArrow, arrows[:-1] + ), + lag_ratio=0.4, + ) + self.wait() + self.tests = tests def show_imo(self): @@ -533,8 +587,8 @@ class USProcess(IntroduceIMO): student.move_to(tests[-1]) student.fade(1) - self.play(FadeInFromDown(group)) self.play( + FadeInFromDown(group), LaggedStartMap( Restore, students, run_time=3, @@ -787,37 +841,38 @@ class Describe2011IMO(IntroduceIMO): ]) lines.set_stroke(TEAL, 2) - morty = Mortimer() - morty.flip() - morty.to_corner(DL) - morty.look_at(numbers) + randy = Randolph() + randy.to_corner(DL) + randy.look_at(numbers) + + words = VGroup(*[ + TextMobject("Prime").next_to(line, DOWN) + for line in reversed(lines) + ]) + words.match_color(lines) self.add(full_rect, numbers) self.play( FadeIn(full_rect), - morty.change, "sassy", - VFadeIn(morty), + randy.change, "sassy", + VFadeIn(randy), ) self.play( ShowCreation(lines), - morty.change, "pondering", + randy.change, "pondering", ) - self.play(Blink(morty)) + self.play(Blink(randy)) self.play( - PiCreatureBubbleIntroduction( - morty, - "${1 \\over \\ln(101)}" - "{1 \\over \\ln(563)}" - "{1 \\over \\ln(2011)}$", - bubble_class=ThoughtBubble, - target_mode="thinking", - ) + randy.change, "thinking", + LaggedStart(*[ + FadeInFrom(word, UP) + for word in words + ], run_time=3, lag_ratio=0.5) ) - self.play(Blink(morty)) + self.play(Blink(randy)) self.play( - FadeOut(morty), - FadeOut(morty.bubble), - FadeOut(morty.bubble.content), + FadeOut(randy), + FadeOut(words), ) self.play(FadeOut(full_rect), FadeOut(lines)) @@ -1071,7 +1126,7 @@ class AskWhatsOnTest(ShowTest, MovingCameraScene): self.wait() self.play( FadeIn(big_rect), - MoveToTarget(frame, run_time=3), + MoveToTarget(frame, run_time=6), ) self.wait() @@ -2909,6 +2964,7 @@ class Rotate180Argument(WindmillScene): def rotate_180(self): windmill = self.windmill + self.add(self.pivot_dot) self.let_windmill_run( windmill, PI / windmill.rot_speed, @@ -2965,6 +3021,12 @@ class Rotate180Argument(WindmillScene): self.wait() +class Rotate180ArgumentFast(Rotate180Argument): + CONFIG = { + "windmill_rotation_speed": 0.5, + } + + class EvenCase(Rotate180Argument): CONFIG = { "n_points": 10, @@ -3144,6 +3206,613 @@ class EvenCase(Rotate180Argument): ) +class TwoTakeaways(TeacherStudentsScene): + def construct(self): + title = TextMobject("Two takeaways") + title.scale(2) + title.to_edge(UP) + + line = Line() + line.match_width(title) + line.next_to(title, DOWN, SMALL_BUFF) + + items = VGroup(*[ + TextMobject("1) Social"), + TextMobject("2) Mathematical"), + ]) + items.scale(1.5) + items.arrange(DOWN, buff=MED_LARGE_BUFF, aligned_edge=LEFT) + items.next_to(line, DOWN, buff=MED_LARGE_BUFF) + + self.play( + ShowCreation(line), + GrowFromPoint(title, self.hold_up_spot), + self.teacher.change, "raise_right_hand", + ) + self.change_all_student_modes("pondering") + self.wait() + for item in items: + self.play(FadeInFrom(item, LEFT)) + item.big = item.copy() + item.small = item.copy() + item.big.scale(1.5, about_edge=LEFT) + item.big.set_color(BLUE) + item.small.scale(0.75, about_edge=LEFT) + item.small.fade(0.5) + self.play(self.teacher.change, "happy") + self.wait() + for i, j in [(0, 1), (1, 0)]: + self.play( + items[i].become, items[i].big, + items[j].become, items[j].small, + ) + self.wait() + + +class EasyToFoolYourself(PiCreatureScene): + CONFIG = { + "default_pi_creature_kwargs": { + "color": GREY_BROWN, + } + } + + def construct(self): + morty = self.pi_creature + morty.to_corner(DL) + + bubble = ThoughtBubble() + for i, part in enumerate(bubble): + part.shift(2 * i * SMALL_BUFF * DOWN) + bubble.pin_to(morty) + + fool_word = TextMobject("Fool") + fool_word.scale(1.5) + fool_arrow = Vector(LEFT) + fool_arrow.next_to(morty, RIGHT, buff=0) + fool_word.next_to(fool_arrow, RIGHT) + + self.add(morty) + self.play( + ShowCreation(bubble), + morty.change, "pondering", + ) + self.play( + bubble[3].set_fill, GREEN_SCREEN, 0.5, + ) + self.wait() + self.play(morty.change, "thinking") + self.play( + FadeInFrom(fool_word, LEFT), + ShowCreation(fool_arrow), + ) + self.wait() + self.pi_creature_says( + "Isn't it\\\\obvious?", + target_mode="maybe", + added_anims=[FadeOut(bubble)] + ) + self.wait(4) + + # + words = TextMobject("No it's not!") + words.scale(1.5) + words.set_color(RED) + words.next_to(morty.bubble, RIGHT, LARGE_BUFF) + words.match_y(morty.bubble.content) + + self.play( + FadeInFromLarge(words), + morty.change, "guilty", + ) + self.wait() + + # for i, part in enumerate(bubble): + # self.add(Integer(i).move_to(part)) + + +class FailureToEmpathize(PiCreatureScene): + def construct(self): + randy, morty = self.pi_creatures + + # What a mess... + big_bubble = ThoughtBubble(height=4, width=5) + big_bubble.scale(1.75) + big_bubble.flip(UR) + for part in big_bubble: + part.rotate(90 * DEGREES) + big_bubble[:3].rotate(-30 * DEGREES) + for i, part in enumerate(big_bubble[:3]): + part.rotate(30 * DEGREES) + part.shift((3 - i) * SMALL_BUFF * DOWN) + big_bubble[0].shift(MED_SMALL_BUFF * RIGHT) + big_bubble[:3].next_to(big_bubble[3], LEFT) + big_bubble[:3].shift(0.3 * DOWN) + big_bubble.set_fill(DARKER_GREY) + big_bubble.to_corner(UR) + + equation = TexMobject( + "\\sum_{k=1}^n (2k - 1) = n^2" + ) + self.pi_creature_thinks( + randy, equation, + target_mode="confused", + look_at_arg=equation, + ) + randy_group = VGroup( + randy, randy.bubble, + randy.bubble.content + ) + self.wait() + self.play( + DrawBorderThenFill(big_bubble), + morty.change, "confused", + randy_group.scale, 0.5, + randy_group.move_to, big_bubble.get_bubble_center(), + randy_group.shift, 0.5 * DOWN + RIGHT, + ) + self.wait() + self.play(morty.change, "maybe") + self.wait(2) + + # Zoom out + morty_group = VGroup(morty, big_bubble) + ap = 5 * RIGHT + 2.5 * UP + self.add(morty_group, randy_group) + self.play( + morty_group.scale, 2, {"about_point": ap}, + morty_group.fade, 1, + randy_group.scale, 2, {"about_point": ap}, + run_time=2 + ) + self.wait() + + def create_pi_creatures(self): + randy = Randolph() + morty = Mortimer() + randy.flip().to_corner(DR) + morty.flip().to_corner(DL) + + return (randy, morty) + + +class DifficultyEstimateVsReality(Scene): + def construct(self): + axes = Axes( + x_min=-1, + x_max=10, + x_axis_config={ + "include_tip": False, + }, + y_min=-1, + y_max=5, + ) + axes.set_height(FRAME_HEIGHT - 1) + axes.center() + axes.x_axis.tick_marks.set_opacity(0) + + y_label = TextMobject("Average score") + y_label.scale(1.25) + y_label.rotate(90 * DEGREES) + y_label.next_to(axes.y_axis, LEFT, SMALL_BUFF) + y_label.shift(UP) + + estimated = [1.8, 2.6, 3, 4, 5] + actual = [1.5, 0.5, 1, 1.2, 1.8] + + colors = [GREEN, RED] + estimated_color, actual_color = colors + + estimated_bars = VGroup() + actual_bars = VGroup() + bar_pairs = VGroup() + + width = 0.25 + for a, e in zip(actual, estimated): + bars = VGroup( + Rectangle(width=width, height=e), + Rectangle(width=width, height=a), + ) + bars.set_stroke(width=1) + bars[0].set_fill(estimated_color, 0.75) + bars[1].set_fill(actual_color, 0.75) + bars.arrange(RIGHT, buff=0, aligned_edge=DOWN) + bar_pairs.add(bars) + estimated_bars.add(bars[0]) + actual_bars.add(bars[1]) + + bar_pairs.arrange(RIGHT, buff=1.5, aligned_edge=DOWN) + bar_pairs.move_to(axes.c2p(5, 0), DOWN) + for bp in bar_pairs: + for bar in bp: + bar.save_state() + bar.stretch(0, 1, about_edge=DOWN) + + x_labels = VGroup(*[ + TextMobject("Q{}".format(i)).next_to(bp, DOWN) + for i, bp in zip(it.count(1), bar_pairs) + ]) + + data_labels = VGroup( + TextMobject("Estimated average"), + TextMobject("Actual average"), + ) + data_labels.arrange(DOWN, buff=MED_LARGE_BUFF, aligned_edge=LEFT) + data_labels.to_edge(UP) + for color, label in zip(colors, data_labels): + square = Square() + square.set_height(0.5) + square.set_fill(color, 0.75) + square.set_stroke(WHITE, 1) + square.next_to(label, LEFT, SMALL_BUFF) + label.add(square) + + self.play(Write(axes)) + self.play(Write(y_label)) + + self.play( + LaggedStartMap( + FadeInFrom, x_labels, + lambda m: (m, UP), + run_time=2, + ), + LaggedStartMap( + Restore, + estimated_bars, + run_time=3, + ), + FadeIn(data_labels[0]), + ) + self.wait() + self.play( + LaggedStartMap( + Restore, + actual_bars, + run_time=3, + ), + FadeIn(data_labels[1]), + ) + self.wait() + + +class KeepInMindWhenTeaching(TeacherStudentsScene): + def construct(self): + self.teacher_says( + "I don't know\\\\what you know!", + target_mode="pleading" + ) + self.wait(2) + self.play( + PiCreatureSays( + self.students[0], "We know", + target_mode="hooray", + ), + self.students[1].change, "happy", + self.students[2].change, "happy", + ) + self.wait(2) + + +class VastSpaceOfConsiderations(Scene): + def construct(self): + considerations = VGroup(*[ + TextMobject(phrase) + for phrase in [ + "Define ``outer'' points", + "Convex hulls", + "Linear equations", + "Sort points by when they're hit", + "Sort points by some kind of angle?", + "How does this permute the $n \\choose 2$ lines through pairs?", + "Some points are hit more than others, can we quantify this?", + ] + ]) + considerations.arrange(DOWN, buff=MED_LARGE_BUFF, aligned_edge=LEFT) + considerations.to_edge(LEFT) + + self.play(LaggedStart(*[ + FadeInFrom(mob, UP) + for mob in considerations + ], run_time=3, lag_ratio=0.2)) + + +class WhatStaysConstantWrapper(Scene): + CONFIG = { + "camera_config": { + "background_color": DARKER_GREY + } + } + + def construct(self): + rect = ScreenRectangle() + rect.set_height(6) + rect.set_stroke(WHITE, 2) + rect.set_fill(BLACK, 1) + title1 = TextMobject("What stays constant?") + title2 = TextMobject("Find an ", "``invariant''") + title2[1].set_color(YELLOW) + for title in [title1, title2]: + title.scale(2) + title.to_edge(UP) + rect.next_to(title1, DOWN) + + self.add(rect) + self.play(FadeInFromDown(title1)) + self.wait() + self.play( + FadeOutAndShift(title1, UP), + FadeInFromDown(title2), + ) + self.wait() + + +class CountHoles(Scene): + def construct(self): + labels = VGroup( + TextMobject("Genus ", "0"), + TextMobject("Genus ", "1"), + TextMobject("Genus ", "2"), + ) + + labels.scale(2) + labels.arrange(RIGHT, buff=1.5) + labels.move_to(2 * DOWN) + + equation = TexMobject("y^2 = x^3 + ax + b") + equation.scale(1.5) + equation.shift(UP) + equation.to_edge(LEFT) + # arrow = TexMobject("\\approx").scale(2) + arrow = Vector(2 * RIGHT) + arrow.next_to(equation, RIGHT) + + equation_text = TextMobject("Some other problem") + equation_text.next_to(equation, DOWN, MED_LARGE_BUFF) + equation_text.match_width(equation) + equation_text.set_color(YELLOW) + + self.play(LaggedStartMap( + FadeInFromDown, labels, + lag_ratio=0.5, + )) + self.wait() + self.play( + labels[1].shift, 4 * RIGHT, + FadeOut(labels[0::2]), + ) + self.play( + FadeInFrom(equation, RIGHT), + GrowArrow(arrow), + ) + self.play(FadeInFrom(equation_text, UP)) + self.wait() + + +class LorenzTransform(Scene): + def construct(self): + grid = NumberPlane( + # faded_line_ratio=0, + # y_axis_config={ + # "y_min": -10, + # "y_max": 10, + # } + ) + grid.scale(2) + back_grid = grid.copy() + back_grid.set_stroke(GREY, 0.5) + # back_grid.set_opacity(0.5) + + c_lines = VGroup(Line(DL, UR), Line(DR, UL)) + c_lines.scale(FRAME_HEIGHT) + c_lines.set_stroke(YELLOW, 3) + + equation = TexMobject( + "d\\tau^2 = dt^2 - dx^2" + ) + equation.scale(1.7) + equation.to_corner(UL, buff=MED_SMALL_BUFF) + equation.shift(2.75 * DOWN) + equation.set_stroke(BLACK, 5, background=True) + + self.add(back_grid, grid, c_lines) + self.add(equation) + beta = 0.4 + self.play( + grid.apply_matrix, np.array([ + [1, beta], + [beta, 1], + ]) / (1 - beta**2), + run_time=2 + ) + self.wait() + + +class OnceACleverDiscovery(Scene): + def construct(self): + energy = TextMobject("energy") + rect = SurroundingRectangle(energy) + words = TextMobject("Once a clever discovery") + vect = Vector(DR) + vect.next_to(rect.get_top(), UL, SMALL_BUFF) + words.next_to(vect.get_start(), UP) + words.set_color(YELLOW) + vect.set_color(YELLOW) + + self.play( + ShowCreation(vect), + ShowCreation(rect), + ) + self.play(FadeInFromDown(words)) + self.wait() + + +class TerryTaoQuote(Scene): + def construct(self): + image = ImageMobject("TerryTao") + image.set_height(4) + name = TextMobject("Terence Tao") + name.scale(1.5) + name.next_to(image, DOWN, buff=0.2) + tao = Group(image, name) + tao.to_corner(DL, buff=MED_SMALL_BUFF) + + tiny_tao = ImageMobject("TerryTaoIMO") + tiny_tao.match_height(image) + tiny_tao.next_to(image, RIGHT, LARGE_BUFF) + + quote = self.get_quote() + + self.play( + FadeInFromDown(image), + Write(name), + ) + self.wait() + self.play( + FadeInFrom(tiny_tao, LEFT) + ) + self.wait() + self.play(FadeOut(tiny_tao)) + + # + self.play( + FadeIn( + quote, + lag_ratio=0.05, + run_time=5, + rate_func=bezier([0, 0, 1, 1]) + ) + ) + self.wait() + story_line = Line() + story_line.match_width(quote.story_part) + story_line.next_to(quote.story_part, DOWN, buff=0) + story_line.set_color(TEAL), + self.play( + quote.story_part.set_color, TEAL, + ShowCreation(story_line), + lag_ratio=0.2, + ) + self.wait() + + def get_quote(self): + story_words = "fables, stories, and anecdotes" + quote = TextMobject( + """ + \\Large + ``Mathematical problems, or puzzles, are important to real mathematics + (like solving real-life problems), just as fables, stories, and anecdotes + are important to the young in understanding real life.''\\\\ + """, + alignment="", + arg_separator=" ", + substrings_to_isolate=[story_words] + ) + quote.story_part = quote.get_part_by_tex(story_words) + quote.set_width(FRAME_WIDTH - 2.5) + quote.to_edge(UP) + + return quote + + +class WindmillFairyTale(Scene): + def construct(self): + paths = SVGMobject(file_name="windmill_fairytale") + + paths.set_height(FRAME_HEIGHT - 1) + paths.set_stroke(width=0) + paths.set_fill([LIGHT_GREY, WHITE]) + + for path in paths: + path.reverse_points() + + self.play(Write(paths[0], run_time=3)) + self.wait() + self.play( + LaggedStart( + FadeInFrom(paths[1], RIGHT), + FadeInFrom(paths[2], RIGHT), + lag_ratio=0.2, + run_time=3, + ) + ) + + +class SolveAProblemOneDay(SpiritOfIMO, PiCreatureScene): + def construct(self): + randy = self.pi_creature + light_bulb = Lightbulb() + light_bulb.base = light_bulb[:3] + light_bulb.light = light_bulb[3:] + light_bulb.set_height(1) + light_bulb.next_to(randy, UP, MED_LARGE_BUFF) + + light = self.get_light(light_bulb.get_center()) + + bubble = ThoughtBubble() + bubble.pin_to(randy) + + you = TextMobject("You") + you.scale(1.5) + arrow = Vector(LEFT) + arrow.next_to(randy, RIGHT) + you.next_to(arrow) + + self.play( + ShowCreation(bubble), + randy.change, "pondering", + ) + self.play( + FadeInFrom(you, LEFT), + GrowArrow(arrow) + ) + self.wait(2) + self.play( + FadeInFromDown(light_bulb), + randy.change, "hooray", + ) + self.play( + LaggedStartMap( + VFadeInThenOut, light, + run_time=2 + ), + randy.change, "thinking", light, + ) + self.wait(2) + + +class QuixoteReference(Scene): + def construct(self): + rect = FullScreenFadeRectangle() + rect.set_fill([DARK_GREY, GREY]) + + windmill = SVGMobject("windmill") + windmill.set_fill([GREY_BROWN, WHITE], 1) + windmill.set_stroke(width=0) + windmill.set_height(6) + windmill.to_edge(RIGHT) + # windmill.to_edge(DOWN, buff=0) + + quixote = SVGMobject("quixote") + quixote.flip() + quixote.set_height(4) + quixote.to_edge(LEFT) + quixote.set_stroke(BLACK, width=0) + quixote.set_fill(BLACK, 1) + quixote.align_to(windmill, DOWN) + + self.add(rect) + # self.add(windmill) + self.play(LaggedStart( + DrawBorderThenFill(windmill), + DrawBorderThenFill( + quixote, + stroke_width=1, + ), + lag_ratio=0.4, + run_time=3 + )) + self.wait() + + class WindmillEndScreen(PatreonEndScreen): CONFIG = { "specific_patrons": [ @@ -3314,6 +3983,7 @@ class Thumbnail(WindmillScene): "stroke_width": 1, }, "random_seed": 7, + "animate": False, } def construct(self): @@ -3321,7 +3991,7 @@ class Thumbnail(WindmillScene): points[:, 0] *= 1.7 points += 0.5 * LEFT - points[1] += DR + 0.5 * DOWN + points[1] = ORIGIN points[10] += LEFT points[6] += 3 * RIGHT @@ -3330,7 +4000,7 @@ class Thumbnail(WindmillScene): angle=45 * DEGREES, ) dots = self.get_dots(points) - rects = self.get_left_right_colorings(windmill) + # rects = self.get_left_right_colorings(windmill) pivot_dot = self.get_pivot_dot(windmill) pivot_dot.scale(2) pivot_dot.set_color(WHITE) @@ -3353,13 +4023,33 @@ class Thumbnail(WindmillScene): arcs.move_to(windmill.pivot) arcs.set_color([LIGHT_GREY, WHITE]) - self.add(rects[0], windmill, dots, pivot_dot) + polygon1 = Polygon( + (FRAME_HEIGHT * UP + FRAME_WIDTH * LEFT) / 2, + (FRAME_HEIGHT * UP + FRAME_HEIGHT * RIGHT) / 2, + (FRAME_HEIGHT * DOWN + FRAME_HEIGHT * LEFT) / 2, + (FRAME_HEIGHT * DOWN + FRAME_WIDTH * LEFT) / 2, + ) + polygon1.set_color([BLUE, DARKER_GREY]) + polygon1.set_fill(opacity=0.5) + polygon2 = Polygon( + (FRAME_HEIGHT * UP + FRAME_WIDTH * RIGHT) / 2, + (FRAME_HEIGHT * UP + FRAME_HEIGHT * RIGHT) / 2, + (FRAME_HEIGHT * DOWN + FRAME_HEIGHT * LEFT) / 2, + (FRAME_HEIGHT * DOWN + FRAME_WIDTH * RIGHT) / 2, + ) + polygon2.set_sheen_direction(DR) + polygon2.set_color([GREY_BROWN, BLACK]) + polygon2.set_fill(opacity=1) + + self.add(polygon1, polygon2) + # self.add(rects[0]) + self.add(windmill, dots, pivot_dot) self.add(arcs) self.add(flash.mobject) self.add_dot_color_updater(dots, windmill, color2=WHITE) words = TextMobject("Next\\\\", "pivot") - words2 = TextMobject("Next ", "next\\\\", "pivot", alignment="") + words2 = TextMobject("Next\\\\", "next\\\\", "pivot", alignment="") words.scale(2) words2.scale(2) # words.next_to(windmill.pivot, RIGHT) @@ -3371,6 +4061,10 @@ class Thumbnail(WindmillScene): arrow.set_color(YELLOW) arrow2 = Arrow(words2[-1].get_right(), new_pivot2, buff=0.6) arrow2.match_style(arrow) + arrow.rotate( + arrow2.get_angle() + PI - arrow.get_angle(), + about_point=new_pivot, + ) self.add(words, arrow) self.add(words2, arrow2) @@ -3378,6 +4072,46 @@ class Thumbnail(WindmillScene): # for i, dot in enumerate(dots): # self.add(Integer(i).move_to(dot)) + if self.animate: + sorted_dots = VGroup(*dots) + sorted_dots.sort(lambda p: np.dot(p, DR)) + + self.play( + polygon1.shift, FRAME_WIDTH * LEFT, + polygon2.shift, FRAME_WIDTH * RIGHT, + LaggedStart(*[ + ApplyMethod(mob.scale, 0) + for mob in [sorted_dots[6], *flash.mobject, windmill, pivot_dot] + ]), + LaggedStart(*[ + ApplyMethod(dot.to_edge, LEFT, {"buff": -1}) + for dot in sorted_dots[:6] + ]), + LaggedStart(*[ + ApplyMethod(dot.to_edge, RIGHT, {"buff": -1}) + for dot in sorted_dots[7:] + ]), + LaggedStart(*[ + FadeOutAndShift(word, RIGHT) + for word in words + ]), + LaggedStart(*[ + FadeOutAndShift(word, LEFT) + for word in words2 + ]), + LaggedStartMap( + Uncreate, + VGroup(arrow, arrow2, *arcs), + ), + run_time=3, + ) + + +class ThumbanailAnimated(Thumbnail): + CONFIG = { + "animate": True, + } + class Thumbnail2(Scene): def construct(self): diff --git a/manimlib/for_3b1b_videos/common_scenes.py b/manimlib/for_3b1b_videos/common_scenes.py index 49c0595c..6c417a98 100644 --- a/manimlib/for_3b1b_videos/common_scenes.py +++ b/manimlib/for_3b1b_videos/common_scenes.py @@ -154,7 +154,8 @@ class PatreonEndScreen(PatreonThanks, PiCreatureScene): "randomize_order": True, "capitalize": True, "name_y_spacing": 0.7, - "thanks_words": "Funded by the community, with special thanks to:", + # "thanks_words": "Funded by the community, with special thanks to:", + "thanks_words": "Early access, name in credits and more at 3b1b.org/support", } def construct(self):