def __init__(self, source, target): matching_line = Line(0.5 * DOWN, 0.5 * UP) def matched_objects(source, target): matched = VGroup(source.copy(), matching_line.copy(), target.copy()) matched.arrange_submobjects(direction=DOWN) return matched final_mobj = VGroup( *(matched_objects(s, t) for (s, t) in zip(source.submobjects, target.submobjects))) self.buff = 2 * DEFAULT_MOBJECT_TO_MOBJECT_BUFFER final_mobj.arrange_submobjects(buff=self.buff) final_mobj.center() def match_animation(source, target, matched): return AnimationGroup(Transform(source, matched.submobjects[0]), ShowCreation(matched.submobjects[1]), Transform(target, matched.submobjects[2])) self.match_animations = [ match_animation(s, t, m) for (s, t, m) in zip( source.submobjects, target.submobjects, final_mobj.submobjects) ] self.matching = VGroup(*[mob[1] for mob in final_mobj])
def arrange_subparts(self, *subparts): for i, piece in enumerate(subparts): piece.rotate(i * np.pi / 12, about_point=ORIGIN) p1, p2, p3, p4, p5, p6, p7 = subparts center_row = VGroup(p1, p4, p7) center_row.arrange_submobjects(RIGHT, buff=0) for p in p2, p3, p5, p6: p.scale_to_fit_width(p1.get_width()) p2.move_to(p1.get_top(), DOWN + LEFT) p3.move_to(p1.get_bottom(), UP + LEFT) p5.move_to(p4.get_top(), DOWN + LEFT) p6.move_to(p4.get_bottom(), UP + LEFT)
def arrange_subparts(self, *subparts): for i, piece in enumerate(subparts): piece.rotate(i*np.pi/12) p1, p2, p3, p4, p5, p6, p7 = subparts center_row = VGroup(p1, p4, p7) center_row.arrange_submobjects(RIGHT, buff = 0) for p in p2, p3, p5, p6: p.scale_to_fit_width(p1.get_width()) p2.move_to(p1.get_top(), DOWN+LEFT) p3.move_to(p1.get_bottom(), UP+LEFT) p5.move_to(p4.get_top(), DOWN+LEFT) p6.move_to(p4.get_bottom(), UP+LEFT)
def construct(self): morty = Mortimer() morty.next_to(ORIGIN, DOWN) n_patrons = len(self.specific_patrons) special_thanks = TextMobject("Special thanks") special_thanks.highlight(YELLOW) special_thanks.to_edge(UP) patreon_logo = PatreonLogo() patreon_logo.next_to(morty, UP, buff=MED_LARGE_BUFF) patrons = map(TextMobject, self.specific_patrons) patron_groups = [] index = 0 counter = 0 while index < len(patrons): next_index = index + self.patron_group_size group = VGroup(*patrons[index:next_index]) group.arrange_submobjects(DOWN, aligned_edge=LEFT) if counter % 2 == 0: group.to_edge(LEFT) else: group.to_edge(RIGHT) patron_groups.append(group) index = next_index counter += 1 self.play( morty.change_mode, "gracious", DrawBorderThenFill(patreon_logo), ) self.play(Write(special_thanks, run_time=1)) for i, group in enumerate(patron_groups): anims = [ FadeIn( group, run_time=2, submobject_mode="lagged_start", lag_factor=4, ), morty.look_at, group.get_top(), ] if i >= 2: anims.append(FadeOut(patron_groups[i - 2])) self.play(*anims) self.play(morty.look_at, group.get_bottom()) self.play(Blink(morty))
def scroll_through_patrons(self): logo_box = Square(side_length=2.5) logo_box.to_corner(DOWN + LEFT, buff=MED_LARGE_BUFF) total_width = SPACE_WIDTH - logo_box.get_right()[0] black_rect = Rectangle(fill_color=BLACK, fill_opacity=1, stroke_width=0, width=2 * SPACE_WIDTH, height=1.1 * SPACE_HEIGHT) black_rect.to_edge(UP, buff=0) line = DashedLine(SPACE_WIDTH * LEFT, SPACE_WIDTH * RIGHT) line.move_to(black_rect, DOWN) line.shift(SMALL_BUFF * SMALL_BUFF * DOWN) self.add(line) patrons = VGroup(*map(TextMobject, self.specific_patrons)) patrons.scale(self.patron_scale_val) for patron in patrons: if patron.get_width() > self.max_patron_width: patron.scale_to_fit_width(self.max_patron_width) columns = VGroup(*[ VGroup(*patrons[i::self.n_patron_columns]).arrange_submobjects( DOWN, buff=MED_SMALL_BUFF) for i in range(self.n_patron_columns) ]) columns.arrange_submobjects( RIGHT, buff=LARGE_BUFF, aligned_edge=UP, ) columns.scale_to_fit_width(total_width - 1) columns.next_to(black_rect, DOWN, 3 * LARGE_BUFF) columns.to_edge(RIGHT) self.play( columns.next_to, SPACE_HEIGHT * DOWN, UP, LARGE_BUFF, columns.to_edge, RIGHT, Animation(black_rect), rate_func=None, run_time=self.run_time, )
class TeacherStudentsScene(PiCreatureScene): CONFIG = { "student_colors": [BLUE_D, BLUE_E, BLUE_C], "student_scale_factor": 0.8, "seconds_to_blink": 2, } def create_pi_creatures(self): self.teacher = Mortimer() self.teacher.to_corner(DOWN + RIGHT) self.teacher.look(DOWN + LEFT) self.students = VGroup( *[Randolph(color=c) for c in self.student_colors]) self.students.arrange_submobjects(RIGHT) self.students.scale(self.student_scale_factor) self.students.to_corner(DOWN + LEFT) self.teacher.look_at(self.students[-1].eyes) for student in self.students: student.look_at(self.teacher.eyes) return [self.teacher] + list(self.students) def get_teacher(self): return self.teacher def get_students(self): return self.students def teacher_says(self, *content, **kwargs): return self.pi_creature_says(self.get_teacher(), *content, **kwargs) def student_says(self, *content, **kwargs): if "target_mode" not in kwargs: target_mode = random.choice([ "raise_right_hand", "raise_left_hand", ]) kwargs["target_mode"] = target_mode student = self.get_students()[kwargs.get("student_index", 1)] return self.pi_creature_says(student, *content, **kwargs) def teacher_thinks(self, *content, **kwargs): return self.pi_creature_thinks(self.get_teacher(), *content, **kwargs) def student_thinks(self, *content, **kwargs): student = self.get_students()[kwargs.get("student_index", 1)] return self.pi_creature_thinks(student, *content, **kwargs) def change_student_modes(self, *modes, **kwargs): added_anims = kwargs.pop("added_anims", []) self.play(self.get_student_changes(*modes, **kwargs), *added_anims) def get_student_changes(self, *modes, **kwargs): pairs = zip(self.get_students(), modes) pairs = [(s, m) for s, m in pairs if m is not None] start = VGroup(*[s for s, m in pairs]) target = VGroup(*[s.copy().change_mode(m) for s, m in pairs]) if "look_at_arg" in kwargs: for pi in target: pi.look_at(kwargs["look_at_arg"]) submobject_mode = kwargs.get("submobject_mode", "lagged_start") return Transform(start, target, submobject_mode=submobject_mode, run_time=2) def zoom_in_on_thought_bubble(self, bubble=None, radius=SPACE_HEIGHT + SPACE_WIDTH): if bubble is None: for pi in self.get_pi_creatures(): if hasattr(pi, "bubble") and isinstance( pi.bubble, ThoughtBubble): bubble = pi.bubble break if bubble is None: raise Exception("No pi creatures have a thought bubble") vect = -bubble.get_bubble_center() def func(point): centered = point + vect return radius * centered / np.linalg.norm(centered) self.play( * [ApplyPointwiseFunction(func, mob) for mob in self.get_mobjects()])
class TeacherStudentsScene(Scene): def setup(self): self.teacher = Mortimer() self.teacher.to_corner(DOWN + RIGHT) self.teacher.look(DOWN+LEFT) self.students = VGroup(*[ Randolph(color = c) for c in BLUE_D, BLUE_C, BLUE_E ]) self.students.arrange_submobjects(RIGHT) self.students.scale(0.8) self.students.to_corner(DOWN+LEFT) self.teacher.look_at(self.students[-1].eyes) for student in self.students: student.look_at(self.teacher.eyes) for pi_creature in self.get_everyone(): pi_creature.bubble = None self.add(*self.get_everyone()) def get_teacher(self): return self.teacher def get_students(self): return self.students def get_everyone(self): return [self.get_teacher()] + list(self.get_students()) def get_bubble_intro_animation(self, content, bubble_type, pi_creature, **bubble_kwargs): bubble = pi_creature.get_bubble(bubble_type, **bubble_kwargs) bubble.add_content(content) bubble.resize_to_content() if pi_creature.bubble: content_intro_anims = [ Transform(pi_creature.bubble, bubble), Transform(pi_creature.bubble.content, bubble.content) ] else: content_intro_anims = [ FadeIn(bubble), Write(content), ] pi_creature.bubble = bubble return content_intro_anims def introduce_bubble(self, content, bubble_type, pi_creature, target_mode = None, added_anims = [], **bubble_kwargs): if all(map(lambda s : isinstance(s, str), content)): content = TextMobject(*content) elif len(content) == 1 and isinstance(content[0], Mobject): content = content[0] else: raise Exception("Invalid content type") anims = [] #Remove other bubbles for p in self.get_everyone(): if (p.bubble is not None) and (p is not pi_creature): anims += [ FadeOut(p.bubble), FadeOut(p.bubble.content) ] p.bubble = None anims.append(ApplyMethod(p.change_mode, "plain")) #Bring in new bubble anims += self.get_bubble_intro_animation( content, bubble_type, pi_creature, **bubble_kwargs ) #Add changing mode if not target_mode: if bubble_type is "speech": target_mode = "speaking" else: target_mode = "pondering" anims.append( ApplyMethod( pi_creature.change_mode, target_mode, ) ) self.play(*anims + added_anims) return pi_creature.bubble def teacher_says(self, *content, **kwargs): return self.introduce_bubble( content, "speech", self.get_teacher(), **kwargs ) def student_says(self, *content, **kwargs): if "target_mode" not in kwargs: target_mode = random.choice([ "raise_right_hand", "raise_left_hand", ]) kwargs["target_mode"] = target_mode student = self.get_students()[kwargs.get("student_index", 1)] return self.introduce_bubble(content, "speech", student, **kwargs) def teacher_thinks(self, *content, **kwargs): return self.introduce_bubble( content, "thought", self.get_teacher(), **kwargs ) def student_thinks(self, *content, **kwargs): student = self.get_students()[kwargs.get("student_index", 1)] return self.introduce_bubble(content, "thought", student, **kwargs) def random_blink(self, num_times = 1): for x in range(num_times): pi_creature = random.choice(self.get_everyone()) self.play(Blink(pi_creature)) Scene.dither(self) def dither(self, time = 1): self.random_blink(num_times = max(time/2, 1)) def change_student_modes(self, *modes, **kwargs): added_anims = kwargs.get("added_anims", []) pairs = zip(self.get_students(), modes) pairs = [(s, m) for s, m in pairs if m is not None] start = VGroup(*[s for s, m in pairs]) target = VGroup(*[s.copy().change_mode(m) for s, m in pairs]) self.play( Transform( start, target, submobject_mode = "lagged_start", run_time = 2 ), *added_anims ) def zoom_in_on_thought_bubble(self, bubble = None, radius = SPACE_HEIGHT+SPACE_WIDTH): if bubble is None: for pi in self.get_everyone(): if hasattr(pi, "bubble") and isinstance(pi.bubble, ThoughtBubble): bubble = pi.bubble break if bubble is None: raise Exception("No pi creatures have a thought bubble") vect = -bubble.get_bubble_center() def func(point): centered = point+vect return radius*centered/np.linalg.norm(centered) self.play(*[ ApplyPointwiseFunction(func, mob) for mob in self.get_mobjects() ])
def matched_objects(source, target): matched = VGroup(source.copy(), matching_line.copy(), target.copy()) matched.arrange_submobjects(direction=DOWN) return matched
class TeacherStudentsScene(PiCreatureScene): CONFIG = { "student_colors" : [BLUE_D, BLUE_C, BLUE_E], "student_scale_factor" : 0.8, "seconds_to_blink" : 2, } def create_pi_creatures(self): self.teacher = Mortimer() self.teacher.to_corner(DOWN + RIGHT) self.teacher.look(DOWN+LEFT) self.students = VGroup(*[ Randolph(color = c) for c in self.student_colors ]) self.students.arrange_submobjects(RIGHT) self.students.scale(self.student_scale_factor) self.students.to_corner(DOWN+LEFT) self.teacher.look_at(self.students[-1].eyes) for student in self.students: student.look_at(self.teacher.eyes) return [self.teacher] + list(self.students) def get_teacher(self): return self.teacher def get_students(self): return self.students def teacher_says(self, *content, **kwargs): return self.pi_creature_says( self.get_teacher(), *content, **kwargs ) def student_says(self, *content, **kwargs): if "target_mode" not in kwargs: target_mode = random.choice([ "raise_right_hand", "raise_left_hand", ]) kwargs["target_mode"] = target_mode student = self.get_students()[kwargs.get("student_index", 1)] return self.pi_creature_says( student, *content, **kwargs ) def teacher_thinks(self, *content, **kwargs): return self.pi_creature_thinks( self.get_teacher(), *content, **kwargs ) def student_thinks(self, *content, **kwargs): student = self.get_students()[kwargs.get("student_index", 1)] return self.pi_creature_thinks(student, *content, **kwargs) def change_student_modes(self, *modes, **kwargs): added_anims = kwargs.get("added_anims", []) pairs = zip(self.get_students(), modes) pairs = [(s, m) for s, m in pairs if m is not None] start = VGroup(*[s for s, m in pairs]) target = VGroup(*[s.copy().change_mode(m) for s, m in pairs]) if "look_at_arg" in kwargs: for pi in target: pi.look_at(kwargs["look_at_arg"]) self.play( Transform( start, target, submobject_mode = "lagged_start", run_time = 2 ), *added_anims ) def zoom_in_on_thought_bubble(self, bubble = None, radius = SPACE_HEIGHT+SPACE_WIDTH): if bubble is None: for pi in self.get_pi_creatures(): if hasattr(pi, "bubble") and isinstance(pi.bubble, ThoughtBubble): bubble = pi.bubble break if bubble is None: raise Exception("No pi creatures have a thought bubble") vect = -bubble.get_bubble_center() def func(point): centered = point+vect return radius*centered/np.linalg.norm(centered) self.play(*[ ApplyPointwiseFunction(func, mob) for mob in self.get_mobjects() ])