diff --git a/efvgt.py b/efvgt.py index 0d2fe167..d9cc05af 100644 --- a/efvgt.py +++ b/efvgt.py @@ -18,6 +18,7 @@ from topics.combinatorics import * from topics.numerals import * from topics.three_dimensions import * from topics.objects import * +from topics.complex_numbers import * from scene import Scene from scene.zoomed_scene import ZoomedScene from scene.reconfigurable_scene import ReconfigurableScene @@ -124,12 +125,12 @@ class Anniversary(TeacherStudentsScene): def complain(self): self.student_says( - "Why are you \\\\ talking so fast?", + "Why were you \\\\ talking so fast?", student_index = 0, target_mode = "sassy", ) self.change_student_modes(*["sassy"]*3) - self.play(self.get_teacher().change_mode, "guilty") + self.play(self.get_teacher().change_mode, "shruggie") self.dither(2) def get_party_hats(self): @@ -172,12 +173,280 @@ class Anniversary(TeacherStudentsScene): ] return confetti_spirils - - - - - - +class WatchingScreen(PiCreatureScene): + CONFIG = { + "screen_height" : 5.5 + } + def create_pi_creatures(self): + randy = Randolph().to_corner(DOWN+LEFT) + return VGroup(randy) + + def construct(self): + screen = Rectangle(height = 9, width = 16) + screen.scale_to_fit_height(self.screen_height) + screen.to_corner(UP+RIGHT) + + self.add(screen) + for mode in "erm", "pondering", "confused": + self.dither() + self.change_mode(mode) + self.play(Animation(screen)) + self.dither() + +class LetsStudyTheBasics(TeacherStudentsScene): + def construct(self): + self.teacher_says("Let's learn some \\\\ group theory.") + self.change_student_modes(*["happy"]*3) + self.dither(2) + +class QuickExplanation(ComplexTransformationScene): + CONFIG = { + "plane_config" : { + "x_line_frequency" : 1, + "y_line_frequency" : 1, + "secondary_line_ratio" : 1, + "space_unit_to_x_unit" : 1.5, + "space_unit_to_y_unit" : 1.5, + }, + "background_fade_factor" : 0.2, + "background_label_scale_val" : 0.7, + "velocity_color" : RED, + "position_color" : YELLOW, + } + def construct(self): + # self.add_transformable_plane() + self.add_equation() + self.add_explanation() + self.add_vectors() + + def add_equation(self): + equation = TexMobject( + "\\frac{d(e^{it})}{dt}", + "=", + "i", "e^{it}" + ) + equation[0].highlight(self.velocity_color) + equation[-1].highlight(self.position_color) + equation.add_background_rectangle() + brace = Brace(equation, UP) + equation.add(brace) + brace_text = TextMobject( + "Velocity vector", "is a", + "$90^\\circ$ \\\\ rotation", + "of", "position vector" + ) + brace_text[0].highlight(self.velocity_color) + brace_text[-1].highlight(self.position_color) + brace_text.add_background_rectangle() + brace_text.scale(0.8) + brace_text.to_corner(UP+LEFT, buff = MED_SMALL_BUFF) + equation.next_to(brace_text, DOWN) + + self.add_foreground_mobjects(brace_text, equation) + self.brace_text = brace_text + + def add_explanation(self): + words = TextMobject(""" + Only a walk around the unit + circle at rate 1 satisfies both + this property and e^0 = 1. + """) + words.scale(0.8) + words.add_background_rectangle() + words.to_corner(UP+RIGHT, buff = MED_SMALL_BUFF) + arrow = Arrow(RIGHT, 1.5*LEFT, color = WHITE) + arrow.to_edge(UP) + + self.add(words, arrow) + + def add_vectors(self): + right = self.z_to_point(1) + s_vector = Arrow( + ORIGIN, right, + tip_length = 0.2, + buff = 0, + color = self.position_color, + ) + + v_vector = s_vector.copy().rotate(np.pi/2) + v_vector.highlight(self.velocity_color) + circle = Circle( + radius = self.z_to_point(1)[0], + color = self.position_color + ) + + self.dither(2) + self.play(ShowCreation(s_vector)) + self.play(ReplacementTransform( + s_vector.copy(), v_vector, path_arc = np.pi/2 + )) + self.dither() + self.play(v_vector.shift, right) + self.dither() + self.vectors = VGroup(s_vector, v_vector) + + kwargs = { + "rate_func" : None, + "run_time" : 5, + } + rotation = Rotating(self.vectors, about_point = ORIGIN, **kwargs) + self.play( + ShowCreation(circle, **kwargs), + rotation + ) + self.play(rotation) + self.play(rotation) + +class SymmetriesOfSquare(ThreeDScene): + CONFIG = { + "square_config" : { + "side_length" : 2, + "stroke_width" : 0, + "fill_color" : BLUE, + "fill_opacity" : 0.75, + }, + } + def construct(self): + ##REMOVE## + should_skip_animations = self.skip_animations + self.skip_animations = True + ## + + self.add_title() + self.ask_about_square_symmetry() + self.talk_through_90_degree_rotation() + self.talk_through_vertical_flip() + self.skip_animations = should_skip_animations + self.confused_by_lack_of_labels() + self.add_labels() + self.show_full_group() + self.show_top_actions() + self.show_bottom_actions() + self.name_dihedral_group() + + def add_title(self): + title = TextMobject("Groups", "$\\leftrightarrow$", "Symmetry") + title.to_edge(UP) + + for index in 0, 2: + self.play(Write(title[index], run_time = 1)) + self.play(GrowFromCenter(title[1])) + self.dither() + + self.title = title + + def ask_about_square_symmetry(self): + brace = Brace(self.title[-1]) + q_marks = brace.get_text("???") + + self.square = Square(**self.square_config) + + self.play(DrawBorderThenFill(self.square)) + self.play(GrowFromCenter(brace), Write(q_marks)) + self.rotate_square() + self.dither() + for axis in UP, UP+RIGHT: + self.flip_square(axis) + self.dither() + self.rotate_square(-np.pi) + self.dither() + self.play(*map(FadeOut, [brace, q_marks])) + + def talk_through_90_degree_rotation(self): + square_radius = np.linalg.norm(self.square.get_corner(UP+RIGHT)) + arc = Arc( + radius = square_radius + SMALL_BUFF, + start_angle = np.pi/4 + SMALL_BUFF, + angle = np.pi/2 - 2*SMALL_BUFF, + color = YELLOW + ) + arc.add_tip() + arcs = VGroup(arc, *[ + arc.copy().rotate(i*np.pi/2) + for i in range(1, 4) + ]) + + self.play(*map(ShowCreation, arcs)) + self.dither() + self.rotate_square(np.pi/2, run_time = 2) + self.dither() + self.play(FadeOut(arcs)) + self.dither() + + def talk_through_vertical_flip(self): + self.flip_square(UP, run_time = 2) + self.dither() + + def confused_by_lack_of_labels(self): + randy = Randolph(mode = "confused") + randy.next_to(self.square, DOWN+LEFT) + randy.shift_onto_screen() + self.play(FadeIn(randy)) + for axis in OUT, RIGHT, UP: + self.rotate_square( + angle = np.pi, axis = axis, + added_anims = [randy.look_at, self.square.points[0]] + ) + self.play(Blink(randy)) + self.dither() + + self.randy = randy + + def add_labels(self): + pass + + def show_full_group(self): + pass + + def show_top_actions(self): + pass + + def show_bottom_actions(self): + pass + + def name_dihedral_group(self): + pass + + ########## + + def rotate_square( + self, + angle = np.pi/2, + axis = OUT, + square = None, + show_axis = False, + added_anims = None, + **kwargs + ): + if square is None: + assert hasattr(self, "square") + square = self.square + added_anims = added_anims or [] + rotation = Rotate(square, angle = angle, axis = axis, **kwargs) + if hasattr(square, "labels"): + for label in rotation.target_mobject.labels: + label.rotate_in_place(-angle, axis) + + if show_axis: + axis_line = DashedLine(2*axis, -2*axis) + self.play( + ShowCreation(axis_line), + Animation(square) + ) + self.play(rotation, *added_anims) + if show_axis: + self.play( + FadeOut(axis_line), + Animation(square) + ) + + def flip_square(self, axis = UP, **kwargs): + self.rotate_square( + angle = np.pi, + axis = axis, + show_axis = True, + **kwargs + ) diff --git a/topics/objects.py b/topics/objects.py index 6b51fdee..6f2351d7 100644 --- a/topics/objects.py +++ b/topics/objects.py @@ -20,9 +20,9 @@ class PartyHat(SVGMobject): "stroke_width" : 0, "fill_opacity" : 1, "propogate_style_to_family" : True, - "frills_color" : MAROON_B, + "frills_colors" : [MAROON_B, PURPLE], "cone_color" : RED, - "dots_color" : YELLOW, + "dots_colors" : [YELLOW], } NUM_FRILLS = 7 NUM_DOTS = 6 @@ -36,9 +36,9 @@ class PartyHat(SVGMobject): self.cone = self[self.NUM_FRILLS] self.dots = VGroup(*self[self.NUM_FRILLS+1:]) - self.frills.highlight(self.frills_color) + self.frills.gradient_highlight(*self.frills_colors) self.cone.highlight(self.cone_color) - self.dots.highlight(self.dots_color) + self.dots.gradient_highlight(*self.dots_colors)