def __init__(self, eq1, eq2, regex, regex2=None, map_list=None, align_char=None): # align equations if align_char: difference_vec = \ self.mob_from_char(eq1, eq1.tex_string, align_char).get_center() - \ self.mob_from_char(eq2, eq2.tex_string, align_char).get_center() else: difference_vec = eq1.get_center() - eq2.get_center() eq2.shift(difference_vec) if regex2 is None: g1 = self.split_by_regex(eq1, regex) g2 = self.split_by_regex(eq2, regex) assert (len(g1.submobjects) == len(g2.submobjects)) trans = ReplacementTransform(g1, g2) AnimationGroup.__init__(self, trans) else: assert (map_list) g1 = self.split_by_regex(eq1, regex) g2 = self.split_by_regex(eq2, regex2) self.g1 = g1 self.g2 = g2 G1 = VGroup() G2 = VGroup() F1 = Group() F2 = Group() g1_nodes = set(pair[0] for pair in map_list) g2_nodes = set(pair[1] for pair in map_list) while map_list: l1 = [g1.submobjects[map_list[0][0]]] l2 = [g2.submobjects[map_list[0][1]]] new_list = [] for i in range(1, len(map_list)): if map_list[i][0] == map_list[0][0]: l2.append(g2.submobjects[map_list[i][1]]) elif map_list[i][1] == map_list[0][1]: l1.append(g1.submobjects[map_list[i][0]]) else: new_list.append(map_list[i]) map_list = new_list G1.submobjects.append(VGroup(*l1)) G2.submobjects.append(VGroup(*l2)) for i in range(len(g1.submobjects)): if i not in g1_nodes: F1.submobjects.append(g1.submobjects[i]) for i in range(len(g2.submobjects)): if i not in g2_nodes: F2.submobjects.append(g2.submobjects[i]) self.g1 = g1 self.g2 = g2 trans = ReplacementTransform(G1, G2) fadeout = FadeOut(F1) fadein = FadeIn(F2) AnimationGroup.__init__(self, trans, fadeout, fadein)
def introduce_bubble(self, *args, **kwargs): if isinstance(args[0], PiCreature): pi_creature = args[0] content = args[1:] else: pi_creature = self.get_primary_pi_creature() content = args bubble_class = kwargs.pop("bubble_class", SpeechBubble) target_mode = kwargs.pop( "target_mode", "thinking" if bubble_class is ThoughtBubble else "speaking" ) bubble_kwargs = kwargs.pop("bubble_kwargs", {}) bubble_removal_kwargs = kwargs.pop("bubble_removal_kwargs", {}) added_anims = kwargs.pop("added_anims", []) anims = [] on_screen_mobjects = self.camera.extract_mobject_family_members( self.get_mobjects() ) def has_bubble(pi): return hasattr(pi, "bubble") and \ pi.bubble is not None and \ pi.bubble in on_screen_mobjects pi_creatures_with_bubbles = list(filter(has_bubble, self.get_pi_creatures())) if pi_creature in pi_creatures_with_bubbles: pi_creatures_with_bubbles.remove(pi_creature) old_bubble = pi_creature.bubble bubble = pi_creature.get_bubble( *content, bubble_class=bubble_class, **bubble_kwargs ) anims += [ ReplacementTransform(old_bubble, bubble), ReplacementTransform(old_bubble.content, bubble.content), pi_creature.change_mode, target_mode ] else: anims.append(PiCreatureBubbleIntroduction( pi_creature, *content, bubble_class=bubble_class, bubble_kwargs=bubble_kwargs, target_mode=target_mode, **kwargs )) anims += [ RemovePiCreatureBubble(pi, **bubble_removal_kwargs) for pi in pi_creatures_with_bubbles ] anims += added_anims self.play(*anims, **kwargs)
def solve_the_equations(self): rects = VGroup(*[ CoverRectangle( equation[0].get_exponent(), stroke_color = color, text = str(num), text_color = color ) for equation, color, num in zip(self.equations, self.colors, self.nums) ]) self.play(DrawBorderThenFill(rects)) self.wait() sps = VGroup() for equation, num, color in zip(self.equations, self.nums, self.colors): sp = TexMobject("x", "^{%d}" % num, "=", "%d" % num) sp[1::2].set_color(color) sp.scale(2) sp.next_to(equation, DOWN, buff = 1) sps.add(sp) rss = VGroup() for num, sp, color in zip(self.nums, sps, self.colors): rs = TexMobject("x", "=", "%d" % num , "^{{1}\\over{%d}}" % num, "=\\sqrt{2}") for tex in (rs[2], rs[3][2]): tex.set_color(color) rs.match_height(sp).move_to(sp) rss.add(rs) tf_anims = [] for sp, rs in zip(sps, rss): tf_anims.append(ReplacementTransform(sp[0], rs[0])) tf_anims.append(ReplacementTransform(sp[1], rs[3][2], path_arc = -TAU/4)) tf_anims.append(ReplacementTransform(sp[2], rs[1])) tf_anims.append(ReplacementTransform(sp[3], rs[2])) tf_anims.append(Write(rs[3][:2])) self.play(FadeIn(sps)) self.wait() self.play(AnimationGroup(*tf_anims), run_time = 2) self.wait() self.play(Write(VGroup(*[rs[4:] for rs in rss]), submobject_mode = "all_at_once")) self.wait() self.play(FadeOut(rects)) self.wait() self.rss = rss
def teacher_holds_up(self, mobject, target_mode="raise_right_hand", **kwargs): mobject.move_to(self.hold_up_spot, DOWN) mobject.shift_onto_screen() mobject_copy = mobject.copy() mobject_copy.shift(DOWN) mobject_copy.fade(1) self.play( ReplacementTransform(mobject_copy, mobject), self.teacher.change, target_mode, )
def rhs_could_be_any_number(self): rhs_list = [ "2", # The journey starts here. "3", # The first number that pops into your head when you hear "2". "\\pi", # The good ol' pi. "\\sqrt{2}", # The (most) famous irrational number. "42", # The answer to everything. "17.29", # TAXI.CAB "-1", # A negative number, because why not? "e", # The (most) famous mathematical constant. "\\dfrac{3+\\sqrt{13}}{2}", # The bronze ratio... and a fraction. "570", # 1/3 of the author. "\\varphi", # The golden ratio. "g_{64}", # Gigigigigigigigantic... "0", # And... stop! "N", ] anything_text = TextMobject("等号右边似乎可以放任何数...") anything_text.set_color(YELLOW) anything_text.to_edge(UP) equations = [ VGroup(ExpTower(order = 5), TexMobject("=", rhs).scale(0.8)).scale(2.5) for rhs in rhs_list ] init_equation = equations[0] init_equation.arrange_submobjects(RIGHT, aligned_edge = DOWN) init_equation.next_to(anything_text, DOWN, buff = self.tower_edge_buff) init_equation.to_edge(LEFT, buff = self.tower_edge_buff) for equation in equations: equation[0].move_to(init_equation[0]) equation[1][0].move_to(init_equation[1][0]) equation[1][1].move_to(init_equation[1][1], aligned_edge = LEFT) self.play(FadeIn(init_equation)) self.wait() self.play(Write(anything_text), run_time = 1) self.play( ShowCreationThenDestruction(SurroundingRectangle(init_equation[1][1])), run_time = 2 ) self.wait() for k, equation in enumerate(equations): if k > 0 and k != len(equations)-1: equation[0].move_to(init_equation[0]) equation[1].move_to(init_equation[1], aligned_edge = LEFT) self.remove(equations[k-1]) self.add(equations[k]) self.wait(1./3) elif k == len(equations)-1: self.wait() self.play(ReplacementTransform(equations[k-1], equations[k])) self.wait() self.anything_text = anything_text self.equation = equations[-1]
def update_mobject(self, new_mob, animate=True): ret = [] if animate: ret.extend( [ReplacementTransform(self.mobject, new_mob, parent=self)]) else: if hasattr(self, "mobject"): self.remove(self.mobject) self.add(new_mob) self.mobject = new_mob return ret
def update_labels(self, new_labels, **kwargs): assert (type(new_labels) == OrderedDict) # make sure labels are different for old_label in self.labels.values(): for new_label in new_labels.values(): assert (id(old_label) != id(new_label)) anims = [] # delete for key, val in new_labels.items(): if val is None: anims.append(Uncreate(self.labels[key])) self.remove(self.labels[key]) del new_labels[key] del self.labels[key] # scale for label in new_labels.values(): if type(label) == Arrow: continue # TODO scale_factor = self.get_label_scale_factor(label, len(new_labels)) label.scale(scale_factor) # place new_labels = self.place_labels(new_labels, **kwargs) # animate if "animate" not in kwargs or kwargs["animate"]: for name in new_labels.keys(): if name in self.labels: anims.extend([ ReplacementTransform(self.labels[name], new_labels[name], parent=self) ]) else: anims.extend([ShowCreation(new_labels[name])]) self.add(new_labels[name]) for name in self.labels: if name not in new_labels: anims.extend([Uncreate(self.labels[name])]) self.remove(self.labels[name]) else: for name in new_labels.keys(): if name not in self.labels: self.add(new_labels[name]) else: self.add(new_labels[name]) self.remove(self.labels[name]) for name in self.labels: if name not in new_labels: self.remove(self.labels[name]) self.labels = new_labels return anims
def build_the_tower(self): highest_order = 6 towers = [ ExpTower(order = k, is_infinite = False) for k in range(1, highest_order + 1) ] towers.append(ExpTower(order = highest_order, is_infinite = True)) heights = list(np.linspace(3, 4.5, highest_order + 2)) for tower, height in zip(towers, heights): tower.set_height(height) init_tower = towers[0] final_tower = towers[-1] self.play(Write(init_tower)) self.wait() for k, tower in enumerate(towers): if k < len(towers) - 2: new_tower = towers[k+1] new_exponent = new_tower.get_exponent() new_base = new_tower.get_base() self.play( ReplacementTransform(tower, new_exponent), Write(new_base), ) self.wait() else: xs = final_tower.get_elements() expdots = final_tower.get_expdots() sur_rect = SurroundingRectangle(expdots) self.play( ReplacementTransform(tower, xs, run_time = 1), Write(expdots, run_time = 2), ) self.play( Indicate(expdots, run_time = 1), ShowCreationThenDestruction(sur_rect, run_time = 2), ) self.wait() break self.tower = final_tower self.highest_order = highest_order
def build_the_equation(self): l_part = self.tower.copy() r_part = TexMobject("=", "2") r_part.match_height(l_part.get_base()) equation = VGroup(l_part, r_part) equation.arrange_submobjects(RIGHT, aligned_edge = DOWN) self.play( ReplacementTransform(self.tower, l_part, run_time = 1), Write(r_part, run_time = 2), ) self.equation = equation
def update_mobject(self, new_mob, animate=True): """ Returns a list of animations of the mobject being updated """ ret = [] if animate: if type(self.mobject) == Line and type(new_mob) == Arrow: ret.extend([ FadeOut(self.mobject, parent=self), FadeIn(new_mob, parent=self), ]) else: ret.extend( [ReplacementTransform(self.mobject, new_mob, parent=self)]) else: if hasattr(self, "mobject"): self.remove(self.mobject) self.add(new_mob) self.mobject = new_mob return ret
def construct(self): # Setup axes_group = self.axes_group pos_axis, time_axis = axes_group[:2] title_pq = self.title_pq walk_q = self.walk_q parts_q = self.parts_q l_arrow = self.l_arrow self.add(axes_group, title_pq, walk_q, l_arrow) walk_q_copy = walk_q.copy() # Q_4 -> P_4 steps_qp = VGroup(*[ TextMobject(text) for text in [ "1. 找到路径终点的位置坐标$h$", "2. 找到最晚一次穿过$\\frac{h}{2}$的时刻", "3. 在这个时刻上进行分割", "4. 将第一段水平翻转", "5. 拼接两个片段" ] ]) for step in steps_qp: step.set_color(YELLOW) step.add_background_rectangle() step1_qp, step2_qp, step3_qp, step4_qp, step5_qp = steps_qp # 1. Find the endpoint step1_qp.next_to(time_axis.number_to_point(4.5), UP) end_horiz_line = DashedLine(LEFT_SIDE, RIGHT_SIDE, color=YELLOW) end_horiz_line.move_to(pos_axis.number_to_point(-4)) end_horiz_line.horizontally_center() end_brace_line = DashedLine(time_axis.number_to_point(8), walk_q.get_end_point()) end_brace = Brace(end_brace_line, direction=RIGHT, color=YELLOW) h = TexMobject("h").set_color(YELLOW) end_brace.put_at_tip(h) self.play(Write(step1_qp), ShowCreation(end_horiz_line), run_time=1) self.play(GrowFromCenter(end_brace), GrowFromCenter(h)) self.wait(1.5) self.play(FadeOut(step1_qp)) # 2. Find the last time it GOES THROUGH half its final value half_point = walk_q.get_arrow_end_point(3) step2_qp.next_to(time_axis.number_to_point(4.5), UP) half_horiz_line = end_horiz_line.copy().shift(2 * UP) half_brace_line = DashedLine(time_axis.number_to_point(4), half_point) half_brace = Brace(half_brace_line, direction=RIGHT, color=YELLOW) half_h = TexMobject("\\frac{h}{2}").set_color(YELLOW) half_brace.put_at_tip(half_h) half_dot = Dot(half_point, color=YELLOW) self.play(FadeIn(step2_qp), run_time=1) self.wait(0.5) self.play( ReplacementTransform(end_brace, half_brace), ReplacementTransform(end_horiz_line, half_horiz_line), ReplacementTransform(h, half_h[0]), Write(half_h[1:]), ) self.play(DrawBorderThenFill(half_dot)) self.wait(1.5) self.play( FadeOut(VGroup(step2_qp, half_horiz_line, half_brace, half_h))) # 3. Split vert_line = DashedLine(2.5 * UP, 2.5 * DOWN, color=YELLOW) vert_line.move_to(half_point) step3_qp.next_to(vert_line, UP) left_part_q, right_part_q = parts_q self.play(ShowCreation(vert_line), Write(step3_qp), run_time=1) self.play( FadeOut(half_dot), left_part_q.shift, 0.5 * DOWN + 0.5 * LEFT, right_part_q.shift, 0.5 * UP + 0.5 * RIGHT, ) self.wait(1.5) self.play(FadeOut(vert_line), FadeOut(step3_qp)) # 4. Flip the first segment horizontally flip_axis = DashedLine(2 * UP, 2 * DOWN, color=GREY) flip_axis.move_to(left_part_q) step4_qp.next_to(flip_axis, DOWN) self.play( ShowCreation(flip_axis), Write(step4_qp), run_time=1, ) self.play( left_part_q.flip, Animation(flip_axis), ) self.wait(1.5) self.play(FadeOut(step4_qp), FadeOut(flip_axis)) # 5. Put the pieces together step5_qp.shift(2.5 * DOWN) flip_arrow_anims = walk_q.get_flip_arrows_animation(3, color=ORANGE) self.play(Write(step5_qp), run_time=1) self.wait(0.5) self.play(flip_arrow_anims, right_part_q.set_color, ORANGE) self.wait(0.5) self.play( left_part_q.shift, 2.5 * UP + 0.5 * RIGHT, right_part_q.shift, 3.5 * UP + 0.5 * LEFT, Animation(step5_qp), ) self.wait(0.5) self.play(FadeOut(step5_qp)) self.wait(1.5) # Now Reset self.play(FadeOut(walk_q)) self.play(FadeIn(walk_q_copy)) self.wait()
def construct(self): # Setup pos_axis = NumberLine(x_min=-3.5, x_max=3.5, color=WHITE) pos_axis.rotate(np.pi / 2) pos_axis.add_tip() time_axis = NumberLine(x_min=0, x_max=9.5, color=WHITE) time_axis.add_tip() pos_text = TextMobject("位置") pos_text.next_to(pos_axis.number_to_point(3.5), LEFT) time_text = TextMobject("时间") time_text.next_to(time_axis.number_to_point(9.5), DOWN) group = VGroup(pos_axis, time_axis, pos_text, time_text) group.to_edge(LEFT, buff=2) self.add(group) # Some preparations sequences = [ "UDDDUDUD", "DUUDDDUD", "DDUUUDUD", "UUUDDUDU", "UDDDUDUD" ] nums = [1, 3, 7, -1, 1] walks = [ RandomWalk1DArrow(sequence).move_start_to( pos_axis.number_to_point(0)) for sequence in sequences ] parts = [walk.split_at(num) for walk, num in zip(walks, nums)] split_lines = [ DashedLine(TOP, BOTTOM, color = GREY) \ .move_to(time_axis.number_to_point(num+1)) for num in nums ] zero_words = [ TextMobject("“正点到家” \\\\ $P_%s$" % str((num+1)//2)) \ .next_to(line, LEFT).shift(2.8 * DOWN).set_color(ORANGE) for num, line in zip(nums, split_lines) ] nocross_words = [ TextMobject("“不经过家门” \\\\ $Q_%s$" % str(4-(num+1)//2)) \ .next_to(line, RIGHT).shift(2.8 * DOWN).set_color(GREEN) for num, line in zip(nums, split_lines) ] for words, direction in zip([zero_words, nocross_words], [RIGHT, LEFT]): for word in words: text, symbol = VGroup(word[:-2]), VGroup(word[-2:]) symbol.scale(1.5) symbol.next_to(text, DOWN, aligned_edge=direction) word.add_background_rectangle() # Demonstrate how to split self.add(walks[0], split_lines[0], zero_words[0], nocross_words[0]) l = len(walks) for k in range(l - 1): cur_walk = walks[k] cur_line = split_lines[k] cur_zero_word = zero_words[k] cur_nocross_word = nocross_words[k] next_walk = walks[k + 1] next_line = split_lines[k + 1] next_zero_word = zero_words[k + 1] next_nocross_word = nocross_words[k + 1] part1, part2 = parts[k] cur_walk.save_state() self.play( part1.set_color, ORANGE, part2.set_color, GREEN, ) self.wait() self.play(cur_walk.restore) self.wait() self.play( ReplacementTransform(cur_walk, next_walk), ReplacementTransform(cur_line, next_line), ReplacementTransform(cur_zero_word, next_zero_word), ReplacementTransform(cur_nocross_word, next_nocross_word), ) self.wait()
def show_the_solution(self): # Prepare for the solution pre_solve = VGroup(*self.question[::2]) self.play( ReplacementTransform(pre_solve, self.solve), FadeOut(self.question[1]), run_time = 1, ) self.wait() # Manipulate LHS old_l_part, r_part = self.equation new_l_part = ExpTower(order = self.highest_order+1, is_infinite = True) new_l_part.match_height(old_l_part) new_l_part.next_to(r_part, LEFT, aligned_edge = DOWN) old_rect, new_rect = rects = [ CoverRectangle(part, text = "2") for part in (old_l_part, new_l_part.get_exponent()) ] old_two, new_two = twos = [ rect.get_text_mob() for rect in rects ] self.play(DrawBorderThenFill(old_rect, run_time = 1)) self.wait() self.play( ReplacementTransform(old_l_part, new_l_part), ReplacementTransform(old_rect, new_rect), ) self.wait() new_equation = VGroup(new_l_part, r_part, new_rect) new_equation.generate_target() new_equation.target.scale(0.8) new_equation.target.shift(UP) self.play(MoveToTarget(new_equation)) self.wait() # A little bit clean-up source_eq = VGroup(*[ mob.copy() for mob in (new_l_part.get_base(), new_two, r_part[0], r_part[1]) ]) source_eq.generate_target() target_eq = TexMobject("x", "^2", "=", "2") target_eq.scale(3).next_to(source_eq, DOWN, buff = 1) for k, (old_part, new_part) in enumerate(zip(source_eq.target, target_eq)): old_part.move_to(new_part) if k == 1: old_part.scale(0.5) old_part.shift(RIGHT/4) self.play( FadeOut(new_rect), MoveToTarget(source_eq), ) self.wait() # Reveal the final answer result = TexMobject("x", "=", "\\sqrt", "2") result.set_height(source_eq.get_height() * 0.7) result.move_to(source_eq) self.play(*[ ReplacementTransform(source_eq[m], result[n], path_arc = angle) for m, n, angle in [(0, 0, 0), (1, 2, -TAU/3), (2, 1, 0), (3, 3, 0)] ]) self.wait() qed = QEDSymbol() qed.next_to(result, RIGHT, aligned_edge = DOWN, buff = 1.5) self.play(FadeIn(qed)) self.wait()