Finished tattoo video

This commit is contained in:
Grant Sanderson
2017-01-05 11:56:52 -08:00
parent 44216ffe0d
commit 740eba856d
5 changed files with 317 additions and 43 deletions

View File

@ -283,6 +283,27 @@ class Succession(Animation):
curr_anim.update(scaled_alpha - index) curr_anim.update(scaled_alpha - index)
self.last_index = index self.last_index = index
class AnimationGroup(Animation):
def __init__(self, *sub_anims, **kwargs):
digest_config(self, kwargs, locals())
max_run_time = float(max([a.run_time for a in sub_anims]))
for anim in sub_anims:
#Use np.divide to that 1./0 = np.inf
anim.alpha_multiplier = np.divide(max_run_time, anim.run_time)
if "run_time" in kwargs:
self.run_time = kwargs.pop("run_time")
else:
self.run_time = max_run_time
everything = Mobject(*[a.mobject for a in sub_anims])
Animation.__init__(self, everything, **kwargs)
def update(self, alpha):
for anim in self.sub_anims:
sub_alpha = np.clip(alpha*anim.alpha_multiplier, 0, 1)
anim.update(sub_alpha)

293
tattoo.py
View File

@ -25,7 +25,6 @@ from mobject.svg_mobject import *
from mobject.tex_mobject import * from mobject.tex_mobject import *
class TrigRepresentationsScene(Scene): class TrigRepresentationsScene(Scene):
CONFIG = { CONFIG = {
"unit_length" : 1.5, "unit_length" : 1.5,
@ -111,6 +110,49 @@ class TrigRepresentationsScene(Scene):
end_point = (1./np.sin(self.theta_value))*self.unit_length*UP end_point = (1./np.sin(self.theta_value))*self.unit_length*UP
return Line(start_point, end_point, color = color) return Line(start_point, end_point, color = color)
class Introduce(TeacherStudentsScene):
def construct(self):
self.teacher_says(
"Something different today!",
target_mode = "hooray",
run_time = 2
)
self.change_student_modes("thinking", "happy", "sassy")
self.random_blink(2)
class ReactionsToTattoo(PiCreatureScene):
def construct(self):
modes = [
"horrified",
"hesitant",
"pondering",
"thinking",
"sassy",
]
tattoo_on_math = TextMobject("Tattoo on \\\\ math")
tattoo_on_math.to_edge(UP)
self.dither(2)
for mode in modes:
self.play(
self.pi_creature.change_mode, mode,
self.pi_creature.look, UP+RIGHT
)
self.dither(2)
self.play(
Write(tattoo_on_math),
self.pi_creature.change_mode, "hooray",
self.pi_creature.look, UP
)
self.dither()
self.change_mode("happy")
self.dither(2)
def get_pi_creature(self):
randy = Randolph()
randy.next_to(ORIGIN, DOWN)
return randy
class IntroduceCSC(TrigRepresentationsScene): class IntroduceCSC(TrigRepresentationsScene):
def construct(self): def construct(self):
self.clear() self.clear()
@ -265,19 +307,24 @@ class ExplainTrigFunctionDistances(TrigRepresentationsScene, PiCreatureScene):
cos_group, cos_group,
self.theta_group, self.theta_group,
) )
thetas = np.linspace(self.theta_value, self.alt_theta_val, 10) thetas = np.linspace(self.theta_value, self.alt_theta_val, 100)
targets = [] targets = []
for theta in list(thetas) + list(reversed(thetas)): for theta in thetas:
self.theta_value = theta self.theta_value = theta
targets.append(VGroup( targets.append(VGroup(
self.get_line_brace_text("sin"), self.get_line_brace_text("sin"),
self.get_line_brace_text("cos"), self.get_line_brace_text("cos"),
self.get_theta_group() self.get_theta_group()
)) ))
self.play(Succession(*[ self.play(Succession(
Transform(mover, target) *[
for target in targets Transform(mover, target, rate_func = None)
], run_time = 5, rate_func = smooth)) for target in targets
],
run_time = 5,
rate_func = there_and_back
))
self.theta_value = thetas[0]
self.change_mode("happy") self.change_mode("happy")
self.dither() self.dither()
@ -339,19 +386,24 @@ class ExplainTrigFunctionDistances(TrigRepresentationsScene, PiCreatureScene):
cot_group, cot_group,
self.theta_group, self.theta_group,
) )
thetas = np.linspace(self.theta_value, self.alt_theta_val, 10) thetas = np.linspace(self.theta_value, self.alt_theta_val, 100)
targets = [] targets = []
for theta in list(thetas) + list(reversed(thetas)): for theta in thetas:
self.theta_value = theta self.theta_value = theta
targets.append(VGroup( targets.append(VGroup(
self.get_line_brace_text("tan"), self.get_line_brace_text("tan"),
self.get_line_brace_text("cot"), self.get_line_brace_text("cot"),
self.get_theta_group() self.get_theta_group()
)) ))
self.play(Succession(*[ self.play(Succession(
Transform(mover, target) *[
for target in targets Transform(mover, target, rate_func = None)
], run_time = 5, rate_func = smooth)) for target in targets
],
run_time = 5,
rate_func = there_and_back
))
self.theta_value = thetas[0]
self.change_mode("happy") self.change_mode("happy")
self.dither(2) self.dither(2)
@ -414,9 +466,9 @@ class ExplainTrigFunctionDistances(TrigRepresentationsScene, PiCreatureScene):
self.theta_group, self.theta_group,
self.tangent_line, self.tangent_line,
) )
thetas = np.linspace(self.theta_value, self.alt_theta_val, 10) thetas = np.linspace(self.theta_value, self.alt_theta_val, 100)
targets = [] targets = []
for theta in list(thetas) + list(reversed(thetas)): for theta in thetas:
self.theta_value = theta self.theta_value = theta
new_sec_group = self.get_line_brace_text("sec") new_sec_group = self.get_line_brace_text("sec")
new_csc_group = self.get_line_brace_text("csc") new_csc_group = self.get_line_brace_text("csc")
@ -431,10 +483,15 @@ class ExplainTrigFunctionDistances(TrigRepresentationsScene, PiCreatureScene):
self.get_theta_group(), self.get_theta_group(),
self.get_tangent_line(), self.get_tangent_line(),
)) ))
self.play(Succession(*[ self.play(Succession(
Transform(mover, target) *[
for target in targets Transform(mover, target, rate_func = None)
], run_time = 5, rate_func = smooth)) for target in targets
],
run_time = 5,
rate_func = there_and_back
))
self.theta_value = thetas[0]
self.change_mode("confused") self.change_mode("confused")
self.dither(2) self.dither(2)
@ -482,6 +539,11 @@ class ExplainTrigFunctionDistances(TrigRepresentationsScene, PiCreatureScene):
frac_group.scale_to_fit_width(SPACE_WIDTH-1) frac_group.scale_to_fit_width(SPACE_WIDTH-1)
frac_group.next_to(ORIGIN, RIGHT).to_edge(UP) frac_group.next_to(ORIGIN, RIGHT).to_edge(UP)
question = TextMobject("Why is this $\\theta$?")
question.highlight(YELLOW)
question.to_corner(UP+RIGHT)
arrow = Arrow(question.get_bottom(), arc_theta)
one_brace, one = self.radial_line_label one_brace, one = self.radial_line_label
one.move_to(one_brace.get_center_of_mass()) one.move_to(one_brace.get_center_of_mass())
@ -499,6 +561,14 @@ class ExplainTrigFunctionDistances(TrigRepresentationsScene, PiCreatureScene):
rate_func = wiggle rate_func = wiggle
)) ))
self.dither(2) self.dither(2)
self.play(
Write(question),
ShowCreation(arrow),
self.pi_creature.change_mode, "confused"
)
self.dither(2)
self.play(*map(FadeOut, [question, arrow]))
self.play(Write(opp_over_hyp)) self.play(Write(opp_over_hyp))
self.dither() self.dither()
@ -562,7 +632,7 @@ class ExplainTrigFunctionDistances(TrigRepresentationsScene, PiCreatureScene):
self.dither() self.dither()
self.play( self.play(
dem2.move_to, frac2[2], dem2.move_to, frac2[2],
VGroup(*frac2[1:3]).highlight, BLACK VGroup(*frac2[1:3]).set_fill, BLACK, 0
) )
self.dither() self.dither()
@ -625,13 +695,21 @@ class ExplainTrigFunctionDistances(TrigRepresentationsScene, PiCreatureScene):
if (vect[1] < 0) ^ (func_name is "sec"): if (vect[1] < 0) ^ (func_name is "sec"):
vect = -vect vect = -vect
angle += np.pi angle += np.pi
brace = Brace(line, vect) brace = Brace(
Line(
line.get_length()*LEFT/2,
line.get_length()*RIGHT/2,
),
UP
)
brace.rotate(angle)
brace.shift(line.get_center())
brace.highlight(line.get_color()) brace.highlight(line.get_color())
text = TexMobject("\\%s(\\theta)"%func_name) text = TexMobject("\\%s(\\theta)"%func_name)
text.scale(0.75) text.scale(0.75)
text[-2].highlight(self.theta_color) text[-2].highlight(self.theta_color)
text.add_background_rectangle() text.add_background_rectangle()
text.next_to(brace.get_center(), vect, buff = 1.2*MED_BUFF) text.next_to(brace.get_center_of_mass(), vect, buff = 1.2*MED_BUFF)
return VGroup(line, brace, text) return VGroup(line, brace, text)
def get_tangent_line(self): def get_tangent_line(self):
@ -652,25 +730,166 @@ class RenameAllInTermsOfSine(Scene):
"\\cot(\\theta)", "\\cot(\\theta)",
] ]
shift_vals = [ shift_vals = [
2*LEFT+3*UP, 4*LEFT+3*UP,
2*LEFT+UP, 4*LEFT+UP,
2*LEFT+DOWN, 4*LEFT+DOWN,
2*RIGHT+3*UP, 4*RIGHT+3*UP,
2*RIGHT+UP, 4*RIGHT+UP,
2*RIGHT+DOWN, 4*RIGHT+DOWN,
] ]
equivs = [ equivs = [
"", "",
"\\sin(90^\\circ - \\theta)", "= \\sin(90^\\circ - \\theta)",
"\\frac{\\sin(\\theta)}{\\sin(90^\\circ - \\theta)}", "= \\frac{\\sin(\\theta)}{\\sin(90^\\circ - \\theta)}",
"\\frac{1}{\\sin(\\theta)}", "= \\frac{1}{\\sin(\\theta)}",
"\\frac{1}{\\sin(90^\\circ - \\theta)}", "= \\frac{1}{\\sin(90^\\circ - \\theta)}",
"\\frac{\\sin(90^\\circ - \\theta)}{\\sin(\\theta)}", "= \\frac{\\sin(90^\\circ - \\theta)}{\\sin(\\theta)}",
] ]
mobs = VGroup(*map(TexMobject, texs))
sin, cos, tan = mobs[:3]
sin.target_color = YELLOW
cos.target_color = GREEN
tan.target_color = RED
rhs_mobs = VGroup(*map(TexMobject, equivs))
rhs_mobs.submobjects[0] = VectorizedPoint()
for mob, shift_val in zip(mobs, shift_vals):
mob.shift(shift_val)
self.play(Write(mobs))
self.dither()
for mob, rhs_mob in zip(mobs, rhs_mobs):
rhs_mob.next_to(mob)
rhs_mob.highlight(sin.target_color)
mob.save_state()
mob.generate_target()
VGroup(mob.target, rhs_mob).move_to(mob)
sin.target.highlight(sin.target_color)
self.play(*it.chain(*[
map(MoveToTarget, mobs),
[Write(rhs_mobs)]
]))
self.dither(2)
anims = []
for mob, rhs_mob in zip(mobs, rhs_mobs)[1:3]:
anims += [
FadeOut(rhs_mob),
mob.restore,
mob.highlight, mob.target_color,
]
self.play(*anims)
self.dither()
new_rhs_mobs = [
TexMobject("=\\frac{1}{\\%s(\\theta)}"%s).highlight(color)
for s, color in [
("cos", cos.target_color),
("tan", tan.target_color),
]
]
anims = []
for mob, rhs, new_rhs in zip(mobs[-2:], rhs_mobs[-2:], new_rhs_mobs):
new_rhs.next_to(mob)
VGroup(mob.target, new_rhs).move_to(
VGroup(mob, rhs)
)
anims += [
MoveToTarget(mob),
Transform(rhs, new_rhs)
]
self.play(*anims)
self.dither(2)
class MisMatchOfCoPrefix(TeacherStudentsScene):
def construct(self):
eq1 = TexMobject(
"\\text{secant}(\\theta) = \\frac{1}{\\text{cosine}(\\theta)}"
)
eq2 = TexMobject(
"\\text{cosecant}(\\theta) = \\frac{1}{\\text{sine}(\\theta)}"
)
eq1.to_corner(UP+LEFT)
eq1.to_edge(LEFT)
eq2.next_to(eq1, DOWN, buff = LARGE_BUFF)
eqs = VGroup(eq1, eq2)
self.play(
self.get_teacher().change_mode, "speaking",
Write(eqs),
*[
ApplyMethod(pi.look_at, eqs)
for pi in self.get_students()
]
)
self.random_blink()
self.play(
VGroup(*eq1[-9:-7]).highlight, YELLOW,
VGroup(*eq2[:2]).highlight, YELLOW,
*[
ApplyMethod(pi.change_mode, "confused")
for pi in self.get_students()
]
)
self.random_blink(2)
class Credit(Scene):
def construct(self):
morty = Mortimer()
morty.next_to(ORIGIN, DOWN)
morty.to_edge(RIGHT)
headphones = Headphones(height = 1)
headphones.move_to(morty.eyes, aligned_edge = DOWN)
headphones.shift(0.1*DOWN)
url = TextMobject("www.audibletrial.com/3blue1brown")
url.scale(0.8)
url.to_corner(UP+RIGHT, buff = LARGE_BUFF)
book = ImageMobject("zen_and_motorcycles")
book.scale_to_fit_height(5)
book.to_edge(DOWN, buff = LARGE_BUFF)
border = Rectangle(color = WHITE)
border.replace(book, stretch = True)
self.play(PiCreatureSays(
morty, "Book recommendation!",
target_mode = "surprised"
))
self.play(Blink(morty))
self.play(
FadeIn(headphones),
morty.change_mode, "thinking",
FadeOut(morty.bubble),
FadeOut(morty.bubble.content),
)
self.play(Write(url))
self.play(morty.change_mode, "happy")
self.dither(2)
self.play(Blink(morty))
self.dither(2)
self.play(
morty.change_mode, "raise_right_hand",
morty.look_at, url
)
self.dither(2)
self.play(
morty.change_mode, "happy",
morty.look_at, book
)
self.play(FadeIn(book))
self.play(ShowCreation(border))
self.dither(2)
self.play(Blink(morty))
self.dither()
self.play(
morty.change_mode, "thinking",
morty.look_at, book
)
self.dither(2)
self.play(Blink(morty))
self.dither(4)
self.play(Blink(morty))

