3b1b-manim/old_projects/efvgt.py
2018-08-21 19:15:16 -07:00

3399 lines
106 KiB
Python

from big_ol_pile_of_manim_imports import *
ADDER_COLOR = GREEN
MULTIPLIER_COLOR = YELLOW
def normalize(vect):
norm = get_norm(vect)
if norm == 0:
return OUT
else:
return vect/norm
def get_composite_rotation_angle_and_axis(angles, axes):
angle1, axis1 = 0, OUT
for angle2, axis2 in zip(angles, axes):
## Figure out what (angle3, axis3) is the same
## as first applying (angle1, axis1), then (angle2, axis2)
axis2 = normalize(axis2)
dot = np.dot(axis2, axis1)
cross = np.cross(axis2, axis1)
angle3 = 2*np.arccos(
np.cos(angle2/2)*np.cos(angle1/2) - \
np.sin(angle2/2)*np.sin(angle1/2)*dot
)
axis3 = (
np.sin(angle2/2)*np.cos(angle1/2)*axis2 + \
np.cos(angle2/2)*np.sin(angle1/2)*axis1 + \
np.sin(angle2/2)*np.sin(angle1/2)*cross
)
axis3 = normalize(axis3)
angle1, axis1 = angle3, axis3
if angle1 > np.pi:
angle1 -= 2*np.pi
return angle1, axis1
class ConfettiSpiril(Animation):
CONFIG = {
"x_start" : 0,
"spiril_radius" : 0.5,
"num_spirils" : 4,
"run_time" : 10,
"rate_func" : None,
}
def __init__(self, mobject, **kwargs):
digest_config(self, kwargs)
mobject.next_to(self.x_start*RIGHT + FRAME_Y_RADIUS*UP, UP)
self.total_vert_shift = \
FRAME_HEIGHT + mobject.get_height() + 2*MED_SMALL_BUFF
Animation.__init__(self, mobject, **kwargs)
def update_submobject(self, submobject, starting_submobject, alpha):
submobject.points = np.array(starting_submobject.points)
def update_mobject(self, alpha):
Animation.update_mobject(self, alpha)
angle = alpha*self.num_spirils*2*np.pi
vert_shift = alpha*self.total_vert_shift
start_center = self.mobject.get_center()
self.mobject.shift(self.spiril_radius*OUT)
self.mobject.rotate(angle, axis = UP, about_point = start_center)
self.mobject.shift(vert_shift*DOWN)
def get_confetti_animations(num_confetti_squares):
colors = [RED, YELLOW, GREEN, BLUE, PURPLE, RED]
confetti_squares = [
Square(
side_length = 0.2,
stroke_width = 0,
fill_opacity = 0.75,
fill_color = random.choice(colors),
)
for x in range(num_confetti_squares)
]
confetti_spirils = [
ConfettiSpiril(
square,
x_start = 2*random.random()*FRAME_X_RADIUS - FRAME_X_RADIUS,
rate_func = squish_rate_func(lambda t : t, a, a+0.5)
)
for a, square in zip(
np.linspace(0, 0.5, num_confetti_squares),
confetti_squares
)
]
return confetti_spirils
class Anniversary(TeacherStudentsScene):
CONFIG = {
"num_confetti_squares" : 50,
}
def construct(self):
self.celebrate()
self.complain()
def celebrate(self):
title = TextMobject("2 year Anniversary!")
title.scale(1.5)
title.to_edge(UP)
first_video = Rectangle(
height = 2, width = 2*(16.0/9),
stroke_color = WHITE,
fill_color = "#111111",
fill_opacity = 0.75,
)
first_video.next_to(self.get_teacher(), UP+LEFT)
first_video.shift(RIGHT)
formula = TexMobject("e^{\\pi i} = -1")
formula.move_to(first_video)
first_video.add(formula)
hats = self.get_party_hats()
confetti_spirils = get_confetti_animations(
self.num_confetti_squares
)
self.play(
Write(title, run_time = 2),
*[
ApplyMethod(pi.change_mode, "hooray")
for pi in self.get_pi_creatures()
]
)
self.play(
DrawBorderThenFill(
hats,
submobject_mode = "lagged_start",
rate_func = None,
run_time = 2,
),
*confetti_spirils + [
Succession(
Animation(pi, run_time = 2),
ApplyMethod(pi.look, UP+LEFT),
ApplyMethod(pi.look, UP+RIGHT),
Animation(pi),
ApplyMethod(pi.look_at, first_video),
rate_func = None
)
for pi in self.get_students()
] + [
Succession(
Animation(self.get_teacher(), run_time = 2),
Blink(self.get_teacher()),
Animation(self.get_teacher(), run_time = 2),
ApplyMethod(self.get_teacher().change_mode, "raise_right_hand"),
rate_func = None
),
DrawBorderThenFill(
first_video,
run_time = 10,
rate_func = squish_rate_func(smooth, 0.5, 0.7)
)
]
)
self.change_student_modes(*["confused"]*3)
def complain(self):
self.student_says(
"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, "shruggie")
self.wait(2)
def get_party_hats(self):
hats = VGroup(*[
PartyHat(
pi_creature = pi,
height = 0.5*pi.get_height()
)
for pi in self.get_pi_creatures()
])
max_angle = np.pi/6
for hat in hats:
hat.rotate(
random.random()*2*max_angle - max_angle,
about_point = hat.get_bottom()
)
return hats
class HomomophismPreview(Scene):
def construct(self):
raise Exception("Meant as a place holder, not to be excecuted")
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.set_height(self.screen_height)
screen.to_corner(UP+RIGHT)
self.add(screen)
for mode in "erm", "pondering", "confused":
self.wait()
self.change_mode(mode)
self.play(Animation(screen))
self.wait()
class LetsStudyTheBasics(TeacherStudentsScene):
def construct(self):
self.teacher_says("Let's learn some \\\\ group theory!")
self.change_student_modes(*["hooray"]*3)
self.wait(2)
class JustGiveMeAQuickExplanation(TeacherStudentsScene):
def construct(self):
self.student_says(
"I ain't got \\\\ time for this!",
target_mode = "angry",
)
self.play(*it.chain(*[
[
pi.change_mode, "hesitant",
pi.look_at, self.get_students()[1].eyes
]
for pi in self.get_students()[::2]
]))
self.wait(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].set_color(self.velocity_color)
equation[-1].set_color(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].set_color(self.velocity_color)
brace_text[-1].set_color(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.set_color(self.velocity_color)
circle = Circle(
radius = self.z_to_point(1)[0],
color = self.position_color
)
self.wait(2)
self.play(ShowCreation(s_vector))
self.play(ReplacementTransform(
s_vector.copy(), v_vector, path_arc = np.pi/2
))
self.wait()
self.play(v_vector.shift, right)
self.wait()
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,
},
"dashed_line_config" : {},
}
def construct(self):
self.add_title()
self.ask_about_square_symmetry()
self.talk_through_90_degree_rotation()
self.talk_through_vertical_flip()
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.wait()
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.wait()
for axis in UP, UP+RIGHT:
self.flip_square(axis)
self.wait()
self.rotate_square(-np.pi)
self.wait()
self.play(*list(map(FadeOut, [brace, q_marks])))
def talk_through_90_degree_rotation(self):
arcs = self.get_rotation_arcs(self.square, np.pi/2)
self.play(*list(map(ShowCreation, arcs)))
self.wait()
self.rotate_square(np.pi/2, run_time = 2)
self.wait()
self.play(FadeOut(arcs))
self.wait()
def talk_through_vertical_flip(self):
self.flip_square(UP, run_time = 2)
self.wait()
def confused_by_lack_of_labels(self):
randy = Randolph(mode = "confused")
randy.next_to(self.square, LEFT, buff = LARGE_BUFF)
randy.to_edge(DOWN)
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.wait()
self.randy = randy
def add_labels(self):
self.add_randy_to_square(self.square)
self.play(
FadeIn(self.square.randy),
self.randy.change_mode, "happy",
self.randy.look_at, self.square.randy.eyes
)
self.play(Blink(self.randy))
self.play(FadeOut(self.randy))
self.wait()
def show_full_group(self):
new_title = TextMobject("Group", "of", "symmetries")
new_title.move_to(self.title)
all_squares = VGroup(*[
self.square.copy().scale(0.5)
for x in range(8)
])
all_squares.arrange_submobjects(RIGHT, buff = LARGE_BUFF)
top_squares = VGroup(*all_squares[:4])
bottom_squares = VGroup(*all_squares[4:])
bottom_squares.next_to(top_squares, DOWN, buff = LARGE_BUFF)
all_squares.set_width(FRAME_WIDTH-2*LARGE_BUFF)
all_squares.center()
all_squares.to_edge(DOWN, buff = LARGE_BUFF)
self.play(ReplacementTransform(self.square, all_squares[0]))
self.play(ReplacementTransform(self.title, new_title))
self.title = new_title
self.play(*[
ApplyMethod(mob.set_color, GREY)
for mob in self.title[1:]
])
for square, angle in zip(all_squares[1:4], [np.pi/2, np.pi, -np.pi/2]):
arcs = self.get_rotation_arcs(square, angle, MED_SMALL_BUFF)
self.play(*list(map(FadeIn, [square, arcs])))
square.rotation_kwargs = {"angle" : angle}
self.rotate_square(square = square, **square.rotation_kwargs)
square.add(arcs)
for square, axis in zip(bottom_squares, [RIGHT, RIGHT+UP, UP, UP+LEFT]):
axis_line = self.get_axis_line(square, axis)
self.play(FadeIn(square))
self.play(ShowCreation(axis_line))
square.rotation_kwargs = {"angle" : np.pi, "axis" : axis}
self.rotate_square(square = square, **square.rotation_kwargs)
square.add(axis_line)
self.wait()
self.all_squares = all_squares
def show_top_actions(self):
all_squares = self.all_squares
self.play(Indicate(all_squares[0]))
self.wait()
self.play(*[
Rotate(
square,
rate_func = lambda t : -there_and_back(t),
run_time = 3,
about_point = square.get_center(),
**square.rotation_kwargs
)
for square in all_squares[1:4]
])
self.wait()
def show_bottom_actions(self):
for square in self.all_squares[4:]:
self.rotate_square(
square = square,
rate_func = there_and_back,
run_time = 2,
**square.rotation_kwargs
)
self.wait()
def name_dihedral_group(self):
new_title = TextMobject(
"``Dihedral group'' of order 8"
)
new_title.to_edge(UP)
self.play(FadeOut(self.title))
self.play(FadeIn(new_title))
self.wait()
##########
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,
about_point = square.get_center(),
**kwargs
)
if hasattr(square, "labels"):
for label in rotation.target_mobject.labels:
label.rotate_in_place(-angle, axis)
if show_axis:
axis_line = self.get_axis_line(square, 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
)
def get_rotation_arcs(self, square, angle, angle_buff = SMALL_BUFF):
square_radius = get_norm(
square.points[0] - square.get_center()
)
arc = Arc(
radius = square_radius + SMALL_BUFF,
start_angle = np.pi/4 + np.sign(angle)*angle_buff,
angle = angle - np.sign(angle)*2*angle_buff,
color = YELLOW
)
arc.add_tip()
if abs(angle) < 3*np.pi/4:
angle_multiple_range = list(range(1, 4))
else:
angle_multiple_range = [2]
arcs = VGroup(arc, *[
arc.copy().rotate(i*np.pi/2)
for i in angle_multiple_range
])
arcs.move_to(square[0])
return arcs
def get_axis_line(self, square, axis):
axis_line = DashedLine(2*axis, -2*axis, **self.dashed_line_config)
axis_line.replace(square, dim_to_match = np.argmax(np.abs(axis)))
axis_line.scale_in_place(1.2)
return axis_line
def add_labels_and_dots(self, square):
labels = VGroup()
dots = VGroup()
for tex, vertex in zip("ABCD", square.get_anchors()):
label = TexMobject(tex)
label.add_background_rectangle()
label.next_to(vertex, vertex-square.get_center(), SMALL_BUFF)
labels.add(label)
dot = Dot(vertex, color = WHITE)
dots.add(dot)
square.add(labels, dots)
square.labels = labels
square.dots = dots
def add_randy_to_square(self, square, mode = "pondering"):
randy = Randolph(mode = mode)
randy.set_height(0.75*square.get_height())
randy.move_to(square)
square.add(randy)
square.randy = randy
class ManyGroupsAreInfinite(TeacherStudentsScene):
def construct(self):
self.teacher_says("Many groups are infinite")
self.change_student_modes(*["pondering"]*3)
self.wait(2)
class CircleSymmetries(Scene):
CONFIG = {
"circle_radius" : 2,
}
def construct(self):
self.add_circle_and_title()
self.show_range_of_angles()
self.associate_rotations_with_points()
def add_circle_and_title(self):
title = TextMobject("Group of rotations")
title.to_edge(UP)
circle = self.get_circle()
self.play(Write(title), ShowCreation(circle, run_time = 2))
self.wait()
angles = [
np.pi/2, -np.pi/3, 5*np.pi/6,
3*np.pi/2 + 0.1
]
angles.append(-sum(angles))
for angle in angles:
self.play(Rotate(circle, angle = angle))
self.wait()
self.circle = circle
def show_range_of_angles(self):
self.add_radial_line()
arc_circle = self.get_arc_circle()
theta = TexMobject("\\theta = ")
theta_value = DecimalNumber(0.00)
theta_value.next_to(theta, RIGHT)
theta_group = VGroup(theta, theta_value)
theta_group.next_to(arc_circle, UP)
def theta_value_update(theta_value, alpha):
new_theta_value = DecimalNumber(alpha*2*np.pi)
new_theta_value.set_height(theta.get_height())
new_theta_value.next_to(theta, RIGHT)
Transform(theta_value, new_theta_value).update(1)
return new_theta_value
self.play(FadeIn(theta_group))
for rate_func in smooth, lambda t : smooth(1-t):
self.play(
Rotate(self.circle, 2*np.pi-0.001),
ShowCreation(arc_circle),
UpdateFromAlphaFunc(theta_value, theta_value_update),
run_time = 7,
rate_func = rate_func
)
self.wait()
self.play(FadeOut(theta_group))
self.wait()
def associate_rotations_with_points(self):
zero_dot = Dot(self.circle.point_from_proportion(0))
zero_dot.set_color(RED)
zero_arrow = Arrow(UP+RIGHT, ORIGIN)
zero_arrow.set_color(zero_dot.get_color())
zero_arrow.next_to(zero_dot, UP+RIGHT, buff = SMALL_BUFF)
self.play(
ShowCreation(zero_arrow),
DrawBorderThenFill(zero_dot)
)
self.circle.add(zero_dot)
self.wait()
for alpha in 0.2, 0.6, 0.4, 0.8:
point = self.circle.point_from_proportion(alpha)
dot = Dot(point, color = YELLOW)
vect = np.sign(point)
arrow = Arrow(vect, ORIGIN)
arrow.next_to(dot, vect, buff = SMALL_BUFF)
arrow.set_color(dot.get_color())
angle = alpha*2*np.pi
self.play(
ShowCreation(arrow),
DrawBorderThenFill(dot)
)
self.play(
Rotate(self.circle, angle, run_time = 2),
Animation(dot)
)
self.wait()
self.play(
Rotate(self.circle, -angle, run_time = 2),
FadeOut(dot),
FadeOut(arrow),
)
self.wait()
####
def get_circle(self):
circle = Circle(color = MAROON_B, radius = self.circle_radius)
circle.ticks = VGroup()
for alpha in np.arange(0, 1, 1./8):
point = circle.point_from_proportion(alpha)
tick = Line((1 - 0.05)*point, (1 + 0.05)*point)
circle.ticks.add(tick)
circle.add(circle.ticks)
return circle
def add_radial_line(self):
radius = Line(
self.circle.get_center(),
self.circle.point_from_proportion(0)
)
static_radius = radius.copy().set_color(GREY)
self.play(ShowCreation(radius))
self.add(static_radius, radius)
self.circle.radius = radius
self.circle.static_radius = static_radius
self.circle.add(radius)
def get_arc_circle(self):
arc_radius = self.circle_radius/5.0
arc_circle = Circle(
radius = arc_radius,
color = WHITE
)
return arc_circle
class GroupOfCubeSymmetries(ThreeDScene):
CONFIG = {
"cube_opacity" : 0.5,
"cube_colors" : [BLUE],
"put_randy_on_cube" : True,
}
def construct(self):
title = TextMobject("Group of cube symmetries")
title.to_edge(UP)
self.add(title)
cube = self.get_cube()
face_centers = [face.get_center() for face in cube[0:7:2]]
angle_axis_pairs = list(zip(3*[np.pi/2], face_centers))
for i in range(3):
ones = np.ones(3)
ones[i] = -1
axis = np.dot(ones, face_centers)
angle_axis_pairs.append((2*np.pi/3, axis))
for angle, axis in angle_axis_pairs:
self.play(Rotate(
cube, angle = angle, axis = axis,
run_time = 2
))
self.wait()
def get_cube(self):
cube = Cube(fill_opacity = self.cube_opacity)
cube.set_color_by_gradient(*self.cube_colors)
if self.put_randy_on_cube:
randy = Randolph(mode = "pondering")
# randy.pupils.shift(0.01*OUT)
# randy.add(randy.pupils.copy().shift(0.02*IN))
# for submob in randy.get_family():
# submob.part_of_three_d_mobject = True
randy.scale(0.5)
face = cube[1]
randy.move_to(face)
face.add(randy)
pose_matrix = self.get_pose_matrix()
cube.apply_function(
lambda p : np.dot(p, pose_matrix.T),
maintain_smoothness = False
)
return cube
def get_pose_matrix(self):
return np.dot(
rotation_matrix(np.pi/8, UP),
rotation_matrix(np.pi/24, RIGHT)
)
class HowDoSymmetriesPlayWithEachOther(TeacherStudentsScene):
def construct(self):
self.teacher_says(
"How do symmetries \\\\ play with each other?",
target_mode = "hesitant",
)
self.change_student_modes("pondering", "maybe", "confused")
self.wait(2)
class AddSquareSymmetries(SymmetriesOfSquare):
def construct(self):
square = Square(**self.square_config)
square.flip(RIGHT)
square.shift(DOWN)
self.add_randy_to_square(square, mode = "shruggie")
alt_square = square.copy()
equals = TexMobject("=")
equals.move_to(square)
equation_square = Square(**self.square_config)
equation = VGroup(
equation_square, TexMobject("+"),
equation_square.copy(), TexMobject("="),
equation_square.copy(),
)
equation[0].add(self.get_rotation_arcs(
equation[0], np.pi/2,
))
equation[2].add(self.get_axis_line(equation[4], UP))
equation[4].add(self.get_axis_line(equation[4], UP+RIGHT))
for mob in equation[::2]:
mob.scale(0.5)
equation.arrange_submobjects(RIGHT)
equation.to_edge(UP)
arcs = self.get_rotation_arcs(square, np.pi/2)
self.add(square)
self.play(FadeIn(arcs))
self.rotate_square(
square = square, angle = np.pi/2,
added_anims = list(map(FadeIn, equation[:2]))
)
self.wait()
self.play(FadeOut(arcs))
self.flip_square(
square = square, axis = UP,
added_anims = list(map(FadeIn, equation[2:4]))
)
self.wait()
alt_square.next_to(equals, RIGHT, buff = LARGE_BUFF)
alt_square.save_state()
alt_square.move_to(square)
alt_square.set_fill(opacity = 0)
self.play(
square.next_to, equals, LEFT, LARGE_BUFF,
alt_square.restore,
Write(equals)
)
self.flip_square(
square = alt_square, axis = UP+RIGHT,
added_anims = list(map(FadeIn, equation[4:])),
)
self.wait(2)
## Reiterate composition
self.rotate_square(square = square, angle = np.pi/2)
self.flip_square(square = square, axis = UP)
self.wait()
self.flip_square(square = alt_square, axis = UP+RIGHT)
self.wait()
class AddCircleSymmetries(CircleSymmetries):
def construct(self):
circle = self.circle = self.get_circle()
arc_circle = self.get_arc_circle()
angles = [3*np.pi/2, 2*np.pi/3, np.pi/6]
arcs = [
arc_circle.copy().scale(scalar)
for scalar in [1, 1.2, 1.4]
]
equation = TexMobject(
"270^\\circ", "+", "120^\\circ", "=", "30^\\circ",
)
equation.to_edge(UP)
colors = [BLUE, YELLOW, GREEN]
for color, arc, term in zip(colors, arcs, equation[::2]):
arc.set_color(color)
term.set_color(color)
self.play(FadeIn(circle))
self.add_radial_line()
alt_radius = circle.radius.copy()
alt_radius.set_color(GREY)
alt_circle = circle.copy()
equals = TexMobject("=")
equals.move_to(circle)
def rotate(circle, angle, arc, terms):
self.play(
Rotate(circle, angle, in_place = True),
ShowCreation(
arc,
rate_func = lambda t : (angle/(2*np.pi))*smooth(t)
),
Write(VGroup(*terms)),
run_time = 2,
)
rotate(circle, angles[0], arcs[0], equation[:2])
self.wait()
circle.add(alt_radius)
rotate(circle, angles[1], arcs[1], equation[2:4])
self.play(FadeOut(alt_radius))
circle.remove(alt_radius)
self.wait()
circle.add(circle.static_radius)
circle.add(*arcs[:2])
alt_static_radius = circle.static_radius.copy()
alt_circle.add(alt_static_radius)
alt_circle.next_to(equals, RIGHT, buff = LARGE_BUFF)
alt_circle.save_state()
alt_circle.move_to(circle)
alt_circle.set_stroke(width = 0)
self.play(
circle.next_to, equals, LEFT, LARGE_BUFF,
alt_circle.restore,
Write(equals)
)
arcs[2].shift(alt_circle.get_center())
alt_circle.remove(alt_static_radius)
self.wait()
rotate(alt_circle, angles[2], arcs[2], equation[4:])
self.wait()
self.play(
Rotate(arcs[1], angles[0], about_point = circle.get_center())
)
self.wait(2)
for term, arc in zip(equation[::2], arcs):
self.play(*[
ApplyMethod(mob.scale_in_place, 1.2, rate_func = there_and_back)
for mob in (term, arc)
])
self.wait()
class AddCubeSymmetries(GroupOfCubeSymmetries):
CONFIG = {
"angle_axis_pairs" : [
(np.pi/2, RIGHT),
(np.pi/2, UP),
],
"cube_opacity" : 0.5,
"cube_colors" : [BLUE],
}
def construct(self):
angle_axis_pairs = list(self.angle_axis_pairs)
angle_axis_pairs.append(
self.get_composition_angle_and_axis()
)
self.pose_matrix = self.get_pose_matrix()
cube = self.get_cube()
equation = cube1, plus, cube2, equals, cube3 = VGroup(
cube, TexMobject("+"),
cube.copy(), TexMobject("="),
cube.copy()
)
equation.arrange_submobjects(RIGHT, buff = MED_LARGE_BUFF)
equation.center()
self.add(cube1)
self.rotate_cube(cube1, *angle_axis_pairs[0])
cube_copy = cube1.copy()
cube_copy.set_fill(opacity = 0)
self.play(
cube_copy.move_to, cube2,
cube_copy.set_fill, None, self.cube_opacity,
Write(plus)
)
self.rotate_cube(cube_copy, *angle_axis_pairs[1])
self.play(Write(equals))
self.play(DrawBorderThenFill(cube3, run_time = 1))
self.rotate_cube(cube3, *angle_axis_pairs[2])
self.wait(2)
times = TexMobject("\\times")
times.scale(1.5)
times.move_to(plus)
times.set_color(RED)
self.wait()
self.play(ReplacementTransform(plus, times))
self.play(Indicate(times))
self.wait()
for cube, (angle, axis) in zip([cube1, cube_copy, cube3], angle_axis_pairs):
self.rotate_cube(
cube, -angle, axis, add_arrows = False,
rate_func = there_and_back,
run_time = 1.5
)
self.wait()
def rotate_cube(self, cube, angle, axis, add_arrows = True, **kwargs):
axis = np.dot(axis, self.pose_matrix.T)
anims = []
if add_arrows:
arrows = VGroup(*[
Arc(
start_angle = np.pi/12+a, angle = 5*np.pi/6,
color = YELLOW
).add_tip()
for a in (0, np.pi)
])
arrows.set_height(1.5*cube.get_height())
z_to_axis = z_to_vector(axis)
arrows.apply_function(
lambda p : np.dot(p, z_to_axis.T),
maintain_smoothness = False
)
arrows.move_to(cube)
arrows.shift(-axis*cube.get_height()/2/get_norm(axis))
anims += list(map(ShowCreation, arrows))
anims.append(
Rotate(
cube, axis = axis, angle = angle, in_place = True,
**kwargs
)
)
self.play(*anims, run_time = 1.5)
def get_composition_angle_and_axis(self):
return get_composite_rotation_angle_and_axis(
*list(zip(*self.angle_axis_pairs))
)
class DihedralGroupStructure(SymmetriesOfSquare):
CONFIG = {
"dashed_line_config" : {
"dashed_segment_length" : 0.1
},
"filed_sum_scale_factor" : 0.4,
"num_rows" : 5,
}
def construct(self):
angle_axis_pairs = [
(np.pi/2, OUT),
(np.pi, OUT),
(-np.pi/2, OUT),
# (np.pi, RIGHT),
# (np.pi, UP+RIGHT),
(np.pi, UP),
(np.pi, UP+LEFT),
]
pair_pairs = list(it.product(*[angle_axis_pairs]*2))
random.shuffle(pair_pairs)
for pair_pair in pair_pairs[:4]:
sum_expression = self.demonstrate_sum(pair_pair)
self.file_away_sum(sum_expression)
for pair_pair in pair_pairs[4:]:
should_skip_animations = self.skip_animations
self.skip_animations = True
sum_expression = self.demonstrate_sum(pair_pair)
self.file_away_sum(sum_expression)
self.skip_animations = should_skip_animations
self.play(FadeIn(sum_expression))
self.wait(3)
def demonstrate_sum(self, angle_axis_pairs):
angle_axis_pairs = list(angle_axis_pairs) + [
get_composite_rotation_angle_and_axis(
*list(zip(*angle_axis_pairs))
)
]
prototype_square = Square(**self.square_config)
prototype_square.flip(RIGHT)
self.add_randy_to_square(prototype_square)
# self.add_labels_and_dots(prototype_square)
prototype_square.scale(0.7)
expression = s1, plus, s2, equals, s3 = VGroup(
prototype_square, TexMobject("+").scale(2),
prototype_square.copy(), TexMobject("=").scale(2),
prototype_square.copy()
)
final_expression = VGroup()
for square, (angle, axis) in zip([s1, s2, s3], angle_axis_pairs):
if np.cos(angle) > 0.5:
square.action_illustration = VectorizedPoint()
elif np.argmax(np.abs(axis)) == 2: ##Axis is in z direction
square.action_illustration = self.get_rotation_arcs(
square, angle
)
else:
square.action_illustration = self.get_axis_line(
square, axis
)
square.add(square.action_illustration)
final_expression.add(square.action_illustration)
square.rotation_kwargs = {
"square" : square,
"angle" : angle,
"axis" : axis,
}
expression.arrange_submobjects()
expression.set_width(FRAME_X_RADIUS+1)
expression.to_edge(RIGHT, buff = SMALL_BUFF)
for square in s1, s2, s3:
square.remove(square.action_illustration)
self.play(FadeIn(s1))
self.play(*list(map(ShowCreation, s1.action_illustration)))
self.rotate_square(**s1.rotation_kwargs)
s1_copy = s1.copy()
self.play(
# FadeIn(s2),
s1_copy.move_to, s2,
Write(plus)
)
Transform(s2, s1_copy).update(1)
self.remove(s1_copy)
self.add(s2)
self.play(*list(map(ShowCreation, s2.action_illustration)))
self.rotate_square(**s2.rotation_kwargs)
self.play(
Write(equals),
FadeIn(s3)
)
self.play(*list(map(ShowCreation, s3.action_illustration)))
self.rotate_square(**s3.rotation_kwargs)
self.wait()
final_expression.add(*expression)
return final_expression
def file_away_sum(self, sum_expression):
if not hasattr(self, "num_sum_expressions"):
self.num_sum_expressions = 0
target = sum_expression.copy()
target.scale(self.filed_sum_scale_factor)
y_index = self.num_sum_expressions%self.num_rows
y_prop = float(y_index)/(self.num_rows-1)
y = interpolate(FRAME_Y_RADIUS-LARGE_BUFF, -FRAME_Y_RADIUS+LARGE_BUFF, y_prop)
x_index = self.num_sum_expressions//self.num_rows
x_spacing = FRAME_WIDTH/3
x = (x_index-1)*x_spacing
target.move_to(x*RIGHT + y*UP)
self.play(Transform(sum_expression, target))
self.wait()
self.num_sum_expressions += 1
self.last_sum_expression = sum_expression
class ThisIsAVeryGeneralIdea(Scene):
def construct(self):
groups = TextMobject("Groups")
groups.to_edge(UP)
groups.set_color(BLUE)
examples = VGroup(*list(map(TextMobject, [
"Square matrices \\\\ \\small (Where $\\det(M) \\ne 0$)",
"Molecular \\\\ symmetry",
"Cryptography",
"Numbers",
])))
numbers = examples[-1]
examples.arrange_submobjects(buff = LARGE_BUFF)
examples.set_width(FRAME_WIDTH-1)
examples.move_to(UP)
lines = VGroup(*[
Line(groups.get_bottom(), ex.get_top(), buff = MED_SMALL_BUFF)
for ex in examples
])
lines.set_color(groups.get_color())
self.add(groups)
for example, line in zip(examples, lines):
self.play(
ShowCreation(line),
Write(example, run_time = 2)
)
self.wait()
self.play(
VGroup(*examples[:-1]).fade, 0.7,
VGroup(*lines[:-1]).fade, 0.7,
)
self.play(
numbers.scale, 1.2, numbers.get_corner(UP+RIGHT),
)
self.wait(2)
sub_categories = VGroup(*list(map(TextMobject, [
"Numbers \\\\ (Additive)",
"Numbers \\\\ (Multiplicative)",
])))
sub_categories.arrange_submobjects(RIGHT, buff = MED_LARGE_BUFF)
sub_categories.next_to(numbers, DOWN, 1.5*LARGE_BUFF)
sub_categories.to_edge(RIGHT)
sub_categories[0].set_color(ADDER_COLOR)
sub_categories[1].set_color(MULTIPLIER_COLOR)
sub_lines = VGroup(*[
Line(numbers.get_bottom(), sc.get_top(), buff = MED_SMALL_BUFF)
for sc in sub_categories
])
sub_lines.set_color(numbers.get_color())
self.play(*it.chain(
list(map(ShowCreation, sub_lines)),
list(map(Write, sub_categories))
))
self.wait()
class NumbersAsActionsQ(TeacherStudentsScene):
def construct(self):
self.student_says(
"Numbers are actions?",
target_mode = "confused",
)
self.change_student_modes("pondering", "confused", "erm")
self.play(self.get_teacher().change_mode, "happy")
self.wait(3)
class AdditiveGroupOfReals(Scene):
CONFIG = {
"number_line_center" : UP,
"shadow_line_center" : DOWN,
"zero_color" : GREEN_B,
"x_min" : -FRAME_WIDTH,
"x_max" : FRAME_WIDTH,
}
def construct(self):
self.add_number_line()
self.show_example_slides(3, -7)
self.write_group_of_slides()
self.show_example_slides(2, 6, -1, -3)
self.mark_zero()
self.show_example_slides_labeled(3, -2)
self.comment_on_zero_as_identity()
self.show_example_slides_labeled(
5.5, added_anims = [self.get_write_name_of_group_anim()]
)
self.show_example_additions((3, 2), (2, -5), (-4, 4))
def add_number_line(self):
number_line = NumberLine(
x_min = self.x_min,
x_max = self.x_max,
)
number_line.shift(self.number_line_center)
shadow_line = NumberLine(color = GREY, stroke_width = 2)
shadow_line.shift(self.shadow_line_center)
for line in number_line, shadow_line:
line.add_numbers()
shadow_line.numbers.fade(0.25)
shadow_line.save_state()
shadow_line.set_color(BLACK)
shadow_line.move_to(number_line)
self.play(*list(map(Write, number_line)), run_time = 1)
self.play(shadow_line.restore, Animation(number_line))
self.wait()
self.number_line = number_line
self.shadow_line = shadow_line
def show_example_slides(self, *nums):
for num in nums:
zero_point = self.number_line.number_to_point(0)
num_point = self.number_line.number_to_point(num)
arrow = Arrow(zero_point, num_point, buff = 0)
arrow.set_color(ADDER_COLOR)
arrow.shift(MED_LARGE_BUFF*UP)
self.play(ShowCreation(arrow))
self.play(
self.number_line.shift,
num_point - zero_point,
run_time = 2
)
self.play(FadeOut(arrow))
def write_group_of_slides(self):
title = TextMobject("Group of line symmetries")
title.to_edge(UP)
self.play(Write(title))
self.title = title
def mark_zero(self):
dot = Dot(
self.number_line.number_to_point(0),
color = self.zero_color
)
arrow = Arrow(dot, color = self.zero_color)
words = TextMobject("Follow zero")
words.next_to(arrow.get_start(), UP)
words.set_color(self.zero_color)
self.play(
ShowCreation(arrow),
DrawBorderThenFill(dot),
Write(words),
)
self.wait()
self.play(*list(map(FadeOut, [arrow, words])))
self.number_line.add(dot)
def show_example_slides_labeled(self, *nums, **kwargs):
for num in nums:
line = DashedLine(
self.number_line.number_to_point(num)+MED_LARGE_BUFF*UP,
self.shadow_line.number_to_point(num)+MED_LARGE_BUFF*DOWN,
)
vect = self.number_line.number_to_point(num) - \
self.number_line.number_to_point(0)
self.play(ShowCreation(line))
self.wait()
self.play(self.number_line.shift, vect, run_time = 2)
self.wait()
if "added_anims" in kwargs:
self.play(*kwargs["added_anims"])
self.wait()
self.play(
self.number_line.shift, -vect,
FadeOut(line)
)
def comment_on_zero_as_identity(self):
line = DashedLine(
self.number_line.number_to_point(0)+MED_LARGE_BUFF*UP,
self.shadow_line.number_to_point(0)+MED_LARGE_BUFF*DOWN,
)
words = TexMobject("0 \\leftrightarrow \\text{Do nothing}")
words.shift(line.get_top()+MED_SMALL_BUFF*UP - words[0].get_bottom())
self.play(
ShowCreation(line),
Write(words)
)
self.wait(2)
self.play(*list(map(FadeOut, [line, words])))
def get_write_name_of_group_anim(self):
new_title = TextMobject("Additive group of real numbers")
VGroup(*new_title[-len("realnumbers"):]).set_color(BLUE)
VGroup(*new_title[:len("Additive")]).set_color(ADDER_COLOR)
new_title.to_edge(UP)
return Transform(self.title, new_title)
def show_example_additions(self, *num_pairs):
for num_pair in num_pairs:
num_mobs = VGroup()
arrows = VGroup()
self.number_line.save_state()
for num in num_pair:
zero_point, num_point, arrow, num_mob = \
self.get_adder_mobs(num)
if len(num_mobs) > 0:
last_num_mob = num_mobs[0]
x = num_mob.get_center()[0]
if x < last_num_mob.get_right()[0] and x > last_num_mob.get_left()[0]:
num_mob.next_to(last_num_mob, RIGHT)
num_mobs.add(num_mob)
arrows.add(arrow)
self.play(
ShowCreation(arrow),
Write(num_mob, run_time = 1)
)
self.play(
self.number_line.shift,
num_point - zero_point
)
self.wait()
#Reset
self.play(
FadeOut(num_mobs),
FadeOut(self.number_line)
)
ApplyMethod(self.number_line.restore).update(1)
self.play(FadeIn(self.number_line))
#Sum arrow
num = sum(num_pair)
zero_point, sum_point, arrow, sum_mob = \
self.get_adder_mobs(sum(num_pair))
VGroup(arrow, sum_mob).shift(MED_LARGE_BUFF*UP)
arrows.add(arrow)
self.play(
ShowCreation(arrow),
Write(sum_mob, run_time = 1)
)
self.wait()
self.play(
self.number_line.shift,
num_point - zero_point,
run_time = 2
)
self.wait()
self.play(
self.number_line.restore,
*list(map(FadeOut, [arrows, sum_mob]))
)
def get_adder_mobs(self, num):
zero_point = self.number_line.number_to_point(0)
num_point = self.number_line.number_to_point(num)
arrow = Arrow(zero_point, num_point, buff = 0)
arrow.set_color(ADDER_COLOR)
arrow.shift(MED_SMALL_BUFF*UP)
if num == 0:
arrow = DashedLine(UP, ORIGIN)
arrow.move_to(zero_point)
elif num < 0:
arrow.set_color(RED)
arrow.shift(SMALL_BUFF*UP)
sign = "+" if num >= 0 else ""
num_mob = TexMobject(sign + str(num))
num_mob.next_to(arrow, UP)
num_mob.set_color(arrow.get_color())
return zero_point, num_point, arrow, num_mob
class AdditiveGroupOfComplexNumbers(ComplexTransformationScene):
CONFIG = {
"x_min" : -2*int(FRAME_X_RADIUS),
"x_max" : 2*int(FRAME_X_RADIUS),
"y_min" : -FRAME_HEIGHT,
"y_max" : FRAME_HEIGHT,
"example_points" : [
complex(3, 2),
complex(1, -3),
]
}
def construct(self):
self.add_plane()
self.show_preview_example_slides()
self.show_vertical_slide()
self.show_example_point()
self.show_example_addition()
self.write_group_name()
self.show_some_random_slides()
def add_plane(self):
self.add_transformable_plane(animate = True)
zero_dot = Dot(
self.z_to_point(0),
color = ADDER_COLOR
)
self.play(ShowCreation(zero_dot))
self.plane.add(zero_dot)
self.plane.zero_dot = zero_dot
self.wait()
def show_preview_example_slides(self):
example_vect = 2*UP+RIGHT
for vect in example_vect, -example_vect:
self.play(self.plane.shift, vect, run_time = 2)
self.wait()
def show_vertical_slide(self):
dots = VGroup(*[
Dot(self.z_to_point(complex(0, i)))
for i in range(1, 4)
])
dots.set_color(YELLOW)
labels = VGroup(*self.imag_labels[-3:])
arrow = Arrow(ORIGIN, dots[-1].get_center(), buff = 0)
arrow.set_color(ADDER_COLOR)
self.plane.save_state()
for dot, label in zip(dots, labels):
self.play(
Indicate(label),
ShowCreation(dot)
)
self.add_foreground_mobjects(dots)
self.wait()
Scene.play(self, ShowCreation(arrow))
self.add_foreground_mobjects(arrow)
self.play(
self.plane.shift, dots[-1].get_center(),
run_time = 2
)
self.wait()
self.play(FadeOut(arrow))
self.foreground_mobjects.remove(arrow)
self.play(
self.plane.shift, 6*DOWN,
run_time = 3,
)
self.wait()
self.play(self.plane.restore, run_time = 2)
self.foreground_mobjects.remove(dots)
self.play(FadeOut(dots))
def show_example_point(self):
z = self.example_points[0]
point = self.z_to_point(z)
dot = Dot(point, color = YELLOW)
arrow = Vector(point, buff = dot.radius)
arrow.set_color(dot.get_color())
label = TexMobject("%d + %di"%(z.real, z.imag))
label.next_to(point, UP)
label.set_color(dot.get_color())
label.add_background_rectangle()
real_arrow = Vector(self.z_to_point(z.real))
imag_arrow = Vector(self.z_to_point(z - z.real))
VGroup(real_arrow, imag_arrow).set_color(ADDER_COLOR)
self.play(
Write(label),
DrawBorderThenFill(dot)
)
self.wait()
self.play(ShowCreation(arrow))
self.add_foreground_mobjects(label, dot, arrow)
self.wait()
self.slide(z)
self.wait()
self.play(FadeOut(self.plane))
self.plane.restore()
self.plane.set_stroke(width = 0)
self.play(self.plane.restore)
self.play(ShowCreation(real_arrow))
self.add_foreground_mobjects(real_arrow)
self.slide(z.real)
self.wait()
self.play(ShowCreation(imag_arrow))
self.wait()
self.play(imag_arrow.shift, self.z_to_point(z.real))
self.add_foreground_mobjects(imag_arrow)
self.slide(z - z.real)
self.wait()
self.foreground_mobjects.remove(real_arrow)
self.foreground_mobjects.remove(imag_arrow)
self.play(*list(map(FadeOut, [real_arrow, imag_arrow, self.plane])))
self.plane.restore()
self.plane.set_stroke(width = 0)
self.play(self.plane.restore)
self.z1 = z
self.arrow1 = arrow
self.dot1 = dot
self.label1 = label
def show_example_addition(self):
z1 = self.z1
arrow1 = self.arrow1
dot1 = self.dot1
label1 = self.label1
z2 = self.example_points[1]
point2 = self.z_to_point(z2)
dot2 = Dot(point2, color = TEAL)
arrow2 = Vector(
point2,
buff = dot2.radius,
color = dot2.get_color()
)
label2 = TexMobject(
"%d %di"%(z2.real, z2.imag)
)
label2.next_to(point2, UP+RIGHT)
label2.set_color(dot2.get_color())
label2.add_background_rectangle()
self.play(ShowCreation(arrow2))
self.play(
DrawBorderThenFill(dot2),
Write(label2)
)
self.add_foreground_mobjects(arrow2, dot2, label2)
self.wait()
self.slide(z1)
arrow2_copy = arrow2.copy()
self.play(arrow2_copy.shift, self.z_to_point(z1))
self.add_foreground_mobjects(arrow2_copy)
self.slide(z2)
self.play(FadeOut(arrow2_copy))
self.foreground_mobjects.remove(arrow2_copy)
self.wait()
##Break into components
real_arrow, imag_arrow = component_arrows = [
Vector(
self.z_to_point(z),
color = ADDER_COLOR
)
for z in [
z1.real+z2.real,
complex(0, z1.imag+z2.imag),
]
]
imag_arrow.shift(real_arrow.get_end())
plus = TexMobject("+").next_to(
real_arrow.get_center(), UP+RIGHT
)
plus.add_background_rectangle()
rp1, rp2, ip1, ip2 = label_parts = [
VGroup(label1[1][0].copy()),
VGroup(label2[1][0].copy()),
VGroup(*label1[1][2:]).copy(),
VGroup(*label2[1][1:]).copy(),
]
for part in label_parts:
part.generate_target()
rp1.target.next_to(plus, LEFT)
rp2.target.next_to(plus, RIGHT)
ip1.target.next_to(imag_arrow.get_center(), RIGHT)
ip1.target.shift(SMALL_BUFF*DOWN)
ip2.target.next_to(ip1.target, RIGHT)
real_background_rect = BackgroundRectangle(
VGroup(rp1.target, rp2.target)
)
imag_background_rect = BackgroundRectangle(
VGroup(ip1.target, ip2.target)
)
self.play(
ShowCreation(real_arrow),
ShowCreation(
real_background_rect,
rate_func = squish_rate_func(smooth, 0.75, 1),
),
Write(plus),
*list(map(MoveToTarget, [rp1, rp2]))
)
self.wait()
self.play(
ShowCreation(imag_arrow),
ShowCreation(
imag_background_rect,
rate_func = squish_rate_func(smooth, 0.75, 1),
),
*list(map(MoveToTarget, [ip1, ip2]))
)
self.wait(2)
to_remove = [
arrow1, dot1, label1,
arrow2, dot2, label2,
real_background_rect,
imag_background_rect,
plus,
] + label_parts + component_arrows
for mob in to_remove:
if mob in self.foreground_mobjects:
self.foreground_mobjects.remove(mob)
self.play(*list(map(FadeOut, to_remove)))
self.play(self.plane.restore, run_time = 2)
self.wait()
def write_group_name(self):
title = TextMobject(
"Additive", "group of", "complex numbers"
)
title[0].set_color(ADDER_COLOR)
title[2].set_color(BLUE)
title.add_background_rectangle()
title.to_edge(UP, buff = MED_SMALL_BUFF)
self.play(Write(title))
self.add_foreground_mobjects(title)
self.wait()
def show_some_random_slides(self):
example_slides = [
complex(3),
complex(0, 2),
complex(-4, -1),
complex(-2, -1),
complex(4, 2),
]
for z in example_slides:
self.slide(z)
self.wait()
#########
def slide(self, z, *added_anims, **kwargs):
kwargs["run_time"] = kwargs.get("run_time", 2)
self.play(
ApplyMethod(
self.plane.shift, self.z_to_point(z),
**kwargs
),
*added_anims
)
class SchizophrenicNumbers(Scene):
def construct(self):
v_line = DashedLine(
FRAME_Y_RADIUS*UP,
FRAME_Y_RADIUS*DOWN
)
left_title = TextMobject("Additive group")
left_title.shift(FRAME_X_RADIUS*LEFT/2)
right_title = TextMobject("Multiplicative group")
right_title.shift(FRAME_X_RADIUS*RIGHT/2)
VGroup(left_title, right_title).to_edge(UP)
self.add(v_line, left_title, right_title)
numbers = VGroup(
Randolph(mode = "happy").scale(0.2),
TexMobject("3").shift(UP+LEFT),
TexMobject("5.83").shift(UP+RIGHT),
TexMobject("\\sqrt{2}").shift(DOWN+LEFT),
TexMobject("2-i").shift(DOWN+RIGHT),
)
for number in numbers:
number.set_color(ADDER_COLOR)
number.scale(1.5)
if isinstance(number, PiCreature):
continue
number.eyes = Eyes(number[0], height = 0.1)
number.add(number.eyes)
numbers[3].eyes.next_to(numbers[3][1], UP, buff = 0)
numbers.shift(FRAME_X_RADIUS*LEFT/2)
self.play(FadeIn(numbers))
self.blink_numbers(numbers)
self.wait()
self.add(numbers.copy())
for number in numbers:
number.generate_target()
number.target.shift(FRAME_X_RADIUS*RIGHT)
number.target.eyes.save_state()
number.target.set_color(MULTIPLIER_COLOR)
number.target.eyes.restore()
self.play(*[
MoveToTarget(
number,
rate_func = squish_rate_func(
smooth, alpha, alpha+0.5
),
run_time = 2,
)
for number, alpha in zip(numbers, np.linspace(0, 0.5, len(numbers)))
])
self.wait()
self.blink_numbers(numbers)
self.wait()
def blink_numbers(self, numbers):
self.play(*[
num.eyes.blink_anim(
rate_func = squish_rate_func(
there_and_back, alpha, alpha+0.2
)
)
for num, alpha in zip(
numbers[1:], 0.8*np.random.random(len(numbers))
)
])
class MultiplicativeGroupOfReals(AdditiveGroupOfReals):
CONFIG = {
"number_line_center" : 0.5*UP,
"shadow_line_center" : 1.5*DOWN,
"x_min" : -3*FRAME_X_RADIUS,
"x_max" : 3*FRAME_X_RADIUS,
"positive_reals_color" : MAROON_B,
}
def setup(self):
self.foreground_mobjects = VGroup()
def construct(self):
self.add_title()
self.add_number_line()
self.introduce_stretch_and_squish()
self.show_zero_fixed_in_place()
self.follow_one()
self.every_positive_number_association()
self.compose_actions(3, 2)
self.compose_actions(4, 0.5)
self.write_group_name()
self.compose_actions(1.5, 1.5)
def add_title(self):
self.title = TextMobject("Group of stretching/squishing actions")
self.title.to_edge(UP)
self.add(self.title)
def add_number_line(self):
AdditiveGroupOfReals.add_number_line(self)
self.zero_point = self.number_line.number_to_point(0)
self.one = [m for m in self.number_line.numbers if m.get_tex_string() is "1"][0]
self.one.add_background_rectangle()
self.one.background_rectangle.scale_in_place(1.3)
self.number_line.save_state()
def introduce_stretch_and_squish(self):
for num in [3, 0.25]:
self.stretch(num)
self.wait()
self.play(self.number_line.restore)
self.wait()
def show_zero_fixed_in_place(self):
arrow = Arrow(self.zero_point + UP, self.zero_point, buff = 0)
arrow.set_color(ADDER_COLOR)
words = TextMobject("Fix zero")
words.set_color(ADDER_COLOR)
words.next_to(arrow, UP)
self.play(
ShowCreation(arrow),
Write(words)
)
self.foreground_mobjects.add(arrow)
self.stretch(4)
self.stretch(0.1)
self.wait()
self.play(self.number_line.restore)
self.play(FadeOut(words))
self.wait()
self.zero_arrow = arrow
def follow_one(self):
dot = Dot(self.number_line.number_to_point(1))
arrow = Arrow(dot.get_center()+UP+RIGHT, dot)
words = TextMobject("Follow one")
words.next_to(arrow.get_start(), UP)
for mob in dot, arrow, words:
mob.set_color(MULTIPLIER_COLOR)
three_line, half_line = [
DashedLine(
self.number_line.number_to_point(num),
self.shadow_line.number_to_point(num)
)
for num in (3, 0.5)
]
three_mob = [m for m in self.shadow_line.numbers if m.get_tex_string() == "3"][0]
half_point = self.shadow_line.number_to_point(0.5)
half_arrow = Arrow(
half_point+UP+LEFT, half_point, buff = SMALL_BUFF,
tip_length = 0.15,
)
half_label = TexMobject("1/2")
half_label.scale(0.7)
half_label.set_color(MULTIPLIER_COLOR)
half_label.next_to(half_arrow.get_start(), LEFT, buff = SMALL_BUFF)
self.play(
ShowCreation(arrow),
DrawBorderThenFill(dot),
Write(words)
)
self.number_line.add(dot)
self.number_line.numbers.add(dot)
self.number_line.save_state()
self.wait()
self.play(*list(map(FadeOut, [arrow, words])))
self.stretch(3)
self.play(
ShowCreation(three_line),
Animation(self.one)
)
dot_copy = dot.copy()
self.play(
dot_copy.move_to, three_line.get_bottom()
)
self.play(Indicate(three_mob, run_time = 2))
self.wait()
self.play(
self.number_line.restore,
*list(map(FadeOut, [three_line, dot_copy]))
)
self.wait()
self.stretch(0.5)
self.play(
ShowCreation(half_line),
Animation(self.one)
)
dot_copy = dot.copy()
self.play(
dot_copy.move_to, half_line.get_bottom()
)
self.play(
Write(half_label),
ShowCreation(half_arrow)
)
self.wait()
self.play(
self.number_line.restore,
*list(map(FadeOut, [
half_label, half_arrow,
half_line, dot_copy
]))
)
self.wait()
self.one_dot = dot
def every_positive_number_association(self):
positive_reals_line = Line(
self.shadow_line.number_to_point(0),
self.shadow_line.number_to_point(FRAME_X_RADIUS),
color = self.positive_reals_color
)
positive_reals_words = TextMobject("All positive reals")
positive_reals_words.set_color(self.positive_reals_color)
positive_reals_words.next_to(positive_reals_line, UP)
positive_reals_words.add_background_rectangle()
third_line, one_line = [
DashedLine(
self.number_line.number_to_point(num),
self.shadow_line.number_to_point(num)
)
for num in (0.33, 1)
]
self.play(
self.zero_arrow.shift, 0.5*UP,
rate_func = there_and_back
)
self.wait()
self.play(
self.one_dot.shift, 0.25*UP,
rate_func = wiggle
)
self.stretch(3)
self.stretch(0.33/3, run_time = 3)
self.wait()
self.play(ShowCreation(third_line), Animation(self.one))
self.play(
ShowCreation(positive_reals_line),
Write(positive_reals_words),
)
self.wait()
self.play(
ReplacementTransform(third_line, one_line),
self.number_line.restore,
Animation(positive_reals_words),
run_time = 2
)
self.number_line.add_to_back(one_line)
self.number_line.save_state()
self.stretch(
7, run_time = 10, rate_func = there_and_back,
added_anims = [Animation(positive_reals_words)]
)
self.wait()
def compose_actions(self, num1, num2):
words = VGroup(*[
TextMobject("(%s by %s)"%(word, str(num)))
for num in (num1, num2, num1*num2)
for word in ["Stretch" if num > 1 else "Squish"]
])
words.submobjects.insert(2, TexMobject("="))
words.arrange_submobjects(RIGHT)
top_words = VGroup(*words[:2])
top_words.set_color(MULTIPLIER_COLOR)
bottom_words = VGroup(*words[2:])
bottom_words.next_to(top_words, DOWN)
words.scale(0.8)
words.next_to(self.number_line, UP)
words.to_edge(RIGHT)
for num, word in zip([num1, num2], top_words):
self.stretch(
num,
added_anims = [FadeIn(word)],
run_time = 3
)
self.wait()
self.play(Write(bottom_words, run_time = 2))
self.wait(2)
self.play(
ApplyMethod(self.number_line.restore, run_time = 2),
FadeOut(words),
)
self.wait()
def write_group_name(self):
new_title = TextMobject(
"Multiplicative group of positive real numbers"
)
new_title.to_edge(UP)
VGroup(
*new_title[:len("Multiplicative")]
).set_color(MULTIPLIER_COLOR)
VGroup(
*new_title[-len("positiverealnumbers"):]
).set_color(self.positive_reals_color)
self.play(Transform(self.title, new_title))
self.wait()
###
def stretch(self, factor, run_time = 2, **kwargs):
kwargs["run_time"] = run_time
target = self.number_line.copy()
target.stretch_about_point(factor, 0, self.zero_point)
total_factor = (target.number_to_point(1)-self.zero_point)[0]
for number in target.numbers:
number.stretch_in_place(1./factor, dim = 0)
if total_factor < 0.7:
number.stretch_in_place(total_factor, dim = 0)
self.play(
Transform(self.number_line, target, **kwargs),
*kwargs.get("added_anims", [])
)
def play(self, *anims, **kwargs):
anims = list(anims) + [Animation(self.foreground_mobjects)]
Scene.play(self, *anims, **kwargs)
class MultiplicativeGroupOfComplexNumbers(AdditiveGroupOfComplexNumbers):
CONFIG = {
"dot_radius" : Dot.CONFIG["radius"],
"y_min" : -3*FRAME_Y_RADIUS,
"y_max" : 3*FRAME_Y_RADIUS,
}
def construct(self):
self.add_plane()
self.add_title()
self.fix_zero_and_move_one()
self.show_example_actions()
self.show_action_at_i()
self.show_action_at_i_again()
self.show_i_squared_is_negative_one()
self.talk_through_specific_example()
self.show_break_down()
self.example_actions_broken_down()
def add_plane(self):
AdditiveGroupOfComplexNumbers.add_plane(self)
one_dot = Dot(
self.z_to_point(1),
color = MULTIPLIER_COLOR,
radius = self.dot_radius,
)
self.plane.add(one_dot)
self.plane.one_dot = one_dot
self.plane.save_state()
self.add(self.plane)
def add_title(self):
title = TextMobject(
"Multiplicative", "group of",
"complex numbers"
)
title.to_edge(UP)
title[0].set_color(MULTIPLIER_COLOR)
title[2].set_color(BLUE)
title.add_background_rectangle()
self.play(Write(title, run_time = 2))
self.wait()
self.add_foreground_mobjects(title)
def fix_zero_and_move_one(self):
zero_arrow = Arrow(
UP+1.25*LEFT, ORIGIN,
buff = 2*self.dot_radius
)
zero_arrow.set_color(ADDER_COLOR)
zero_words = TextMobject("Fix zero")
zero_words.set_color(ADDER_COLOR)
zero_words.add_background_rectangle()
zero_words.next_to(zero_arrow.get_start(), UP)
one_point = self.z_to_point(1)
one_arrow = Arrow(
one_point+UP+1.25*RIGHT, one_point,
buff = 2*self.dot_radius,
color = MULTIPLIER_COLOR,
)
one_words = TextMobject("Drag one")
one_words.set_color(MULTIPLIER_COLOR)
one_words.add_background_rectangle()
one_words.next_to(one_arrow.get_start(), UP)
self.play(
Write(zero_words, run_time = 2),
ShowCreation(zero_arrow),
Indicate(self.plane.zero_dot, color = RED),
)
self.play(
Write(one_words, run_time = 2),
ShowCreation(one_arrow),
Indicate(self.plane.one_dot, color = RED),
)
self.wait(2)
self.play(*list(map(FadeOut, [
zero_words, zero_arrow,
one_words, one_arrow,
])))
def show_example_actions(self):
z_list = [
complex(2),
complex(0.5),
complex(2, 1),
complex(-2, 2),
]
for last_z, z in zip([1] + z_list, z_list):
self.multiply_by_z(z/last_z)
self.wait()
self.reset_plane()
self.wait()
def show_action_at_i(self):
i_point = self.z_to_point(complex(0, 1))
i_dot = Dot(i_point)
i_dot.set_color(RED)
i_arrow = Arrow(i_point+UP+LEFT, i_point)
i_arrow.set_color(i_dot.get_color())
arc = Arc(
start_angle = np.pi/24,
angle = 10*np.pi/24,
radius = self.z_to_point(1)[0],
num_anchors = 20,
)
arc.add_tip(tip_length = 0.15)
arc.set_color(YELLOW)
self.play(
ShowCreation(i_arrow),
DrawBorderThenFill(i_dot)
)
self.wait()
self.play(
FadeOut(i_arrow),
ShowCreation(arc)
)
self.add_foreground_mobjects(arc)
self.wait(2)
self.multiply_by_z(complex(0, 1), run_time = 3)
self.remove(i_dot)
self.wait()
self.turn_arrow = arc
def show_action_at_i_again(self):
neg_one_label = [m for m in self.real_labels if m.get_tex_string() == "-1"][0]
half_turn_arc = Arc(
start_angle = np.pi/12,
angle = 10*np.pi/12,
color = self.turn_arrow.get_color()
)
half_turn_arc.add_tip(tip_length = 0.15)
self.multiply_by_z(complex(0, 1), run_time = 3)
self.wait()
self.play(Transform(
self.turn_arrow, half_turn_arc,
path_arc = np.pi/2
))
self.wait()
self.play(Indicate(neg_one_label, run_time = 2))
self.wait()
self.foreground_mobjects.remove(self.turn_arrow)
self.reset_plane(FadeOut(self.turn_arrow))
def show_i_squared_is_negative_one(self):
equation = TexMobject("i", "\\cdot", "i", "=", "-1")
terms = equation[::2]
equation.add_background_rectangle()
equation.next_to(ORIGIN, RIGHT)
equation.shift(1.5*UP)
equation.set_color(MULTIPLIER_COLOR)
self.play(Write(equation, run_time = 2))
self.wait()
for term in terms[:2]:
self.multiply_by_z(
complex(0, 1),
added_anims = [
Animation(equation),
Indicate(term, color = RED, run_time = 2)
]
)
self.wait()
self.play(Indicate(terms[-1], color = RED, run_time = 2))
self.wait()
self.reset_plane(FadeOut(equation))
def talk_through_specific_example(self):
z = complex(2, 1)
angle = np.angle(z)
point = self.z_to_point(z)
dot = Dot(point, color = WHITE)
label = TexMobject("%d + %di"%(z.real, z.imag))
label.add_background_rectangle()
label.next_to(dot, UP+RIGHT, buff = 0)
brace = Brace(
Line(ORIGIN, self.z_to_point(np.sqrt(5))),
UP
)
brace_text = brace.get_text("$\\sqrt{5}$")
brace_text.add_background_rectangle()
brace_text.scale(0.7, about_point = brace.get_top())
brace.rotate(angle)
brace_text.rotate(angle).rotate_in_place(-angle)
VGroup(brace, brace_text).set_color(MAROON_B)
arc = Arc(angle, color = WHITE, radius = 0.5)
angle_label = TexMobject("30^\\circ")
angle_label.scale(0.7)
angle_label.next_to(
arc, RIGHT,
buff = SMALL_BUFF, aligned_edge = DOWN
)
angle_label.set_color(MULTIPLIER_COLOR)
self.play(
Write(label),
DrawBorderThenFill(dot)
)
self.add_foreground_mobjects(label, dot)
self.wait()
self.multiply_by_z(z, run_time = 3)
self.wait()
self.reset_plane()
self.multiply_by_z(
np.exp(complex(0, 1)*angle),
added_anims = [
ShowCreation(arc, run_time = 2),
Write(angle_label)
]
)
self.add_foreground_mobjects(arc, angle_label)
self.wait()
self.play(
GrowFromCenter(brace),
Write(brace_text)
)
self.add_foreground_mobjects(brace, brace_text)
self.multiply_by_z(np.sqrt(5), run_time = 3)
self.wait(2)
to_remove = [
label, dot,
brace, brace_text,
arc, angle_label,
]
for mob in to_remove:
self.foreground_mobjects.remove(mob)
self.reset_plane(*list(map(FadeOut, to_remove)))
self.wait()
def show_break_down(self):
positive_reals = Line(ORIGIN, FRAME_X_RADIUS*RIGHT)
positive_reals.set_color(MAROON_B)
circle = Circle(
radius = self.z_to_point(1)[0],
color = MULTIPLIER_COLOR
)
real_actions = [3, 0.5, 1]
rotation_actions = [
np.exp(complex(0, angle))
for angle in np.linspace(0, 2*np.pi, 4)[1:]
]
self.play(ShowCreation(positive_reals))
self.add_foreground_mobjects(positive_reals)
for last_z, z in zip([1]+real_actions, real_actions):
self.multiply_by_z(z/last_z)
self.wait()
self.play(ShowCreation(circle))
self.add_foreground_mobjects(circle)
for last_z, z in zip([1]+rotation_actions, rotation_actions):
self.multiply_by_z(z/last_z, run_time = 3)
self.wait()
def example_actions_broken_down(self):
z_list = [
complex(2, -1),
complex(-2, -3),
complex(0.5, 0.5),
]
for z in z_list:
dot = Dot(self.z_to_point(z))
dot.set_color(WHITE)
dot.save_state()
dot.move_to(self.plane.one_dot)
dot.set_fill(opacity = 1)
norm = np.abs(z)
angle = np.angle(z)
rot_z = np.exp(complex(0, angle))
self.play(dot.restore)
self.multiply_by_z(norm)
self.wait()
self.multiply_by_z(rot_z)
self.wait()
self.reset_plane(FadeOut(dot))
##
def multiply_by_z(self, z, run_time = 2, **kwargs):
target = self.plane.copy()
target.apply_complex_function(lambda w : z*w)
for dot in target.zero_dot, target.one_dot:
dot.set_width(2*self.dot_radius)
angle = np.angle(z)
kwargs["path_arc"] = kwargs.get("path_arc", angle)
self.play(
Transform(self.plane, target, run_time = run_time, **kwargs),
*kwargs.get("added_anims", [])
)
def reset_plane(self, *added_anims):
self.play(FadeOut(self.plane), *added_anims)
self.plane.restore()
self.play(FadeIn(self.plane))
class ExponentsAsRepeatedMultiplication(TeacherStudentsScene):
def construct(self):
self.show_repeated_multiplication()
self.show_non_counting_exponents()
def show_repeated_multiplication(self):
three_twos = TexMobject("2 \\cdot 2 \\cdot 2")
five_twos = TexMobject("2 \\cdot "*4 + "2")
exponents = []
teacher_corner = self.get_teacher().get_corner(UP+LEFT)
for twos in three_twos, five_twos:
twos.next_to(teacher_corner, UP)
twos.generate_target()
d = sum(np.array(list(twos.get_tex_string())) == "2")
exponents.append(d)
twos.brace = Brace(twos, UP)
twos.exp = twos.brace.get_text("$2^%d$"%d)
twos.generate_target()
twos.brace_anim = MaintainPositionRelativeTo(
VGroup(twos.brace, twos.exp), twos
)
self.play(
GrowFromCenter(three_twos.brace),
Write(three_twos.exp),
self.get_teacher().change_mode, "raise_right_hand",
)
for mob in three_twos:
self.play(Write(mob, run_time = 1))
self.change_student_modes(*["pondering"]*3)
self.wait(2)
self.play(
FadeIn(five_twos.brace),
FadeIn(five_twos.exp),
three_twos.center,
three_twos.to_edge, UP, 2*LARGE_BUFF,
three_twos.brace_anim,
)
self.play(FadeIn(
five_twos,
run_time = 3,
submobject_mode = "lagged_start"
))
self.wait(2)
cdot = TexMobject("\\cdot")
lhs = TexMobject("2^{%d + %d} = "%tuple(exponents))
rule = VGroup(
lhs, three_twos.target, cdot, five_twos.target
)
rule.arrange_submobjects()
lhs.next_to(three_twos.target, LEFT, aligned_edge = DOWN)
rule.next_to(self.get_pi_creatures(), UP)
self.play(
MoveToTarget(three_twos),
three_twos.brace_anim,
MoveToTarget(five_twos),
five_twos.brace_anim,
Write(cdot),
self.get_teacher().change_mode, "happy",
)
self.wait()
self.play(Write(lhs))
self.wait()
self.change_student_modes(*["happy"]*3)
self.wait()
general_equation = TexMobject("2^{x+y}=", "2^x", "2^y")
general_equation.to_edge(UP, buff = MED_LARGE_BUFF)
general_equation[0].set_color(GREEN_B)
VGroup(*general_equation[1:]).set_color(MULTIPLIER_COLOR)
self.play(*[
ReplacementTransform(
mob.copy(), term, run_time = 2
)
for term, mob in zip(general_equation, [
lhs, three_twos.exp, five_twos.exp
])
])
self.wait(2)
self.exponential_rule = general_equation
self.expanded_exponential_rule = VGroup(
lhs, three_twos, three_twos.brace, three_twos.exp,
cdot, five_twos, five_twos.brace, five_twos.exp,
)
def show_non_counting_exponents(self):
self.play(
self.expanded_exponential_rule.scale, 0.5,
self.expanded_exponential_rule.to_corner, UP+LEFT
)
half_power, neg_power, imag_power = alt_powers = VGroup(
TexMobject("2^{1/2}"),
TexMobject("2^{-1}"),
TexMobject("2^{i}"),
)
alt_powers.arrange_submobjects(RIGHT, buff = LARGE_BUFF)
alt_powers.next_to(self.get_students(), UP, buff = LARGE_BUFF)
self.play(
Write(half_power, run_time = 2),
*[
ApplyMethod(pi.change_mode, "pondering")
for pi in self.get_pi_creatures()
]
)
for mob in alt_powers[1:]:
self.play(Write(mob, run_time = 1))
self.wait()
self.wait()
self.play(*it.chain(*[
[pi.change_mode, "confused", pi.look_at, half_power]
for pi in self.get_students()
]))
for power in alt_powers[:2]:
self.play(Indicate(power))
self.wait()
self.wait()
self.teacher_says("Extend the \\\\ definition")
self.change_student_modes("pondering", "confused", "erm")
self.wait()
half_expression = TexMobject(
"\\big(", "2^{1/2}", "\\big)",
"\\big(2^{1/2}\\big) = 2^{1}"
)
neg_one_expression = TexMobject(
"\\big(", "2^{-1}", "\\big)",
"\\big( 2^{1} \\big) = 2^{0}"
)
expressions = VGroup(half_expression, neg_one_expression)
expressions.arrange_submobjects(
DOWN, aligned_edge = LEFT, buff = MED_LARGE_BUFF
)
expressions.next_to(self.get_students(), UP, buff = LARGE_BUFF)
expressions.to_edge(LEFT)
self.play(
Transform(half_power, half_expression[1]),
Write(half_expression),
RemovePiCreatureBubble(self.get_teacher()),
)
self.wait()
self.play(
Transform(neg_power, neg_one_expression[1]),
Write(neg_one_expression)
)
self.wait(2)
self.play(
self.exponential_rule.next_to,
self.get_teacher().get_corner(UP+LEFT), UP, MED_LARGE_BUFF,
self.get_teacher().change_mode, "raise_right_hand",
)
self.wait(2)
self.play(
imag_power.move_to, UP,
imag_power.scale_in_place, 1.5,
imag_power.set_color, BLUE,
self.exponential_rule.to_edge, RIGHT,
self.get_teacher().change_mode, "speaking"
)
self.play(*it.chain(*[
[pi.change_mode, "pondering", pi.look_at, imag_power]
for pi in self.get_students()
]))
self.wait()
group_theory_words = TextMobject("Group theory?")
group_theory_words.next_to(
self.exponential_rule, UP, buff = LARGE_BUFF
)
arrow = Arrow(
group_theory_words,
self.exponential_rule,
color = WHITE,
buff = SMALL_BUFF
)
group_theory_words.shift_onto_screen()
self.play(
Write(group_theory_words),
ShowCreation(arrow)
)
self.wait(2)
class ExponentsAsHomomorphism(Scene):
CONFIG = {
"top_line_center" : 2.5*UP,
"top_line_config" : {
"x_min" : -16,
"x_max" : 16,
},
"bottom_line_center" : 2.5*DOWN,
"bottom_line_config" : {
"x_min" : -FRAME_WIDTH,
"x_max" : FRAME_WIDTH,
}
}
def construct(self):
self.comment_on_equation()
self.show_adders()
self.show_multipliers()
self.confused_at_mapping()
self.talk_through_composition()
self.add_quote()
def comment_on_equation(self):
equation = TexMobject(
"2", "^{x", "+", "y}", "=", "2^x", "2^y"
)
lhs = VGroup(*equation[:4])
rhs = VGroup(*equation[5:])
lhs_brace = Brace(lhs, UP)
lhs_text = lhs_brace.get_text("Add inputs")
lhs_text.set_color(GREEN_B)
rhs_brace = Brace(rhs, DOWN)
rhs_text = rhs_brace.get_text("Multiply outputs")
rhs_text.set_color(MULTIPLIER_COLOR)
self.add(equation)
for brace, text in (lhs_brace, lhs_text), (rhs_brace, rhs_text):
self.play(
GrowFromCenter(brace),
Write(text)
)
self.wait()
self.wait()
self.equation = equation
self.lhs_brace_group = VGroup(lhs_brace, lhs_text)
self.rhs_brace_group = VGroup(rhs_brace, rhs_text)
def show_adders(self):
equation = self.equation
adders = VGroup(equation[1], equation[3]).copy()
top_line = NumberLine(**self.top_line_config)
top_line.add_numbers()
top_line.shift(self.top_line_center)
self.play(
adders.scale, 1.5,
adders.center,
adders.space_out_submobjects, 2,
adders.to_edge, UP,
adders.set_color, GREEN_B,
FadeOut(self.lhs_brace_group),
Write(top_line)
)
self.wait()
for x in 3, 5, -8:
self.play(top_line.shift, x*RIGHT, run_time = 2)
self.wait()
self.top_line = top_line
self.adders = adders
def show_multipliers(self):
equation = self.equation
multipliers = VGroup(*self.equation[-2:]).copy()
bottom_line = NumberLine(**self.bottom_line_config)
bottom_line.add_numbers()
bottom_line.shift(self.bottom_line_center)
self.play(
multipliers.space_out_submobjects, 4,
multipliers.next_to, self.bottom_line_center,
UP, MED_LARGE_BUFF,
multipliers.set_color, YELLOW,
FadeOut(self.rhs_brace_group),
Write(bottom_line),
)
stretch_kwargs = {
}
for x in 3, 1./5, 5./3:
self.play(
self.get_stretch_anim(bottom_line, x),
run_time = 3
)
self.wait()
self.bottom_line = bottom_line
self.multipliers = multipliers
def confused_at_mapping(self):
arrow = Arrow(
self.top_line.get_bottom()[1]*UP,
self.bottom_line.get_top()[1]*UP,
color = WHITE
)
randy = Randolph(mode = "confused")
randy.scale(0.75)
randy.flip()
randy.next_to(arrow, RIGHT, LARGE_BUFF)
randy.look_at(arrow.get_top())
self.play(self.equation.to_edge, LEFT)
self.play(
ShowCreation(arrow),
FadeIn(randy)
)
self.play(randy.look_at, arrow.get_bottom())
self.play(Blink(randy))
self.wait()
for x in 1, -2, 3, 1, -3:
self.play(
self.get_stretch_anim(self.bottom_line, 2**x),
self.top_line.shift, x*RIGHT,
randy.look_at, self.top_line,
run_time = 2
)
if random.random() < 0.3:
self.play(Blink(randy))
else:
self.wait()
self.randy = randy
def talk_through_composition(self):
randy = self.randy
terms = list(self.adders) + list(self.multipliers)
inputs = [-1, 2]
target_texs = list(map(str, inputs))
target_texs += ["2^{%d}"%x for x in inputs]
for mob, target_tex in zip(terms, target_texs):
target = TexMobject(target_tex)
target.set_color(mob[0].get_color())
target.move_to(mob, DOWN)
if mob in self.adders:
target.to_edge(UP)
mob.target = target
self.play(
self.equation.next_to, ORIGIN, LEFT, MED_LARGE_BUFF,
randy.change_mode, "pondering",
randy.look_at, self.equation
)
self.wait()
self.play(randy.look_at, self.top_line)
self.show_composition(
*inputs,
parallel_anims = list(map(MoveToTarget, self.adders))
)
self.play(
FocusOn(self.bottom_line_center),
randy.look_at, self.bottom_line_center,
)
self.show_composition(
*inputs,
parallel_anims = list(map(MoveToTarget, self.multipliers))
)
self.wait()
def add_quote(self):
brace = Brace(self.equation, UP)
quote = TextMobject("``Preserves the group structure''")
quote.add_background_rectangle()
quote.next_to(brace, UP)
self.play(
GrowFromCenter(brace),
Write(quote),
self.randy.look_at, quote,
)
self.play(self.randy.change_mode, "thinking")
self.play(Blink(self.randy))
self.wait()
self.show_composition(-1, 2)
self.wait()
####
def show_composition(self, *inputs, **kwargs):
parallel_anims = kwargs.get("parallel_anims", [])
for x in range(len(inputs) - len(parallel_anims)):
parallel_anims.append(Animation(Mobject()))
for line in self.top_line, self.bottom_line:
line.save_state()
for x, parallel_anim in zip(inputs, parallel_anims):
anims = [
ApplyMethod(self.top_line.shift, x*RIGHT),
self.get_stretch_anim(self.bottom_line, 2**x),
]
for anim in anims:
anim.set_run_time(2)
self.play(parallel_anim)
self.play(*anims)
self.wait()
self.play(*[
line.restore
for line in (self.top_line, self.bottom_line)
])
def get_stretch_anim(self, bottom_line, x):
target = bottom_line.copy()
target.stretch_about_point(
x, 0, self.bottom_line_center,
)
for number in target.numbers:
number.stretch_in_place(1./x, dim = 0)
return Transform(bottom_line, target)
class DihedralCubeHomomorphism(GroupOfCubeSymmetries, SymmetriesOfSquare):
def construct(self):
angle_axis_pairs = [
(np.pi/2, OUT),
(np.pi, RIGHT),
(np.pi, OUT),
(np.pi, UP+RIGHT),
(-np.pi/2, OUT),
(np.pi, UP+LEFT),
]
angle_axis_pairs *= 3
title = TextMobject(
"``", "Homo", "morph", "ism", "''",
arg_separator = ""
)
homo_brace = Brace(title[1], UP, buff = SMALL_BUFF)
homo_def = homo_brace.get_text("same")
morph_brace = Brace(title[2], UP, buff = SMALL_BUFF)
morph_def = morph_brace.get_text("shape", buff = SMALL_BUFF)
def_group = VGroup(
homo_brace, homo_def,
morph_brace, morph_def
)
VGroup(title, def_group).to_edge(UP)
homo_group = VGroup(title[1], homo_brace, homo_def)
morph_group = VGroup(title[2], morph_brace, morph_def)
equation = TexMobject("f(X \\circ Y) = f(X) \\circ f(Y)")
equation.next_to(title, DOWN)
self.add(title, equation)
arrow = Arrow(LEFT, RIGHT)
cube = self.get_cube()
cube.next_to(arrow, RIGHT)
pose_matrix = self.get_pose_matrix()
square = self.square = Square(**self.square_config)
self.add_randy_to_square(square)
square.next_to(arrow, LEFT)
VGroup(square, arrow, cube).next_to(
equation, DOWN, buff = MED_LARGE_BUFF
)
self.add(square, cube)
self.play(ShowCreation(arrow))
for i, (angle, raw_axis) in enumerate(angle_axis_pairs):
posed_axis = np.dot(raw_axis, pose_matrix.T)
self.play(*[
Rotate(
mob, angle = angle, axis = axis,
in_place = True,
run_time = abs(angle/(np.pi/2))
)
for mob, axis in (square, raw_axis), (cube, posed_axis)
])
self.wait()
if i == 2:
for group, color in (homo_group, YELLOW), (morph_group, BLUE):
part, remainder = group[0], VGroup(*group[1:])
remainder.set_color(color)
self.play(
part.set_color, color,
FadeIn(remainder)
)
class ComplexExponentiationAbstract():
CONFIG = {
"start_base" : 2,
"new_base" : 5,
"group_type" : None,
"color" : None,
"vect" : None,
}
def construct(self):
self.base = self.start_base
example_inputs = [2, -3, 1]
self.add_vertical_line()
self.add_plane_unanimated()
self.add_title()
self.add_arrow()
self.show_example(complex(1, 1))
self.draw_real_line()
self.show_real_actions(*example_inputs)
self.show_pure_imaginary_actions(*example_inputs)
self.set_color_vertical_line()
self.set_color_unit_circle()
self.show_pure_imaginary_actions(*example_inputs)
self.walk_input_up_vertical()
self.change_base(self.new_base, str(self.new_base))
self.walk_input_up_vertical()
self.change_base(np.exp(1), "e")
self.take_steps_for_e()
self.write_eulers_formula()
self.show_pure_imaginary_actions(-np.pi, np.pi)
self.wait()
def add_vertical_line(self):
line = Line(FRAME_Y_RADIUS*UP, FRAME_Y_RADIUS*DOWN)
line.set_stroke(color = self.color, width = 10)
line.shift(-FRAME_X_RADIUS*self.vect/2)
self.add(line)
self.add_foreground_mobjects(line)
def add_plane_unanimated(self):
should_skip_animations = self.skip_animations
self.skip_animations = True
self.add_plane()
self.skip_animations = should_skip_animations
def add_title(self):
title = TextMobject(self.group_type, "group")
title.scale(0.8)
title[0].set_color(self.color)
title.add_background_rectangle()
title.to_edge(UP, buff = MED_SMALL_BUFF)
self.add_foreground_mobjects(title)
def add_arrow(self):
arrow = Arrow(LEFT, RIGHT, color = WHITE)
arrow.move_to(-FRAME_X_RADIUS*self.vect/2 + 2*UP)
arrow.set_stroke(width = 6),
func_mob = TexMobject("2^x")
func_mob.next_to(arrow, UP, aligned_edge = LEFT)
func_mob.add_background_rectangle()
self.add_foreground_mobjects(arrow, func_mob)
self.wait()
self.func_mob = func_mob
def show_example(self, z):
self.apply_action(
z,
run_time = 5,
rate_func = there_and_back
)
def draw_real_line(self):
line = VGroup(Line(ORIGIN, FRAME_X_RADIUS*RIGHT))
if self.vect[0] < 0:
line.add(Line(ORIGIN, FRAME_X_RADIUS*LEFT))
line.set_color(RED)
self.play(*list(map(ShowCreation, line)), run_time = 3)
self.add_foreground_mobjects(line)
self.real_line = line
def show_real_actions(self, *example_inputs):
for x in example_inputs:
self.apply_action(x)
self.wait()
def show_pure_imaginary_actions(self, *example_input_imag_parts):
for y in example_input_imag_parts:
self.apply_action(complex(0, y), run_time = 3)
self.wait()
def change_base(self, new_base, new_base_tex):
new_func_mob = TexMobject(new_base_tex + "^x")
new_func_mob.add_background_rectangle()
new_func_mob.move_to(self.func_mob)
self.play(FocusOn(self.func_mob))
self.play(Transform(self.func_mob, new_func_mob))
self.wait()
self.base = new_base
def write_eulers_formula(self):
formula = TexMobject("e^", "{\\pi", "i}", "=", "-1")
VGroup(*formula[1:3]).set_color(ADDER_COLOR)
formula[-1].set_color(MULTIPLIER_COLOR)
formula.scale(1.5)
formula.next_to(ORIGIN, UP)
formula.shift(-FRAME_X_RADIUS*self.vect/2)
for part in formula:
part.add_to_back(BackgroundRectangle(part))
Scene.play(self, Write(formula))
self.add_foreground_mobjects(formula)
self.wait(2)
class ComplexExponentiationAdderHalf(
ComplexExponentiationAbstract,
AdditiveGroupOfComplexNumbers
):
CONFIG = {
"group_type" : "Additive",
"color" : GREEN_B,
"vect" : LEFT,
}
def construct(self):
ComplexExponentiationAbstract.construct(self)
def apply_action(self, z, run_time = 2, **kwargs):
kwargs["run_time"] = run_time
self.play(
ApplyMethod(
self.plane.shift, self.z_to_point(z),
**kwargs
),
*kwargs.get("added_anims", [])
)
def set_color_vertical_line(self):
line = VGroup(
Line(ORIGIN, FRAME_Y_RADIUS*UP),
Line(ORIGIN, FRAME_Y_RADIUS*DOWN),
)
line.set_color(YELLOW)
self.play(
FadeOut(self.real_line),
*list(map(ShowCreation, line))
)
self.foreground_mobjects.remove(self.real_line)
self.play(
line.rotate, np.pi/24,
rate_func = wiggle,
)
self.wait()
self.foreground_mobjects = [line] + self.foreground_mobjects
self.vertical_line = line
def set_color_unit_circle(self):
line = VGroup(
Line(ORIGIN, FRAME_Y_RADIUS*UP),
Line(ORIGIN, FRAME_Y_RADIUS*DOWN),
)
line.set_color(YELLOW)
for submob in line:
submob.insert_n_anchor_points(10)
submob.make_smooth()
circle = VGroup(
Circle(),
Circle().flip(RIGHT),
)
circle.set_color(YELLOW)
circle.shift(FRAME_X_RADIUS*RIGHT)
self.play(ReplacementTransform(
line, circle, run_time = 3
))
self.remove(circle)
self.wait()
def walk_input_up_vertical(self):
arrow = Arrow(ORIGIN, UP, buff = 0, tip_length = 0.15)
arrow.set_color(GREEN)
brace = Brace(arrow, RIGHT, buff = SMALL_BUFF)
brace_text = brace.get_text("1 unit")
brace_text.add_background_rectangle()
Scene.play(self, ShowCreation(arrow))
self.add_foreground_mobjects(arrow)
self.play(
GrowFromCenter(brace),
Write(brace_text, run_time = 1)
)
self.add_foreground_mobjects(brace, brace_text)
self.wait()
self.apply_action(complex(0, 1))
self.wait(7)##Line up with MultiplierHalf
to_remove = arrow, brace, brace_text
for mob in to_remove:
self.foreground_mobjects.remove(mob)
self.play(*list(map(FadeOut, to_remove)))
self.apply_action(complex(0, -1))
def take_steps_for_e(self):
slide_values = [1, 1, 1, np.pi-3]
braces = [
Brace(Line(ORIGIN, x*UP), RIGHT, buff = SMALL_BUFF)
for x in np.cumsum(slide_values)
]
labels = list(map(TextMobject, [
"1 unit",
"2 units",
"3 units",
"$\\pi$ units",
]))
for label, brace in zip(labels, braces):
label.add_background_rectangle()
label.next_to(brace, RIGHT, buff = SMALL_BUFF)
curr_brace = None
curr_label = None
for slide_value, label, brace in zip(slide_values, labels, braces):
self.apply_action(complex(0, slide_value))
if curr_brace is None:
curr_brace = brace
curr_label = label
self.play(
GrowFromCenter(curr_brace),
Write(curr_label)
)
self.add_foreground_mobjects(brace, label)
else:
self.play(
Transform(curr_brace, brace),
Transform(curr_label, label),
)
self.wait()
self.wait(4) ##Line up with multiplier half
class ComplexExponentiationMultiplierHalf(
ComplexExponentiationAbstract,
MultiplicativeGroupOfComplexNumbers
):
CONFIG = {
"group_type" : "Multiplicative",
"color" : MULTIPLIER_COLOR,
"vect" : RIGHT,
}
def construct(self):
ComplexExponentiationAbstract.construct(self)
def apply_action(self, z, run_time = 2, **kwargs):
kwargs["run_time"] = run_time
self.multiply_by_z(self.base**z, **kwargs)
def set_color_vertical_line(self):
self.play(FadeOut(self.real_line))
self.foreground_mobjects.remove(self.real_line)
self.wait(2)
def set_color_unit_circle(self):
line = VGroup(
Line(ORIGIN, FRAME_Y_RADIUS*UP),
Line(ORIGIN, FRAME_Y_RADIUS*DOWN),
)
line.set_color(YELLOW)
line.shift(FRAME_X_RADIUS*LEFT)
for submob in line:
submob.insert_n_anchor_points(10)
submob.make_smooth()
circle = VGroup(
Circle(),
Circle().flip(RIGHT),
)
circle.set_color(YELLOW)
self.play(ReplacementTransform(
line, circle, run_time = 3
))
self.add_foreground_mobjects(circle)
self.wait()
def walk_input_up_vertical(self):
output_z = self.base**complex(0, 1)
angle = np.angle(output_z)
arc, brace, curved_brace, radians_label = \
self.get_arc_braces_and_label(angle)
self.wait(3)
self.apply_action(complex(0, 1))
Scene.play(self, ShowCreation(arc))
self.add_foreground_mobjects(arc)
self.play(GrowFromCenter(brace))
self.play(Transform(brace, curved_brace))
self.play(Write(radians_label, run_time = 2))
self.wait(2)
self.foreground_mobjects.remove(arc)
self.play(*list(map(FadeOut, [arc, brace, radians_label])))
self.apply_action(complex(0, -1))
def get_arc_braces_and_label(self, angle):
arc = Arc(angle)
arc.set_stroke(GREEN, width = 6)
arc_line = Line(RIGHT, RIGHT+angle*UP)
brace = Brace(arc_line, RIGHT, buff = 0)
for submob in brace.family_members_with_points():
submob.insert_n_anchor_points(10)
curved_brace = brace.copy()
curved_brace.shift(LEFT)
curved_brace.apply_complex_function(
np.exp, maintain_smoothness = False
)
half_point = arc.point_from_proportion(0.5)
radians_label = TexMobject("%.3f"%angle)
radians_label.add_background_rectangle()
radians_label.next_to(
1.5*half_point, np.round(half_point), buff = 0
)
return arc, brace, curved_brace, radians_label
def take_steps_for_e(self):
angles = [1, 2, 3, np.pi]
curr_brace = None
curr_label = None
curr_arc = None
for last_angle, angle in zip([0]+angles, angles):
arc, brace, curved_brace, label = self.get_arc_braces_and_label(angle)
if angle == np.pi:
label = TexMobject("%.5f\\dots"%np.pi)
label.add_background_rectangle(opacity = 1)
label.next_to(curved_brace, UP, buff = SMALL_BUFF)
self.apply_action(complex(0, angle-last_angle))
self.wait(2)#Line up with Adder half
if curr_brace is None:
curr_brace = curved_brace
curr_label = label
curr_arc = arc
brace.set_fill(opacity = 0)
Scene.play(self, ShowCreation(curr_arc))
self.add_foreground_mobjects(curr_arc)
self.play(
ReplacementTransform(brace, curr_brace),
Write(curr_label)
)
self.add_foreground_mobjects(curr_brace, curr_label)
else:
Scene.play(self, ShowCreation(arc))
self.add_foreground_mobjects(arc)
self.foreground_mobjects.remove(curr_arc)
self.remove(curr_arc)
curr_arc = arc
self.play(
Transform(curr_brace, curved_brace),
Transform(curr_label, label),
)
self.wait()
self.wait()
class ExpComplexHomomorphismPreviewAbstract(ComplexExponentiationAbstract):
def construct(self):
self.base = self.start_base
self.add_vertical_line()
self.add_plane_unanimated()
self.add_title()
self.add_arrow()
self.change_base(np.exp(1), "e")
self.write_eulers_formula()
self.show_pure_imaginary_actions(np.pi, 0, -np.pi)
self.wait()
class ExpComplexHomomorphismPreviewAdderHalf(
ExpComplexHomomorphismPreviewAbstract,
ComplexExponentiationAdderHalf
):
def construct(self):
ExpComplexHomomorphismPreviewAbstract.construct(self)
class ExpComplexHomomorphismPreviewMultiplierHalf(
ExpComplexHomomorphismPreviewAbstract,
ComplexExponentiationMultiplierHalf
):
def construct(self):
ExpComplexHomomorphismPreviewAbstract.construct(self)
class WhyE(TeacherStudentsScene):
def construct(self):
self.student_says("Why e?")
self.play(self.get_teacher().change_mode, "pondering")
self.wait(3)
class ReadFormula(Scene):
def construct(self):
formula = TexMobject("e^", "{\\pi i}", "=", "-1")
formula[1].set_color(GREEN_B)
formula[3].set_color(MULTIPLIER_COLOR)
formula.scale(2)
randy = Randolph()
randy.shift(2*LEFT)
formula.next_to(randy, RIGHT, aligned_edge = UP)
randy.look_at(formula)
self.add(randy, formula)
self.wait()
self.play(randy.change_mode, "thinking")
self.wait()
self.play(Blink(randy))
self.wait(3)
class EfvgtPatreonThanks(PatreonThanks):
CONFIG = {
"specific_patrons" : [
"Ali Yahya",
"Meshal Alshammari",
"CrypticSwarm ",
"Justin Helps",
"Ankit Agarwal",
"Yu Jun",
"Shelby Doolittle",
"Dave Nicponski",
"Damion Kistler",
"Juan Benet",
"Othman Alikhan",
"Markus Persson",
"Dan Buchoff",
"Derek Dai",
"Joseph John Cox",
"Luc Ritchie",
"Nils Schneider",
"Mathew Bramson",
"Guido Gambardella",
"Jerry Ling",
"Mark Govea",
"Vecht",
"Shimin Kuang",
"Rish Kundalia",
"Achille Brighton",
"Kirk Werklund",
"Ripta Pasay",
"Felipe Diniz",
]
}
class EmeraldLogo(SVGMobject):
CONFIG = {
"file_name" : "emerald_logo",
"stroke_width" : 0,
"fill_opacity" : 1,
"propagate_style_to_family" : True,
# "helix_color" : "#439271",
"helix_color" : GREEN_E,
}
def __init__(self, **kwargs):
SVGMobject.__init__(self, **kwargs)
self.set_height(1)
for submob in self.split()[18:]:
submob.set_color(self.helix_color)
class ECLPromo(PiCreatureScene):
CONFIG = {
"seconds_to_blink" : 4,
}
def construct(self):
logo = EmeraldLogo()
logo.to_corner(UP+LEFT, buff = MED_SMALL_BUFF)
logo_part1 = VGroup(*logo[:15])
logo_part2 = VGroup(*logo[15:])
rect = Rectangle(height = 9, width = 16)
rect.set_height(5)
rect.next_to(logo, DOWN)
rect.to_edge(LEFT)
self.play(
self.pi_creature.change_mode, "hooray",
ShowCreation(rect)
)
self.wait(3)
self.play(FadeIn(
logo_part1, run_time = 3,
submobject_mode = "lagged_start"
))
logo_part2.save_state()
logo_part2.scale(2)
logo_part2.next_to(self.pi_creature.get_corner(UP+LEFT), UP)
logo_part2.shift(MED_SMALL_BUFF*RIGHT)
self.play(
self.pi_creature.change_mode, "raise_right_hand",
)
self.play(DrawBorderThenFill(logo_part2))
self.play(
logo_part2.scale_in_place, 0.5,
logo_part2.to_edge, UP
)
self.play(
logo_part2.restore,
self.pi_creature.change_mode, "happy"
)
self.play(self.pi_creature.look_at, rect)
self.wait(10)
self.play(
self.pi_creature.change_mode, "pondering",
self.pi_creature.look, DOWN
)
self.wait(10)
class ExpTransformation(ComplexTransformationScene):
CONFIG = {
"camera_class": ThreeDCamera,
}
def construct(self):
self.camera.camera_distance = 10,
self.add_transformable_plane()
self.prepare_for_transformation(self.plane)
final_plane = self.plane.copy().apply_complex_function(np.exp)
cylinder = self.plane.copy().apply_function(
lambda x_y_z : np.array([x_y_z[0], np.sin(x_y_z[1]), -np.cos(x_y_z[1])])
)
title = TexMobject("x \\to e^x")
title.add_background_rectangle()
title.scale(1.5)
title.next_to(ORIGIN, RIGHT)
title.to_edge(UP, buff = MED_SMALL_BUFF)
self.add_foreground_mobjects(title)
self.play(Transform(
self.plane, cylinder,
run_time = 3,
path_arc_axis = RIGHT,
path_arc = np.pi,
))
self.play(Rotate(
self.plane, -np.pi/3, UP,
run_time = 5
))
self.play(Transform(self.plane, final_plane, run_time = 3))
self.wait(3)
class Thumbnail(Scene):
def construct(self):
formula = TexMobject("e^", "{\\pi i}", "=", "-1")
formula[1].set_color(GREEN_B)
formula[3].set_color(YELLOW)
formula.scale(4)
formula.to_edge(UP, buff = LARGE_BUFF)
self.add(formula)
via = TextMobject("via")
via.scale(2)
groups = TextMobject("Group theory")
groups.scale(3)
groups.to_edge(DOWN)
via.move_to(VGroup(formula, groups))
self.add(via, groups)