Merge pull request #196 from 3b1b/eola2

Eola2
This commit is contained in:
Grant Sanderson 2018-04-06 13:09:53 -07:00 committed by GitHub
commit 7c272c6236
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 529 additions and 223 deletions

View file

View file

@ -0,0 +1,286 @@
from big_ol_pile_of_manim_imports import *
class WorkOutNumerically(Scene):
CONFIG = {
"M1_COLOR": TEAL,
"M2_COLOR": PINK,
}
def construct(self):
self.add_question()
self.add_example()
self.compute_rhs()
self.compute_lhs()
def add_question(self):
equation = self.original_equation = TexMobject(
"\\det(", "M_1", "M_2", ")", "=",
"\\det(", "M_1", ")",
"\\det(", "M_2", ")",
)
equation.set_color_by_tex_to_color_map({
"M_1": self.M1_COLOR,
"M_2": self.M2_COLOR,
})
challenge = TextMobject("Explain in one sentence")
challenge.set_color(YELLOW)
group = VGroup(challenge, equation)
group.arrange_submobjects(DOWN)
group.to_edge(UP)
self.add(equation)
self.play(Write(challenge))
self.wait()
def add_example(self):
M1 = self.M1 = Matrix([[2, -1], [1, 1]])
M1.set_color(self.M1_COLOR)
self.M1_copy = M1.copy()
M2 = self.M2 = Matrix([[-1, 4], [1, 1]])
M2.set_color(self.M2_COLOR)
self.M2_copy = M2.copy()
eq_parts = TexMobject(
"\\det", "\\big(", "\\big)", "=",
"\\det", "\\big(", "\\big)",
"\\det", "\\big(", "\\big)",
)
for part in eq_parts.get_parts_by_tex("\\big"):
part.scale(2)
part.stretch(1.5, 1)
i1, i2, i3 = [
eq_parts.index_of_part(part) + 1
for part in eq_parts.get_parts_by_tex("\\big(")
]
equation = self.equation_with_numbers = VGroup(*it.chain(
eq_parts[:i1], [M1, M2],
eq_parts[i1:i2], [self.M1_copy],
eq_parts[i2:i3], [self.M2_copy],
eq_parts[i3:],
))
equation.arrange_submobjects(RIGHT, buff=SMALL_BUFF)
eq_parts.get_part_by_tex("=").shift(0.2 * SMALL_BUFF * DOWN)
equation.scale_to_fit_width(FRAME_WIDTH - 2 * LARGE_BUFF)
equation.next_to(self.original_equation, DOWN, MED_LARGE_BUFF)
self.play(LaggedStart(FadeIn, equation))
def compute_rhs(self):
M1, M2 = self.M1_copy, self.M2_copy
line1 = VGroup(
TexMobject(
"\\big(", "2", "\\cdot", "2", "-",
"(-1)", "\\cdot", "1", "\\big)"
),
TexMobject(
"\\big(", "-1", "\\cdot", "1", "-",
"4", "\\cdot", "1", "\\big)"
),
)
line1.arrange_submobjects(RIGHT, buff=SMALL_BUFF)
line1[0].set_color(self.M1_COLOR)
line1[1].set_color(self.M2_COLOR)
indices = [1, 3, 5, 7]
line2 = TexMobject("(3)", "(-5)")
line2.match_style(line1)
line3 = TexMobject("-15")
arrows = [TexMobject("\\downarrow") for x in range(2)]
lines = VGroup(line1, arrows[0], line2, arrows[1], line3)
lines.arrange_submobjects(DOWN, buff=MED_SMALL_BUFF)
lines.next_to(self.equation_with_numbers, DOWN, buff=MED_LARGE_BUFF)
lines.to_edge(RIGHT)
for matrix, det in zip([M1, M2], line1):
numbers = VGroup(*[det[i] for i in indices])
numbers_iter = iter(numbers)
non_numbers = VGroup(*filter(
lambda m: m not in numbers,
det
))
matrix_numbers = VGroup(*[
matrix.mob_matrix[i][j].copy()
for i, j in (0, 0), (1, 1), (0, 1), (1, 0)
])
self.play(
LaggedStart(FadeIn, non_numbers, run_time=1),
LaggedStart(
ReplacementTransform,
matrix_numbers,
lambda m: (m, numbers_iter.next()),
path_arc=TAU / 6
),
)
self.play(LaggedStart(FadeIn, lines[1:], run_time=3))
def compute_lhs(self):
matrix = Matrix([[-3, 7], [0, 5]])
matrix.set_color(BLUE)
matrix.scale(0.8)
empty_det_tex = TexMobject("\\det", "\\big(", "\\big)")
empty_det_tex[1:].scale(1.5)
empty_det_tex[1:].match_height(matrix, stretch=True)
det_tex = VGroup(empty_det_tex[:2], matrix, *empty_det_tex[2:])
det_tex.arrange_submobjects(RIGHT, buff=SMALL_BUFF)
group = VGroup(
det_tex,
TexMobject("\\downarrow"),
TexMobject("(-3)(5) - (7)(0)").scale(0.8),
TexMobject("\\downarrow"),
TexMobject("-15"),
)
group.arrange_submobjects(DOWN, buff=2 * SMALL_BUFF)
# group.scale_to_fit_height(0.4*FRAME_HEIGHT)
group.next_to(self.equation_with_numbers, DOWN)
group.shift(FRAME_WIDTH * LEFT / 4)
self.play(FadeIn(empty_det_tex))
self.play(*[
ReplacementTransform(M.copy(), matrix)
for M in self.M1, self.M2
])
self.play(LaggedStart(FadeIn, group[1:]))
self.wait()
class LetsGoInOneSentence(TeacherStudentsScene):
def construct(self):
self.teacher_says(
"Here we go, \\\\", "one sentence!"
)
self.change_all_student_modes("hooray")
self.teacher_says(
"Or three...", "",
target_mode="guilty"
)
self.change_all_student_modes("sassy")
self.wait(4)
class SuccessiveLinearTransformations(LinearTransformationScene):
CONFIG = {
"matrix_2": [[3, -1], [0, 1]],
"matrix_1": [[2, 3], [-1. / 3, 2]],
}
def construct(self):
self.create_product_and_inverse()
self.scale_area_successively()
self.apply_transformations_successively()
self.scale_area_successively(
"\\det(M_2)", "\\det(M_1)", "\\det(M_1 M_2)",
reset=False
)
# self.show_det_as_scaling_factor()
def create_product_and_inverse(self):
self.matrix_product = np.dot(self.matrix_1, self.matrix_2)
self.matrix_product_inverse = np.linalg.inv(self.matrix_product)
def scale_area_successively(self, tex2="3", tex1="5", tex_prod="15", reset=True):
self.add_unit_square()
t1 = "$%s \\, \\cdot $" % tex1
t2 = "$%s \\, \\cdot $" % tex2
t3 = "Area"
areas = VGroup(
TextMobject("", "", t3),
TextMobject("", t2, t3),
TextMobject(t1, t2, t3),
)
areas.scale(0.8)
areas.move_to(self.square)
area = areas[0]
self.add_moving_mobject(area, areas[1])
self.play(
FadeIn(self.square),
Write(area),
Animation(self.basis_vectors)
)
self.apply_matrix(self.matrix_2)
self.wait()
self.add_moving_mobject(area, areas[2])
self.apply_matrix(self.matrix_1)
self.wait()
product = VGroup(area[:2])
brace = Brace(product, DOWN, buff=SMALL_BUFF)
brace_tex = brace.get_tex(tex_prod, buff=SMALL_BUFF)
brace_tex.scale(0.8, about_edge=UP)
self.play(
GrowFromCenter(brace),
Write(brace_tex)
)
self.wait()
if reset:
self.play(
FadeOut(VGroup(self.square, area, brace, brace_tex)),
Animation(self.plane),
Animation(self.basis_vectors)
)
self.transformable_mobjects.remove(self.square)
self.moving_mobjects = []
self.reset_plane()
self.wait()
def apply_transformations_successively(self):
M1, M2, all_space = expression = TexMobject(
"M_1", "M_2", "\\text{(All 2d space)}"
)
expression.set_color_by_tex_to_color_map({
"M_1": TEAL,
"M_2": PINK,
})
expression.shift(FRAME_WIDTH * LEFT / 4)
expression.to_edge(UP)
for part in expression:
part.add_background_rectangle()
part.background_rectangle.stretch(1.05, 0)
M1.save_state()
M1.move_to(ORIGIN)
M1.fade(1)
# Apply one after the other
self.play(
FocusOn(M2, run_time=1),
FadeIn(VGroup(M2, all_space))
)
self.add_foreground_mobjects(M2, all_space)
self.apply_matrix(self.matrix_2)
self.wait()
self.play(M1.restore)
self.add_foreground_mobjects(M1)
self.apply_matrix(self.matrix_1)
self.wait()
# Show full composition
rp, lp = parens = TexMobject("()")
matrices = VGroup(M1, M2)
matrices.generate_target()
parens.match_height(matrices)
lp.move_to(matrices, RIGHT)
matrices.target.next_to(lp, LEFT, SMALL_BUFF)
rp.next_to(matrices.target, LEFT, SMALL_BUFF)
self.reset_plane()
self.play(
MoveToTarget(matrices),
*map(GrowFromCenter, parens)
)
self.apply_matrix(self.matrix_product)
self.wait()
self.reset_plane()
def show_det_as_scaling_factor(self):
pass
###
def reset_plane(self):
plane_and_bases = VGroup(self.plane, self.basis_vectors)
self.play(FadeOut(plane_and_bases))
self.apply_matrix(self.matrix_product_inverse, run_time=0)
self.play(FadeIn(plane_and_bases))