View File

@ -10,7 +10,7 @@ from topics.objects import Bubble, ThoughtBubble, SpeechBubble
from animation import Animation from animation import Animation
from animation.transform import Transform, ApplyMethod, \ from animation.transform import Transform, ApplyMethod, \
FadeOut, FadeIn, ApplyPointwiseFunction FadeOut, FadeIn, ApplyPointwiseFunction
from animation.simple_animations import Write, ShowCreation from animation.simple_animations import Write, ShowCreation, AnimationGroup
from scene import Scene from scene import Scene
@ -213,7 +213,6 @@ class BabyPiCreature(PiCreature):
self.look(looking_direction) self.look(looking_direction)
class Blink(ApplyMethod): class Blink(ApplyMethod):
CONFIG = { CONFIG = {
"rate_func" : squish_rate_func(there_and_back) "rate_func" : squish_rate_func(there_and_back)
@ -222,6 +221,39 @@ class Blink(ApplyMethod):
ApplyMethod.__init__(self, pi_creature.blink, **kwargs) ApplyMethod.__init__(self, pi_creature.blink, **kwargs)
class PiCreatureSays(AnimationGroup):
CONFIG = {
"target_mode" : "speaking",
"change_mode_kwargs" : {},
"bubble_creation_kwargs" : {},
"write_kwargs" : {},
}
def __init__(self, pi_creature, *content, **kwargs):
digest_config(self, kwargs)
bubble = pi_creature.get_bubble("speech")
bubble.write(*content)
bubble.resize_to_content()
bubble.pin_to(pi_creature)
pi_creature.bubble = bubble
AnimationGroup.__init__(
self,
ApplyMethod(
pi_creature.change_mode,
self.target_mode,
**self.change_mode_kwargs
),
ShowCreation(
bubble,
**self.bubble_creation_kwargs
),
Write(
bubble.content,
**self.write_kwargs
),
**kwargs
)
class PiCreatureScene(Scene): class PiCreatureScene(Scene):
CONFIG = { CONFIG = {

View File

@ -29,7 +29,7 @@ class ParametricFunction(VMobject):
CONFIG = { CONFIG = {
"t_min" : 0, "t_min" : 0,
"t_max" : 1, "t_max" : 1,
"epsilon" : 0.1, "num_anchor_points" : 10,
} }
def __init__(self, function, **kwargs): def __init__(self, function, **kwargs):
self.function = function self.function = function
@ -38,10 +38,10 @@ class ParametricFunction(VMobject):
def generate_points(self): def generate_points(self):
self.set_anchor_points([ self.set_anchor_points([
self.function(t) self.function(t)
for t in np.arange( for t in np.linspace(
self.t_min, self.t_min,
self.t_max+self.epsilon, self.t_max,
self.epsilon self.num_anchor_points
) )
], mode = "smooth") ], mode = "smooth")

View File

@ -122,6 +122,8 @@ class Bubble(SVGMobject):
"bubble_center_adjustment_factor" : 1./8, "bubble_center_adjustment_factor" : 1./8,
"file_name" : None, "file_name" : None,
"propogate_style_to_family" : True, "propogate_style_to_family" : True,
"fill_color" : BLACK,
"fill_opacity" : 0.8,
} }
def __init__(self, **kwargs): def __init__(self, **kwargs):
digest_config(self, kwargs, locals()) digest_config(self, kwargs, locals())