View file

@ -15,7 +15,8 @@ from utils.paths import counterclockwise_path
from utils.rate_functions import double_smooth from utils.rate_functions import double_smooth
from utils.rate_functions import smooth from utils.rate_functions import smooth
#Drawing # Drawing
class ShowPartial(Animation): class ShowPartial(Animation):
def update_submobject(self, submobject, starting_submobject, alpha): def update_submobject(self, submobject, starting_submobject, alpha):
@ -26,26 +27,31 @@ class ShowPartial(Animation):
def get_bounds(self, alpha): def get_bounds(self, alpha):
raise Exception("Not Implemented") raise Exception("Not Implemented")
class ShowCreation(ShowPartial): class ShowCreation(ShowPartial):
CONFIG = { CONFIG = {
"submobject_mode" : "one_at_a_time", "submobject_mode": "one_at_a_time",
} }
def get_bounds(self, alpha): def get_bounds(self, alpha):
return (0, alpha) return (0, alpha)
class Uncreate(ShowCreation): class Uncreate(ShowCreation):
CONFIG = { CONFIG = {
"rate_func" : lambda t : smooth(1-t), "rate_func": lambda t: smooth(1 - t),
"remover" : True "remover": True
} }
class Write(ShowCreation): class Write(ShowCreation):
CONFIG = { CONFIG = {
"rate_func" : None, "rate_func": None,
"submobject_mode" : "lagged_start", "submobject_mode": "lagged_start",
} }
def __init__(self, mob_or_text, **kwargs): def __init__(self, mob_or_text, **kwargs):
digest_config(self, kwargs) digest_config(self, kwargs)
if isinstance(mob_or_text, str): if isinstance(mob_or_text, str):
mobject = TextMobject(mob_or_text) mobject = TextMobject(mob_or_text)
else: else:
@ -67,13 +73,15 @@ class Write(ShowCreation):
else: else:
self.run_time = 2 self.run_time = 2
class DrawBorderThenFill(Animation): class DrawBorderThenFill(Animation):
CONFIG = { CONFIG = {
"run_time" : 2, "run_time": 2,
"stroke_width" : 2, "stroke_width": 2,
"stroke_color" : None, "stroke_color": None,
"rate_func" : double_smooth, "rate_func": double_smooth,
} }
def __init__(self, vmobject, **kwargs): def __init__(self, vmobject, **kwargs):
if not isinstance(vmobject, VMobject): if not isinstance(vmobject, VMobject):
raise Exception("DrawBorderThenFill only works for VMobjects") raise Exception("DrawBorderThenFill only works for VMobjects")
@ -82,59 +90,64 @@ class DrawBorderThenFill(Animation):
def update_submobject(self, submobject, starting_submobject, alpha): def update_submobject(self, submobject, starting_submobject, alpha):
submobject.pointwise_become_partial( submobject.pointwise_become_partial(
starting_submobject, 0, min(2*alpha, 1) starting_submobject, 0, min(2 * alpha, 1)
) )
if alpha < 0.5: if alpha < 0.5:
if self.stroke_color: if self.stroke_color:
color = self.stroke_color color = self.stroke_color
elif starting_submobject.stroke_width > 0: elif starting_submobject.stroke_width > 0:
color = starting_submobject.get_stroke_color() color = starting_submobject.get_stroke_color()
else: else:
color = starting_submobject.get_color() color = starting_submobject.get_color()
submobject.set_stroke(color, width = self.stroke_width) submobject.set_stroke(color, width=self.stroke_width)
submobject.set_fill(opacity = 0) submobject.set_fill(opacity=0)
else: else:
if not self.reached_halfway_point_before: if not self.reached_halfway_point_before:
self.reached_halfway_point_before = True self.reached_halfway_point_before = True
submobject.points = np.array(starting_submobject.points) submobject.points = np.array(starting_submobject.points)
width, opacity = [ width, opacity = [
interpolate(start, end, 2*alpha - 1) interpolate(start, end, 2 * alpha - 1)
for start, end in [ for start, end in [
(self.stroke_width, starting_submobject.get_stroke_width()), (self.stroke_width, starting_submobject.get_stroke_width()),
(0, starting_submobject.get_fill_opacity()) (0, starting_submobject.get_fill_opacity())
] ]
] ]
submobject.set_stroke(width = width) submobject.set_stroke(width=width)
submobject.set_fill(opacity = opacity) submobject.set_fill(opacity=opacity)
# Fading
#Fading
class FadeOut(Transform): class FadeOut(Transform):
CONFIG = { CONFIG = {
"remover" : True, "remover": True,
} }
def __init__(self, mobject, **kwargs): def __init__(self, mobject, **kwargs):
target = mobject.copy() target = mobject.copy()
target.fade(1) target.fade(1)
Transform.__init__(self, mobject, target, **kwargs) Transform.__init__(self, mobject, target, **kwargs)
def clean_up(self, surrounding_scene = None): def clean_up(self, surrounding_scene=None):
Transform.clean_up(self, surrounding_scene) Transform.clean_up(self, surrounding_scene)
self.update(0) self.update(0)
class FadeIn(Transform): class FadeIn(Transform):
def __init__(self, mobject, **kwargs): def __init__(self, mobject, **kwargs):
target = mobject.copy() target = mobject.copy()
Transform.__init__(self, mobject, target, **kwargs) Transform.__init__(self, mobject, target, **kwargs)
self.starting_mobject.fade(1) self.starting_mobject.fade(1)
if isinstance(self.starting_mobject, VMobject): if isinstance(self.starting_mobject, VMobject):
self.starting_mobject.set_stroke(width = 0) self.starting_mobject.set_stroke(width=0)
self.starting_mobject.set_fill(opacity = 0) self.starting_mobject.set_fill(opacity=0)
class FadeInAndShiftFromDirection(Transform): class FadeInAndShiftFromDirection(Transform):
CONFIG = { CONFIG = {
"direction" : DOWN, "direction": DOWN,
} }
def __init__(self, mobject, **kwargs): def __init__(self, mobject, **kwargs):
digest_config(self, kwargs) digest_config(self, kwargs)
target = mobject.copy() target = mobject.copy()
@ -142,19 +155,23 @@ class FadeInAndShiftFromDirection(Transform):
mobject.fade(1) mobject.fade(1)
Transform.__init__(self, mobject, target, **kwargs) Transform.__init__(self, mobject, target, **kwargs)
class FadeInFromDown(FadeInAndShiftFromDirection): class FadeInFromDown(FadeInAndShiftFromDirection):
""" """
Essential a more convenient form of FadeInAndShiftFromDirection Essential a more convenient form of FadeInAndShiftFromDirection
""" """
CONFIG = { CONFIG = {
"direction" : DOWN, "direction": DOWN,
} }
#Growing # Growing
class GrowFromPoint(Transform): class GrowFromPoint(Transform):
CONFIG = { CONFIG = {
"point_color" : None, "point_color": None,
} }
def __init__(self, mobject, point, **kwargs): def __init__(self, mobject, point, **kwargs):
digest_config(self, kwargs) digest_config(self, kwargs)
target = mobject.copy() target = mobject.copy()
@ -165,19 +182,23 @@ class GrowFromPoint(Transform):
mobject.set_color(point_mob.get_color()) mobject.set_color(point_mob.get_color())
Transform.__init__(self, mobject, target, **kwargs) Transform.__init__(self, mobject, target, **kwargs)
class GrowFromCenter(GrowFromPoint): class GrowFromCenter(GrowFromPoint):
def __init__(self, mobject, **kwargs): def __init__(self, mobject, **kwargs):
GrowFromPoint.__init__(self, mobject, mobject.get_center(), **kwargs) GrowFromPoint.__init__(self, mobject, mobject.get_center(), **kwargs)
class GrowArrow(GrowFromPoint): class GrowArrow(GrowFromPoint):
def __init__(self, arrow, **kwargs): def __init__(self, arrow, **kwargs):
GrowFromPoint.__init__(self, arrow, arrow.get_start(), **kwargs) GrowFromPoint.__init__(self, arrow, arrow.get_start(), **kwargs)
class SpinInFromNothing(GrowFromCenter): class SpinInFromNothing(GrowFromCenter):
CONFIG = { CONFIG = {
"path_func" : counterclockwise_path() "path_func": counterclockwise_path()
} }
class ShrinkToCenter(Transform): class ShrinkToCenter(Transform):
def __init__(self, mobject, **kwargs): def __init__(self, mobject, **kwargs):
Transform.__init__( Transform.__init__(

View file

@ -1,7 +1,6 @@
#!/usr/bin/env python2 #!/usr/bin/env python2
import sys import sys
# import getopt
import argparse import argparse
import imp import imp
import imp import imp
@ -11,8 +10,9 @@ import os
import subprocess as sp import subprocess as sp
import traceback import traceback
from camera.camera import Camera
from constants import * from constants import *
from camera.camera import Camera
from scene.scene import Scene from scene.scene import Scene
from utils.sounds import play_error_sound from utils.sounds import play_error_sound
from utils.sounds import play_finish_sound from utils.sounds import play_finish_sound

View file

@ -25,17 +25,19 @@ from scene.scene import Scene
from utils.rate_functions import squish_rate_func from utils.rate_functions import squish_rate_func
from utils.rate_functions import there_and_back from utils.rate_functions import there_and_back
class PiCreatureScene(Scene): class PiCreatureScene(Scene):
CONFIG = { CONFIG = {
"total_wait_time" : 0, "total_wait_time": 0,
"seconds_to_blink" : 3, "seconds_to_blink": 3,
"pi_creatures_start_on_screen" : True, "pi_creatures_start_on_screen": True,
"default_pi_creature_kwargs" : { "default_pi_creature_kwargs": {
"color" : GREY_BROWN, "color": GREY_BROWN,
"flip_at_start" : True, "flip_at_start": True,
}, },
"default_pi_creature_start_corner" : DOWN+LEFT, "default_pi_creature_start_corner": DOWN + LEFT,
} }
def setup(self): def setup(self):
self.pi_creatures = self.create_pi_creatures() self.pi_creatures = self.create_pi_creatures()
self.pi_creature = self.get_primary_pi_creature() self.pi_creature = self.get_primary_pi_creature()
@ -43,7 +45,7 @@ class PiCreatureScene(Scene):
self.add(*self.pi_creatures) self.add(*self.pi_creatures)
def create_pi_creatures(self): def create_pi_creatures(self):
""" """
Likely updated for subclasses Likely updated for subclasses
""" """
return VGroup(self.create_pi_creature()) return VGroup(self.create_pi_creature())
@ -66,7 +68,7 @@ class PiCreatureScene(Scene):
def get_on_screen_pi_creatures(self): def get_on_screen_pi_creatures(self):
mobjects = self.get_mobjects() mobjects = self.get_mobjects()
return VGroup(*filter( return VGroup(*filter(
lambda pi : pi in mobjects, lambda pi: pi in mobjects,
self.get_pi_creatures() self.get_pi_creatures()
)) ))
@ -80,7 +82,7 @@ class PiCreatureScene(Scene):
bubble_class = kwargs.pop("bubble_class", SpeechBubble) bubble_class = kwargs.pop("bubble_class", SpeechBubble)
target_mode = kwargs.pop( target_mode = kwargs.pop(
"target_mode", "target_mode",
"thinking" if bubble_class is ThoughtBubble else "speaking" "thinking" if bubble_class is ThoughtBubble else "speaking"
) )
bubble_kwargs = kwargs.pop("bubble_kwargs", {}) bubble_kwargs = kwargs.pop("bubble_kwargs", {})
@ -91,10 +93,11 @@ class PiCreatureScene(Scene):
on_screen_mobjects = self.camera.extract_mobject_family_members( on_screen_mobjects = self.camera.extract_mobject_family_members(
self.get_mobjects() self.get_mobjects()
) )
def has_bubble(pi): def has_bubble(pi):
return hasattr(pi, "bubble") and \ return hasattr(pi, "bubble") and \
pi.bubble is not None and \ pi.bubble is not None and \
pi.bubble in on_screen_mobjects pi.bubble in on_screen_mobjects
pi_creatures_with_bubbles = filter(has_bubble, self.get_pi_creatures()) pi_creatures_with_bubbles = filter(has_bubble, self.get_pi_creatures())
if pi_creature in pi_creatures_with_bubbles: if pi_creature in pi_creatures_with_bubbles:
@ -102,7 +105,7 @@ class PiCreatureScene(Scene):
old_bubble = pi_creature.bubble old_bubble = pi_creature.bubble
bubble = pi_creature.get_bubble( bubble = pi_creature.get_bubble(
*content, *content,
bubble_class = bubble_class, bubble_class=bubble_class,
**bubble_kwargs **bubble_kwargs
) )
anims += [ anims += [
@ -114,9 +117,9 @@ class PiCreatureScene(Scene):
anims.append(PiCreatureBubbleIntroduction( anims.append(PiCreatureBubbleIntroduction(
pi_creature, pi_creature,
*content, *content,
bubble_class = bubble_class, bubble_class=bubble_class,
bubble_kwargs = bubble_kwargs, bubble_kwargs=bubble_kwargs,
target_mode = target_mode, target_mode=target_mode,
**kwargs **kwargs
)) ))
anims += [ anims += [
@ -130,26 +133,28 @@ class PiCreatureScene(Scene):
def pi_creature_says(self, *args, **kwargs): def pi_creature_says(self, *args, **kwargs):
self.introduce_bubble( self.introduce_bubble(
*args, *args,
bubble_class = SpeechBubble, bubble_class=SpeechBubble,
**kwargs **kwargs
) )
def pi_creature_thinks(self, *args, **kwargs): def pi_creature_thinks(self, *args, **kwargs):
self.introduce_bubble( self.introduce_bubble(
*args, *args,
bubble_class = ThoughtBubble, bubble_class=ThoughtBubble,
**kwargs **kwargs
) )
def say(self, *content, **kwargs): def say(self, *content, **kwargs):
self.pi_creature_says(self.get_primary_pi_creature(), *content, **kwargs) self.pi_creature_says(
self.get_primary_pi_creature(), *content, **kwargs)
def think(self, *content, **kwargs): def think(self, *content, **kwargs):
self.pi_creature_thinks(self.get_primary_pi_creature(), *content, **kwargs) self.pi_creature_thinks(
self.get_primary_pi_creature(), *content, **kwargs)
def compile_play_args_to_animation_list(self, *args): def compile_play_args_to_animation_list(self, *args):
""" """
Add animations so that all pi creatures look at the Add animations so that all pi creatures look at the
first mobject being animated with each .play call first mobject being animated with each .play call
""" """
animations = Scene.compile_play_args_to_animation_list(self, *args) animations = Scene.compile_play_args_to_animation_list(self, *args)
@ -157,13 +162,13 @@ class PiCreatureScene(Scene):
return animations return animations
non_pi_creature_anims = filter( non_pi_creature_anims = filter(
lambda anim : anim.mobject not in self.get_pi_creatures(), lambda anim: anim.mobject not in self.get_pi_creatures(),
animations animations
) )
if len(non_pi_creature_anims) == 0: if len(non_pi_creature_anims) == 0:
return animations return animations
first_anim = non_pi_creature_anims[0] first_anim = non_pi_creature_anims[0]
#Look at ending state # Look at ending state
first_anim.update(1) first_anim.update(1)
point_of_interest = first_anim.mobject.get_center() point_of_interest = first_anim.mobject.get_center()
first_anim.update(0) first_anim.update(0)
@ -174,7 +179,7 @@ class PiCreatureScene(Scene):
if pi_creature in first_anim.mobject.submobject_family(): if pi_creature in first_anim.mobject.submobject_family():
continue continue
anims_with_pi_creature = filter( anims_with_pi_creature = filter(
lambda anim : pi_creature in anim.mobject.submobject_family(), lambda anim: pi_creature in anim.mobject.submobject_family(),
animations animations
) )
for anim in anims_with_pi_creature: for anim in anims_with_pi_creature:
@ -193,7 +198,7 @@ class PiCreatureScene(Scene):
def blink(self): def blink(self):
self.play(Blink(random.choice(self.get_on_screen_pi_creatures()))) self.play(Blink(random.choice(self.get_on_screen_pi_creatures())))
def joint_blink(self, pi_creatures = None, shuffle = True, **kwargs): def joint_blink(self, pi_creatures=None, shuffle=True, **kwargs):
if pi_creatures is None: if pi_creatures is None:
pi_creatures = self.get_on_screen_pi_creatures() pi_creatures = self.get_on_screen_pi_creatures()
creatures_list = list(pi_creatures) creatures_list = list(pi_creatures)
@ -202,25 +207,25 @@ class PiCreatureScene(Scene):
def get_rate_func(pi): def get_rate_func(pi):
index = creatures_list.index(pi) index = creatures_list.index(pi)
proportion = float(index)/len(creatures_list) proportion = float(index) / len(creatures_list)
start_time = 0.8*proportion start_time = 0.8 * proportion
return squish_rate_func( return squish_rate_func(
there_and_back, there_and_back,
start_time, start_time + 0.2 start_time, start_time + 0.2
) )
self.play(*[ self.play(*[
Blink(pi, rate_func = get_rate_func(pi), **kwargs) Blink(pi, rate_func=get_rate_func(pi), **kwargs)
for pi in creatures_list for pi in creatures_list
]) ])
return self return self
def wait(self, time = 1, blink = True): def wait(self, time=1, blink=True):
while time >= 1: while time >= 1:
time_to_blink = self.total_wait_time%self.seconds_to_blink == 0 time_to_blink = self.total_wait_time % self.seconds_to_blink == 0
if blink and self.any_pi_creatures_on_screen() and time_to_blink: if blink and self.any_pi_creatures_on_screen() and time_to_blink:
self.blink() self.blink()
self.num_plays -= 1 #This shouldn't count as an animation self.num_plays -= 1 # This shouldn't count as an animation
else: else:
self.non_blink_wait() self.non_blink_wait()
time -= 1 time -= 1
@ -229,14 +234,14 @@ class PiCreatureScene(Scene):
self.non_blink_wait(time) self.non_blink_wait(time)
return self return self
def non_blink_wait(self, time = 1): def non_blink_wait(self, time=1):
Scene.wait(self, time) Scene.wait(self, time)
return self return self
def change_mode(self, mode): def change_mode(self, mode):
self.play(self.get_primary_pi_creature().change_mode, mode) self.play(self.get_primary_pi_creature().change_mode, mode)
def look_at(self, thing_to_look_at, pi_creatures = None): def look_at(self, thing_to_look_at, pi_creatures=None):
if pi_creatures is None: if pi_creatures is None:
pi_creatures = self.get_pi_creatures() pi_creatures = self.get_pi_creatures()
self.play(*it.chain(*[ self.play(*it.chain(*[
@ -244,30 +249,33 @@ class PiCreatureScene(Scene):
for pi in pi_creatures for pi in pi_creatures
])) ]))
class TeacherStudentsScene(PiCreatureScene): class TeacherStudentsScene(PiCreatureScene):
CONFIG = { CONFIG = {
"student_colors" : [BLUE_D, BLUE_E, BLUE_C], "student_colors": [BLUE_D, BLUE_E, BLUE_C],
"student_scale_factor" : 0.8, "student_scale_factor": 0.8,
"seconds_to_blink" : 2, "seconds_to_blink": 2,
"screen_height" : 3, "screen_height": 3,
} }
def setup(self): def setup(self):
PiCreatureScene.setup(self) PiCreatureScene.setup(self)
self.screen = ScreenRectangle(height = self.screen_height) self.screen = ScreenRectangle(height=self.screen_height)
self.screen.to_corner(UP+LEFT) self.screen.to_corner(UP + LEFT)
self.hold_up_spot = self.teacher.get_corner(UP+LEFT) + MED_LARGE_BUFF*UP self.hold_up_spot = self.teacher.get_corner(
UP + LEFT) + MED_LARGE_BUFF * UP
def create_pi_creatures(self): def create_pi_creatures(self):
self.teacher = Mortimer() self.teacher = Mortimer()
self.teacher.to_corner(DOWN + RIGHT) self.teacher.to_corner(DOWN + RIGHT)
self.teacher.look(DOWN+LEFT) self.teacher.look(DOWN + LEFT)
self.students = VGroup(*[ self.students = VGroup(*[
Randolph(color = c) Randolph(color=c)
for c in self.student_colors for c in self.student_colors
]) ])
self.students.arrange_submobjects(RIGHT) self.students.arrange_submobjects(RIGHT)
self.students.scale(self.student_scale_factor) self.students.scale(self.student_scale_factor)
self.students.to_corner(DOWN+LEFT) self.students.to_corner(DOWN + LEFT)
self.teacher.look_at(self.students[-1].eyes) self.teacher.look_at(self.students[-1].eyes)
for student in self.students: for student in self.students:
student.look_at(self.teacher.eyes) student.look_at(self.teacher.eyes)
@ -288,8 +296,8 @@ class TeacherStudentsScene(PiCreatureScene):
def student_says(self, *content, **kwargs): def student_says(self, *content, **kwargs):
if "target_mode" not in kwargs: if "target_mode" not in kwargs:
target_mode = random.choice([ target_mode = random.choice([
"raise_right_hand", "raise_right_hand",
"raise_left_hand", "raise_left_hand",
]) ])
kwargs["target_mode"] = target_mode kwargs["target_mode"] = target_mode
student = self.get_students()[kwargs.get("student_index", 1)] student = self.get_students()[kwargs.get("student_index", 1)]
@ -307,7 +315,7 @@ class TeacherStudentsScene(PiCreatureScene):
return self.pi_creature_thinks(student, *content, **kwargs) return self.pi_creature_thinks(student, *content, **kwargs)
def change_all_student_modes(self, mode, **kwargs): def change_all_student_modes(self, mode, **kwargs):
self.change_student_modes(*[mode]*len(self.students), **kwargs) self.change_student_modes(*[mode] * len(self.students), **kwargs)
def change_student_modes(self, *modes, **kwargs): def change_student_modes(self, *modes, **kwargs):
added_anims = kwargs.pop("added_anims", []) added_anims = kwargs.pop("added_anims", [])
@ -326,12 +334,12 @@ class TeacherStudentsScene(PiCreatureScene):
pi.look_at(kwargs["look_at_arg"]) pi.look_at(kwargs["look_at_arg"])
submobject_mode = kwargs.get("submobject_mode", "lagged_start") submobject_mode = kwargs.get("submobject_mode", "lagged_start")
return Transform( return Transform(
start, target, start, target,
submobject_mode = submobject_mode, submobject_mode=submobject_mode,
run_time = 2 run_time=2
) )
def zoom_in_on_thought_bubble(self, bubble = None, radius = FRAME_Y_RADIUS+FRAME_X_RADIUS): def zoom_in_on_thought_bubble(self, bubble=None, radius=FRAME_Y_RADIUS + FRAME_X_RADIUS):
if bubble is None: if bubble is None:
for pi in self.get_pi_creatures(): for pi in self.get_pi_creatures():
if hasattr(pi, "bubble") and isinstance(pi.bubble, ThoughtBubble): if hasattr(pi, "bubble") and isinstance(pi.bubble, ThoughtBubble):
@ -340,15 +348,16 @@ class TeacherStudentsScene(PiCreatureScene):
if bubble is None: if bubble is None:
raise Exception("No pi creatures have a thought bubble") raise Exception("No pi creatures have a thought bubble")
vect = -bubble.get_bubble_center() vect = -bubble.get_bubble_center()
def func(point): def func(point):
centered = point+vect centered = point + vect
return radius*centered/np.linalg.norm(centered) return radius * centered / np.linalg.norm(centered)
self.play(*[ self.play(*[
ApplyPointwiseFunction(func, mob) ApplyPointwiseFunction(func, mob)
for mob in self.get_mobjects() for mob in self.get_mobjects()
]) ])
def teacher_holds_up(self, mobject, target_mode = "raise_right_hand", **kwargs): def teacher_holds_up(self, mobject, target_mode="raise_right_hand", **kwargs):
mobject.move_to(self.hold_up_spot, DOWN) mobject.move_to(self.hold_up_spot, DOWN)
mobject.shift_onto_screen() mobject.shift_onto_screen()
mobject_copy = mobject.copy() mobject_copy = mobject.copy()
@ -358,5 +367,3 @@ class TeacherStudentsScene(PiCreatureScene):
ReplacementTransform(mobject_copy, mobject), ReplacementTransform(mobject_copy, mobject),
self.teacher.change, target_mode, self.teacher.change, target_mode,
) )

View file

@ -12,12 +12,13 @@ from constants import *
VECTOR_LABEL_SCALE_FACTOR = 0.8 VECTOR_LABEL_SCALE_FACTOR = 0.8
def matrix_to_tex_string(matrix): def matrix_to_tex_string(matrix):
matrix = np.array(matrix).astype("string") matrix = np.array(matrix).astype("string")
if matrix.ndim == 1: if matrix.ndim == 1:
matrix = matrix.reshape((matrix.size, 1)) matrix = matrix.reshape((matrix.size, 1))
n_rows, n_cols = matrix.shape n_rows, n_cols = matrix.shape
prefix = "\\left[ \\begin{array}{%s}"%("c"*n_cols) prefix = "\\left[ \\begin{array}{%s}" % ("c" * n_cols)
suffix = "\\end{array} \\right]" suffix = "\\end{array} \\right]"
rows = [ rows = [
" & ".join(row) " & ".join(row)
@ -25,39 +26,43 @@ def matrix_to_tex_string(matrix):
] ]
return prefix + " \\\\ ".join(rows) + suffix return prefix + " \\\\ ".join(rows) + suffix
def matrix_to_mobject(matrix): def matrix_to_mobject(matrix):
return TexMobject(matrix_to_tex_string(matrix)) return TexMobject(matrix_to_tex_string(matrix))
def vector_coordinate_label(vector_mob, integer_labels = True,
n_dim = 2, color = WHITE): def vector_coordinate_label(vector_mob, integer_labels=True,
n_dim=2, color=WHITE):
vect = np.array(vector_mob.get_end()) vect = np.array(vector_mob.get_end())
if integer_labels: if integer_labels:
vect = np.round(vect).astype(int) vect = np.round(vect).astype(int)
vect = vect[:n_dim] vect = vect[:n_dim]
vect = vect.reshape((n_dim, 1)) vect = vect.reshape((n_dim, 1))
label = Matrix(vect, add_background_rectangles = True) label = Matrix(vect, add_background_rectangles=True)
label.scale(VECTOR_LABEL_SCALE_FACTOR) label.scale(VECTOR_LABEL_SCALE_FACTOR)
shift_dir = np.array(vector_mob.get_end()) shift_dir = np.array(vector_mob.get_end())
if shift_dir[0] >= 0: #Pointing right if shift_dir[0] >= 0: # Pointing right
shift_dir -= label.get_left() + DEFAULT_MOBJECT_TO_MOBJECT_BUFFER*LEFT shift_dir -= label.get_left() + DEFAULT_MOBJECT_TO_MOBJECT_BUFFER * LEFT
else: #Pointing left else: # Pointing left
shift_dir -= label.get_right() + DEFAULT_MOBJECT_TO_MOBJECT_BUFFER*RIGHT shift_dir -= label.get_right() + DEFAULT_MOBJECT_TO_MOBJECT_BUFFER * RIGHT
label.shift(shift_dir) label.shift(shift_dir)
label.set_color(color) label.set_color(color)
label.rect = BackgroundRectangle(label) label.rect = BackgroundRectangle(label)
label.add_to_back(label.rect) label.add_to_back(label.rect)
return label return label
class Matrix(VMobject): class Matrix(VMobject):
CONFIG = { CONFIG = {
"v_buff" : 0.5, "v_buff": 0.5,
"h_buff" : 1, "h_buff": 1,
"add_background_rectangles" : False "add_background_rectangles": False
} }
def __init__(self, matrix, **kwargs): def __init__(self, matrix, **kwargs):
""" """
Matrix can either either include numbres, tex_strings, Matrix can either either include numbres, tex_strings,
or mobjects or mobjects
""" """
VMobject.__init__(self, **kwargs) VMobject.__init__(self, **kwargs)
@ -89,9 +94,9 @@ class Matrix(VMobject):
if i == 0 and j == 0: if i == 0 and j == 0:
continue continue
elif i == 0: elif i == 0:
mob.next_to(matrix[i][j-1], RIGHT, self.h_buff) mob.next_to(matrix[i][j - 1], RIGHT, self.h_buff)
else: else:
mob.next_to(matrix[i-1][j], DOWN, self.v_buff) mob.next_to(matrix[i - 1][j], DOWN, self.v_buff)
return self return self
def add_brackets(self): def add_brackets(self):
@ -107,7 +112,7 @@ class Matrix(VMobject):
def set_color_columns(self, *colors): def set_color_columns(self, *colors):
for i, color in enumerate(colors): for i, color in enumerate(colors):
VGroup(*self.mob_matrix[:,i]).set_color(color) VGroup(*self.mob_matrix[:, i]).set_color(color)
return self return self
def add_background_to_entries(self): def add_background_to_entries(self):
@ -123,15 +128,3 @@ class Matrix(VMobject):
def get_brackets(self): def get_brackets(self):
return self.brackets return self.brackets

View file

@ -1,3 +1,4 @@
argparse==1.4.0
colour==0.1.2 colour==0.1.2
numpy==1.11.0 numpy==1.11.0
Pillow==3.4.2 Pillow==3.4.2

View file

@ -36,25 +36,27 @@ X_COLOR = GREEN_C
Y_COLOR = RED_C Y_COLOR = RED_C
Z_COLOR = BLUE_D Z_COLOR = BLUE_D
class VectorScene(Scene): class VectorScene(Scene):
CONFIG = { CONFIG = {
"basis_vector_stroke_width" : 6 "basis_vector_stroke_width": 6
} }
def add_plane(self, animate = False, **kwargs):
def add_plane(self, animate=False, **kwargs):
plane = NumberPlane(**kwargs) plane = NumberPlane(**kwargs)
if animate: if animate:
self.play(ShowCreation(plane, submobject_mode = "lagged_start")) self.play(ShowCreation(plane, submobject_mode="lagged_start"))
self.add(plane) self.add(plane)
return plane return plane
def add_axes(self, animate = False, color = WHITE, **kwargs): def add_axes(self, animate=False, color=WHITE, **kwargs):
axes = Axes(color = color, tick_frequency = 1) axes = Axes(color=color, tick_frequency=1)
if animate: if animate:
self.play(ShowCreation(axes, submobject_mode = "one_at_a_time")) self.play(ShowCreation(axes, submobject_mode="one_at_a_time"))
self.add(axes) self.add(axes)
return axes return axes
def lock_in_faded_grid(self, dimness = 0.7, axes_dimness = 0.5): def lock_in_faded_grid(self, dimness=0.7, axes_dimness=0.5):
plane = self.add_plane() plane = self.add_plane()
axes = plane.get_axes() axes = plane.get_axes()
plane.fade(dimness) plane.fade(dimness)
@ -63,9 +65,9 @@ class VectorScene(Scene):
self.add(axes) self.add(axes)
self.freeze_background() self.freeze_background()
def add_vector(self, vector, color = YELLOW, animate = True, **kwargs): def add_vector(self, vector, color=YELLOW, animate=True, **kwargs):
if not isinstance(vector, Arrow): if not isinstance(vector, Arrow):
vector = Vector(vector, color = color, **kwargs) vector = Vector(vector, color=color, **kwargs)
if animate: if animate:
self.play(ShowCreation(vector)) self.play(ShowCreation(vector))
self.add(vector) self.add(vector)
@ -76,40 +78,41 @@ class VectorScene(Scene):
self.play(Write(coords)) self.play(Write(coords))
return coords return coords
def get_basis_vectors(self): def get_basis_vectors(self, i_hat_color=X_COLOR, j_hat_color=Y_COLOR):
return [ return VGroup(*[
Vector( Vector(
vect, color = color, vect,
stroke_width = self.basis_vector_stroke_width color=color,
stroke_width=self.basis_vector_stroke_width
) )
for vect, color in [ for vect, color in [
([1, 0], X_COLOR), ([1, 0], i_hat_color),
([0, 1], Y_COLOR) ([0, 1], j_hat_color)
] ]
] ])
def get_basis_vector_labels(self, **kwargs): def get_basis_vector_labels(self, **kwargs):
i_hat, j_hat = self.get_basis_vectors() i_hat, j_hat = self.get_basis_vectors()
return VGroup(*[ return VGroup(*[
self.get_vector_label( self.get_vector_label(
vect, label, color = color, vect, label, color=color,
label_scale_factor = 1, label_scale_factor=1,
**kwargs **kwargs
) )
for vect, label , color in [ for vect, label, color in [
(i_hat, "\\hat{\\imath}", X_COLOR), (i_hat, "\\hat{\\imath}", X_COLOR),
(j_hat, "\\hat{\\jmath}", Y_COLOR), (j_hat, "\\hat{\\jmath}", Y_COLOR),
] ]
]) ])
def get_vector_label(self, vector, label, def get_vector_label(self, vector, label,
direction = "left", direction="left",
rotate = False, rotate=False,
color = None, color=None,
label_scale_factor = VECTOR_LABEL_SCALE_FACTOR): label_scale_factor=VECTOR_LABEL_SCALE_FACTOR):
if not isinstance(label, TexMobject): if not isinstance(label, TexMobject):
if len(label) == 1: if len(label) == 1:
label = "\\vec{\\textbf{%s}}"%label label = "\\vec{\\textbf{%s}}" % label
label = TexMobject(label) label = TexMobject(label)
if color is None: if color is None:
color = vector.get_color() color = vector.get_color()
@ -119,53 +122,53 @@ class VectorScene(Scene):
angle = vector.get_angle() angle = vector.get_angle()
if not rotate: if not rotate:
label.rotate(-angle, about_point = ORIGIN) label.rotate(-angle, about_point=ORIGIN)
if direction is "left": if direction is "left":
label.shift(-label.get_bottom() + 0.1*UP) label.shift(-label.get_bottom() + 0.1 * UP)
else: else:
label.shift(-label.get_top() + 0.1*DOWN) label.shift(-label.get_top() + 0.1 * DOWN)
label.rotate(angle, about_point = ORIGIN) label.rotate(angle, about_point=ORIGIN)
label.shift((vector.get_end() - vector.get_start())/2) label.shift((vector.get_end() - vector.get_start()) / 2)
return label return label
def label_vector(self, vector, label, animate = True, **kwargs): def label_vector(self, vector, label, animate=True, **kwargs):
label = self.get_vector_label(vector, label, **kwargs) label = self.get_vector_label(vector, label, **kwargs)
if animate: if animate:
self.play(Write(label, run_time = 1)) self.play(Write(label, run_time=1))
self.add(label) self.add(label)
return label return label
def position_x_coordinate(self, x_coord, x_line, vector): def position_x_coordinate(self, x_coord, x_line, vector):
x_coord.next_to(x_line, -np.sign(vector[1])*UP) x_coord.next_to(x_line, -np.sign(vector[1]) * UP)
x_coord.set_color(X_COLOR) x_coord.set_color(X_COLOR)
return x_coord return x_coord
def position_y_coordinate(self, y_coord, y_line, vector): def position_y_coordinate(self, y_coord, y_line, vector):
y_coord.next_to(y_line, np.sign(vector[0])*RIGHT) y_coord.next_to(y_line, np.sign(vector[0]) * RIGHT)
y_coord.set_color(Y_COLOR) y_coord.set_color(Y_COLOR)
return y_coord return y_coord
def coords_to_vector(self, vector, coords_start = 2*RIGHT+2*UP, clean_up = True): def coords_to_vector(self, vector, coords_start=2 * RIGHT + 2 * UP, clean_up=True):
starting_mobjects = list(self.mobjects) starting_mobjects = list(self.mobjects)
array = Matrix(vector) array = Matrix(vector)
array.shift(coords_start) array.shift(coords_start)
arrow = Vector(vector) arrow = Vector(vector)
x_line = Line(ORIGIN, vector[0]*RIGHT) x_line = Line(ORIGIN, vector[0] * RIGHT)
y_line = Line(x_line.get_end(), arrow.get_end()) y_line = Line(x_line.get_end(), arrow.get_end())
x_line.set_color(X_COLOR) x_line.set_color(X_COLOR)
y_line.set_color(Y_COLOR) y_line.set_color(Y_COLOR)
x_coord, y_coord = array.get_mob_matrix().flatten() x_coord, y_coord = array.get_mob_matrix().flatten()
self.play(Write(array, run_time = 1)) self.play(Write(array, run_time=1))
self.wait() self.wait()
self.play(ApplyFunction( self.play(ApplyFunction(
lambda x : self.position_x_coordinate(x, x_line, vector), lambda x: self.position_x_coordinate(x, x_line, vector),
x_coord x_coord
)) ))
self.play(ShowCreation(x_line)) self.play(ShowCreation(x_line))
self.play( self.play(
ApplyFunction( ApplyFunction(
lambda y : self.position_y_coordinate(y, y_line, vector), lambda y: self.position_y_coordinate(y, y_line, vector),
y_coord y_coord
), ),
FadeOut(array.get_brackets()) FadeOut(array.get_brackets())
@ -178,7 +181,7 @@ class VectorScene(Scene):
self.clear() self.clear()
self.add(*starting_mobjects) self.add(*starting_mobjects)
def vector_to_coords(self, vector, integer_labels = True, clean_up = True): def vector_to_coords(self, vector, integer_labels=True, clean_up=True):
starting_mobjects = list(self.mobjects) starting_mobjects = list(self.mobjects)
show_creation = False show_creation = False
if isinstance(vector, Arrow): if isinstance(vector, Arrow):
@ -187,8 +190,8 @@ class VectorScene(Scene):
else: else:
arrow = Vector(vector) arrow = Vector(vector)
show_creation = True show_creation = True
array = vector_coordinate_label(arrow, integer_labels = integer_labels) array = vector_coordinate_label(arrow, integer_labels=integer_labels)
x_line = Line(ORIGIN, vector[0]*RIGHT) x_line = Line(ORIGIN, vector[0] * RIGHT)
y_line = Line(x_line.get_end(), arrow.get_end()) y_line = Line(x_line.get_end(), arrow.get_end())
x_line.set_color(X_COLOR) x_line.set_color(X_COLOR)
y_line.set_color(Y_COLOR) y_line.set_color(Y_COLOR)
@ -206,18 +209,18 @@ class VectorScene(Scene):
self.play( self.play(
ShowCreation(x_line), ShowCreation(x_line),
Write(x_coord_start), Write(x_coord_start),
run_time = 1 run_time=1
) )
self.play( self.play(
ShowCreation(y_line), ShowCreation(y_line),
Write(y_coord_start), Write(y_coord_start),
run_time = 1 run_time=1
) )
self.wait() self.wait()
self.play( self.play(
Transform(x_coord_start, x_coord, submobject_mode = "all_at_once"), Transform(x_coord_start, x_coord, submobject_mode="all_at_once"),
Transform(y_coord_start, y_coord, submobject_mode = "all_at_once"), Transform(y_coord_start, y_coord, submobject_mode="all_at_once"),
Write(brackets, run_time = 1), Write(brackets, run_time=1),
) )
self.wait() self.wait()
@ -236,53 +239,56 @@ class VectorScene(Scene):
x_max = int(FRAME_X_RADIUS + abs(vector[0])) x_max = int(FRAME_X_RADIUS + abs(vector[0]))
y_max = int(FRAME_Y_RADIUS + abs(vector[1])) y_max = int(FRAME_Y_RADIUS + abs(vector[1]))
dots = VMobject(*[ dots = VMobject(*[
Dot(x*RIGHT + y*UP) Dot(x * RIGHT + y * UP)
for x in range(-x_max, x_max) for x in range(-x_max, x_max)
for y in range(-y_max, y_max) for y in range(-y_max, y_max)
]) ])
dots.set_fill(BLACK, opacity = 0) dots.set_fill(BLACK, opacity=0)
dots_halfway = dots.copy().shift(vector/2).set_fill(WHITE, 1) dots_halfway = dots.copy().shift(vector / 2).set_fill(WHITE, 1)
dots_end = dots.copy().shift(vector) dots_end = dots.copy().shift(vector)
self.play(Transform( self.play(Transform(
dots, dots_halfway, rate_func = rush_into dots, dots_halfway, rate_func=rush_into
)) ))
self.play(Transform( self.play(Transform(
dots, dots_end, rate_func = rush_from dots, dots_end, rate_func=rush_from
)) ))
self.remove(dots) self.remove(dots)
class LinearTransformationScene(VectorScene): class LinearTransformationScene(VectorScene):
CONFIG = { CONFIG = {
"include_background_plane" : True, "include_background_plane": True,
"include_foreground_plane" : True, "include_foreground_plane": True,
"foreground_plane_kwargs" : { "foreground_plane_kwargs": {
"x_radius" : FRAME_WIDTH, "x_radius": FRAME_WIDTH,
"y_radius" : FRAME_HEIGHT, "y_radius": FRAME_HEIGHT,
"secondary_line_ratio" : 0 "secondary_line_ratio": 0
}, },
"background_plane_kwargs" : { "background_plane_kwargs": {
"color" : GREY, "color": GREY,
"secondary_color" : DARK_GREY, "secondary_color": DARK_GREY,
"axes_color" : GREY, "axes_color": GREY,
"stroke_width" : 2, "stroke_width": 2,
}, },
"show_coordinates" : False, "show_coordinates": False,
"show_basis_vectors" : True, "show_basis_vectors": True,
"i_hat_color" : X_COLOR, "basis_vector_stroke_width": 6,
"j_hat_color" : Y_COLOR, "i_hat_color": X_COLOR,
"leave_ghost_vectors" : False, "j_hat_color": Y_COLOR,
"t_matrix" : [[3, 0], [1, 2]], "leave_ghost_vectors": False,
"t_matrix": [[3, 0], [1, 2]],
} }
def setup(self): def setup(self):
if hasattr(self, "has_already_setup"): if hasattr(self, "has_already_setup"):
return return
self.has_already_setup = True self.has_already_setup = True
##^This is to not break all the old Scenes # ^This is to not break all the old Scenes
self.background_mobjects = [] self.background_mobjects = []
self.foreground_mobjects = [] self.foreground_mobjects = []
self.transformable_mobjects = [] self.transformable_mobjects = []
self.moving_vectors = [] self.moving_vectors = []
self.transformable_labels = [] self.transformable_labels = []
self.moving_mobjects = [] self.moving_mobjects = []
@ -293,21 +299,18 @@ class LinearTransformationScene(VectorScene):
if self.show_coordinates: if self.show_coordinates:
self.background_plane.add_coordinates() self.background_plane.add_coordinates()
if self.include_background_plane: if self.include_background_plane:
self.add_background_mobject(self.background_plane) self.add_background_mobject(self.background_plane)
if self.include_foreground_plane: if self.include_foreground_plane:
self.plane = NumberPlane(**self.foreground_plane_kwargs) self.plane = NumberPlane(**self.foreground_plane_kwargs)
self.add_transformable_mobject(self.plane) self.add_transformable_mobject(self.plane)
if self.show_basis_vectors: if self.show_basis_vectors:
self.i_hat, self.j_hat = [ self.basis_vectors = self.get_basis_vectors(
self.add_vector( i_hat_color=self.i_hat_color,
coords, color, animate = False, stroke_width = 6 j_hat_color=self.j_hat_color,
) )
for coords, color in [ self.moving_vectors += list(self.basis_vectors)
((1, 0), self.i_hat_color), self.i_hat, self.j_hat = self.basis_vectors
((0, 1), self.j_hat_color),
]
]
def add_special_mobjects(self, mob_list, *mobs_to_add): def add_special_mobjects(self, mob_list, *mobs_to_add):
for mobject in mobs_to_add: for mobject in mobs_to_add:
@ -320,17 +323,17 @@ class LinearTransformationScene(VectorScene):
def add_foreground_mobject(self, *mobjects): def add_foreground_mobject(self, *mobjects):
self.add_special_mobjects(self.foreground_mobjects, *mobjects) self.add_special_mobjects(self.foreground_mobjects, *mobjects)
def add_transformable_mobject(self, *mobjects): def add_transformable_mobject(self, *mobjects):
self.add_special_mobjects(self.transformable_mobjects, *mobjects) self.add_special_mobjects(self.transformable_mobjects, *mobjects)
def add_moving_mobject(self, mobject, target_mobject = None): def add_moving_mobject(self, mobject, target_mobject=None):
mobject.target = target_mobject mobject.target = target_mobject
self.add_special_mobjects(self.moving_mobjects, mobject) self.add_special_mobjects(self.moving_mobjects, mobject)
def add_unit_square(self, color = YELLOW, opacity = 0.3, animate = False): def add_unit_square(self, color=YELLOW, opacity=0.3, animate=False):
square = Square(color = color, side_length = 1) square = Square(color=color, side_length=1)
square.shift(-square.get_corner(DOWN+LEFT)) square.shift(-square.get_corner(DOWN + LEFT))
if animate: if animate:
added_anims = map(Animation, self.moving_vectors) added_anims = map(Animation, self.moving_vectors)
self.play(ShowCreation(square), *added_anims) self.play(ShowCreation(square), *added_anims)
@ -338,13 +341,13 @@ class LinearTransformationScene(VectorScene):
else: else:
square.set_fill(color, opacity) square.set_fill(color, opacity)
self.add_transformable_mobject(square) self.add_transformable_mobject(square)
self.bring_to_front(*self.moving_vectors) self.bring_to_front(*self.moving_vectors)
self.square = square self.square = square
return self return self
def add_vector(self, vector, color = YELLOW, **kwargs): def add_vector(self, vector, color=YELLOW, **kwargs):
vector = VectorScene.add_vector( vector = VectorScene.add_vector(
self, vector, color = color, **kwargs self, vector, color=color, **kwargs
) )
self.moving_vectors.append(vector) self.moving_vectors.append(vector)
return vector return vector
@ -354,12 +357,12 @@ class LinearTransformationScene(VectorScene):
self.add_foreground_mobject(coords) self.add_foreground_mobject(coords)
return coords return coords
def add_transformable_label(self, vector, label, new_label = None, **kwargs): def add_transformable_label(self, vector, label, new_label=None, **kwargs):
label_mob = self.label_vector(vector, label, **kwargs) label_mob = self.label_vector(vector, label, **kwargs)
if new_label: if new_label:
label_mob.target_text = new_label label_mob.target_text = new_label
else: else:
label_mob.target_text = "L(%s)"%label_mob.get_tex_string() label_mob.target_text = "L(%s)" % label_mob.get_tex_string()
label_mob.vector = vector label_mob.vector = vector
label_mob.kwargs = kwargs label_mob.kwargs = kwargs
if "animate" in label_mob.kwargs: if "animate" in label_mob.kwargs:
@ -367,7 +370,7 @@ class LinearTransformationScene(VectorScene):
self.transformable_labels.append(label_mob) self.transformable_labels.append(label_mob)
return label_mob return label_mob
def add_title(self, title, scale_factor = 1.5, animate = False): def add_title(self, title, scale_factor=1.5, animate=False):
if not isinstance(title, Mobject): if not isinstance(title, Mobject):
title = TextMobject(title).scale(scale_factor) title = TextMobject(title).scale(scale_factor)
title.to_edge(UP) title.to_edge(UP)
@ -378,7 +381,10 @@ class LinearTransformationScene(VectorScene):
self.title = title self.title = title
return self return self
def get_matrix_transformation(self, transposed_matrix): def get_matrix_transformation(self, matrix):
return self.get_transposed_matrix_transformation(np.array(matrix).T)
def get_transposed_matrix_transformation(self, transposed_matrix):
transposed_matrix = np.array(transposed_matrix) transposed_matrix = np.array(transposed_matrix)
if transposed_matrix.shape == (2, 2): if transposed_matrix.shape == (2, 2):
new_matrix = np.identity(3) new_matrix = np.identity(3)
@ -389,11 +395,11 @@ class LinearTransformationScene(VectorScene):
return lambda point: np.dot(point, transposed_matrix) return lambda point: np.dot(point, transposed_matrix)
def get_piece_movement(self, pieces): def get_piece_movement(self, pieces):
start = VMobject(*pieces) start = VMobject(*pieces)
target = VMobject(*[mob.target for mob in pieces]) target = VMobject(*[mob.target for mob in pieces])
if self.leave_ghost_vectors: if self.leave_ghost_vectors:
self.add(start.copy().fade(0.7)) self.add(start.copy().fade(0.7))
return Transform(start, target, submobject_mode = "all_at_once") return Transform(start, target, submobject_mode="all_at_once")
def get_moving_mobject_movement(self, func): def get_moving_mobject_movement(self, func):
for m in self.moving_mobjects: for m in self.moving_mobjects:
@ -405,7 +411,7 @@ class LinearTransformationScene(VectorScene):
def get_vector_movement(self, func): def get_vector_movement(self, func):
for v in self.moving_vectors: for v in self.moving_vectors:
v.target = Vector(func(v.get_end()), color = v.get_color()) v.target = Vector(func(v.get_end()), color=v.get_color())
norm = np.linalg.norm(v.target.get_end()) norm = np.linalg.norm(v.target.get_end())
if norm < 0.1: if norm < 0.1:
v.target.get_tip().scale_in_place(norm) v.target.get_tip().scale_in_place(norm)
@ -418,12 +424,15 @@ class LinearTransformationScene(VectorScene):
) )
return self.get_piece_movement(self.transformable_labels) return self.get_piece_movement(self.transformable_labels)
def apply_matrix(self, matrix, **kwargs):
self.apply_transposed_matrix(np.array(matrix).T, **kwargs)
def apply_transposed_matrix(self, transposed_matrix, **kwargs): def apply_transposed_matrix(self, transposed_matrix, **kwargs):
func = self.get_matrix_transformation(transposed_matrix) func = self.get_transposed_matrix_transformation(transposed_matrix)
if "path_arc" not in kwargs: if "path_arc" not in kwargs:
net_rotation = np.mean([ net_rotation = np.mean([
angle_of_vector(func(RIGHT)), angle_of_vector(func(RIGHT)),
angle_of_vector(func(UP))-np.pi/2 angle_of_vector(func(UP)) - np.pi / 2
]) ])
kwargs["path_arc"] = net_rotation kwargs["path_arc"] = net_rotation
self.apply_function(func, **kwargs) self.apply_function(func, **kwargs)
@ -436,7 +445,7 @@ class LinearTransformationScene(VectorScene):
self.plane.prepare_for_nonlinear_transform() self.plane.prepare_for_nonlinear_transform()
self.apply_function(function, **kwargs) self.apply_function(function, **kwargs)
def apply_function(self, function, added_anims = [], **kwargs): def apply_function(self, function, added_anims=[], **kwargs):
if "run_time" not in kwargs: if "run_time" not in kwargs:
kwargs["run_time"] = 3 kwargs["run_time"] = 3
anims = [ anims = [
@ -451,14 +460,3 @@ class LinearTransformationScene(VectorScene):
for f_mob in self.foreground_mobjects for f_mob in self.foreground_mobjects
] + added_anims ] + added_anims
self.play(*anims, **kwargs) self.play(*anims, **kwargs)