Up to physics example in eola chapter 0

This commit is contained in:
Grant Sanderson 2016-07-12 15:16:20 -07:00
parent c910b00a32
commit d35a8f76ac
9 changed files with 425 additions and 64 deletions

View file

@ -114,6 +114,17 @@ class Flash(Animation):
alpha
)
class MoveAlongPath(Animation):
def __init__(self, mobject, vmobject, **kwargs):
digest_config(self, kwargs, locals())
Animation.__init__(self, mobject, **kwargs)
def update_mobject(self, alpha):
self.mobject.shift(
self.vmobject.point_from_proportion(alpha) - \
self.mobject.get_center()
)
class Homotopy(Animation):
def __init__(self, homotopy, mobject, **kwargs):
"""

View file

@ -22,7 +22,7 @@ LOW_QUALITY_CAMERA_CONFIG = {
DEFAULT_POINT_DENSITY_2D = 25
DEFAULT_POINT_DENSITY_1D = 250
DEFAULT_POINT_THICKNESS = 3
DEFAULT_POINT_THICKNESS = 4
#TODO, Make sure these are not needed
SPACE_HEIGHT = 4.0

View file

@ -16,6 +16,7 @@ from scene import Scene
from camera import Camera
from mobject.svg_mobject import *
from mobject.tex_mobject import *
from mobject.vectorized_mobject import *
from eola.utils import *
@ -37,22 +38,20 @@ class OpeningQuote(Scene):
words.to_edge(UP)
for mob in words.submobjects[48:49+13]:
mob.highlight(GREEN)
words.show()
author = TextMobject("-Hermann Weyl")
author.highlight(YELLOW)
author.next_to(words, DOWN)
self.play(Write(words))
self.dither()
self.play(FadeIn(author))
self.play(FadeIn(words))
self.dither(3)
self.play(Write(author))
self.dither()
class AboutLinearAlgebra(Scene):
def construct(self):
self.show_dependencies()
self.linalg_is_confusing()
self.ask_questions()
self.to_thought_bubble()
def show_dependencies(self):
linalg = TextMobject("Linear Algebra")
@ -86,7 +85,7 @@ class AboutLinearAlgebra(Scene):
self.dither()
self.linalg = linalg
def linalg_is_confusing(self):
def to_thought_bubble(self):
linalg = self.linalg
all_else = list(self.mobjects)
all_else.remove(linalg)
@ -94,6 +93,7 @@ class AboutLinearAlgebra(Scene):
randy.to_corner()
bubble = randy.get_bubble(width = 10)
new_linalg = bubble.position_mobject_inside(linalg.copy())
q_marks = TextMobject("???").next_to(randy, UP)
self.play(*map(FadeOut, all_else))
self.remove(*all_else)
@ -102,17 +102,44 @@ class AboutLinearAlgebra(Scene):
Write(bubble),
FadeIn(randy)
)
self.play(ApplyMethod(randy.change_mode, "confused"))
self.dither()
self.play(Blink(randy))
self.play(FadeOut(linalg))
self.remove(linalg)
self.randy, self.bubble = randy, bubble
topics = [
self.get_matrix_multiplication(),
self.get_determinant(),
self.get_cross_product(),
self.get_eigenvalue(),
]
questions = [
self.get_matrix_multiplication_question(),
self.get_cross_product_question(),
self.get_eigen_question(),
]
for count, topic in enumerate(topics + questions):
bubble.position_mobject_inside(topic)
if count == len(topics):
self.play(FadeOut(linalg))
self.play(
ApplyMethod(randy.change_mode, "confused"),
Write(q_marks, run_time = 1)
)
linalg = VectorizedPoint(linalg.get_center())
if count > len(topics):
self.remove(linalg)
self.play(FadeIn(topic))
linalg = topic
else:
self.play(Transform(linalg, topic))
def ask_questions(self):
randy, bubble = self.randy, self.bubble
matrix_multiplication = TexMobject("""
if count %3 == 0:
self.play(Blink(randy))
self.dither()
else:
self.dither(2)
def get_matrix_multiplication(self):
return TexMobject("""
\\left[
\\begin{array}{cc}
a & b \\\\
@ -134,6 +161,44 @@ class AboutLinearAlgebra(Scene):
\\right]
""")
def get_determinant(self):
return TexMobject("""
\\text{Det}\\left(
\\begin{array}{cc}
a & b \\\\
c & d
\\end{array}
\\right)
=
ac - bc
""")
def get_cross_product(self):
return TexMobject("""
\\vec\\textbf{v} \\times \\textbf{w} =
\\text{Det}\\left(
\\begin{array}{ccc}
\\hat{\imath} & \\hat{\jmath} & \\hat{k} \\\\
v_1 & v_2 & v_3 \\\\
w_1 & w_2 & w_3 \\\\
\\end{array}
\\right)
""")
def get_eigenvalue(self):
result = TextMobject("\\Text{Det}\\left(A - \\lambda I \\right) = 0")
result.submobjects[-5].highlight(YELLOW)
return result
def get_matrix_multiplication_question(self):
why = TextMobject("Why?").highlight(BLUE)
mult = self.get_matrix_multiplication()
why.next_to(mult, UP)
result = VMobject(why, mult)
result.get_center = lambda : mult.get_center()
return result
def get_cross_product_question(self):
cross = TexMobject("\\vec{v} \\times \\vec{w}")
left_right_arrow = DoubleArrow(Point(LEFT), Point(RIGHT))
det = TextMobject("Det")
@ -143,19 +208,16 @@ class AboutLinearAlgebra(Scene):
q_mark.next_to(left_right_arrow, UP)
cross_question = VMobject(cross, left_right_arrow, q_mark, det)
cross_question.get_center = lambda : left_right_arrow.get_center()
return cross_question
eigen_q = TextMobject("Eigen?")
for mob in matrix_multiplication, cross_question, eigen_q:
bubble.position_mobject_inside(mob)
self.play(FadeIn(mob))
if randy.mode is not "pondering":
self.play(ApplyMethod(randy.change_mode, "pondering"))
self.dither()
else:
self.dither(2)
self.remove(mob)
def get_eigen_question(self):
result = TextMobject(
"What the heck \\\\ does ``eigen'' mean?",
)
for mob in result.submobjects[-11:-6]:
mob.highlight(YELLOW)
return result
class NumericVsGeometric(Scene):
@ -265,11 +327,11 @@ class NumericVsGeometric(Scene):
class ExampleTransformation(LinearTransformationScene):
def construct(self):
self.setup()
self.add_vector(np.array(TRANFORMED_VECTOR).flatten())
self.apply_matrix(EXAMPLE_TRANFORM)
self.dither()
class NumericToComputations(Scene):
def construct(self):
top = TextMobject("Numeric understanding")
@ -285,6 +347,273 @@ class NumericToComputations(Scene):
class LinAlgPyramid(Scene):
def construct(self):
rects = self.get_rects()
words = self.place_words_in_rects([
"Geometric understanding",
"Computations",
"Uses"
], rects)
for word, rect in zip(words, rects):
self.play(
Write(word),
ShowCreation(rect),
run_time = 1
)
self.dither()
self.play(*[
ApplyMethod(m.highlight, DARK_GREY)
for m in words[0], rects[0]
])
self.dither()
self.list_applications(rects[-1])
def get_rects(self):
height = 1
rects = [
Rectangle(height = height, width = width)
for width in 8, 5, 2
]
rects[0].shift(2*DOWN)
for i in 1, 2:
rects[i].next_to(rects[i-1], UP, buff = 0)
return rects
def place_words_in_rects(self, words, rects):
result = []
for word, rect in zip(words, rects):
tex_mob = TextMobject(word)
tex_mob.shift(rect.get_center())
result.append(tex_mob)
return result
def list_applications(self, top_mob):
subjects = [
TextMobject(word).to_corner(UP+RIGHT)
for word in [
"computer science",
"engineering",
"statistics",
"economics",
"pure math",
]
]
arrow = Arrow(top_mob, subjects[0].get_bottom(), color = RED)
self.play(ShowCreation(arrow))
curr_subject = None
for subject in subjects:
if curr_subject:
subject.shift(curr_subject.get_center()-subject.get_center())
self.play(Transform(curr_subject, subject, run_time = 0.5))
else:
curr_subject = subject
self.play(FadeIn(curr_subject, run_time = 0.5))
self.dither()
class IndimidatingProf(Scene):
def construct(self):
randy = Randolph().to_corner()
morty = Mortimer().to_corner(DOWN+RIGHT)
morty.shift(3*LEFT)
speech_bubble = SpeechBubble(height = 3).flip()
speech_bubble.pin_to(morty)
speech_bubble.shift(RIGHT)
speech_bubble.write("And of course $B^{-1}AB$ will \\\\ also have positive eigenvalues...")
thought_bubble = ThoughtBubble(width = 4, height = 4)
thought_bubble.next_to(morty, UP)
thought_bubble.to_edge(RIGHT)
q_marks = TextMobject("???")
q_marks.next_to(randy, UP)
self.add(randy, morty)
self.play(
FadeIn(speech_bubble),
ApplyMethod(morty.change_mode, "speaking")
)
self.play(FadeIn(thought_bubble))
self.dither()
self.play(
ApplyMethod(randy.change_mode, "confused"),
Write(q_marks, run_time = 1)
)
self.dither()
class ThoughtBubbleTransformation(LinearTransformationScene):
def construct(self):
self.setup()
rotation = rotation_about_z(np.pi/3)
self.apply_matrix(
np.linalg.inv(rotation),
path_arc = -np.pi/3,
)
self.apply_matrix(EXAMPLE_TRANFORM)
self.apply_matrix(
rotation,
path_arc = np.pi/3,
)
self.dither()
class SineApproximations(Scene):
def construct(self):
series = self.get_series()
one_approx = self.get_approx_series("1", 1)
one_approx.highlight(YELLOW)
pi_sixts_approx = self.get_approx_series("\\pi/6", np.pi/6)
pi_sixts_approx.highlight(RED)
words = TextMobject("(How calculators compute sine)")
words.highlight(GREEN)
series.to_edge(UP)
one_approx.next_to(series, DOWN, buff = 1.5)
pi_sixts_approx.next_to(one_approx, DOWN, buff = 1.5)
self.play(Write(series))
self.dither()
self.play(FadeIn(words))
self.dither(2)
self.play(FadeOut(words))
self.remove(words)
self.dither()
self.play(Write(one_approx))
self.play(Write(pi_sixts_approx))
self.dither()
def get_series(self):
return TexMobject("""
\\sin(x) = x - \\dfrac{x^3}{3!} + \\dfrac{x^5}{5!}
+ \\cdots + (-1)^n \\dfrac{x^{2n+1}}{(2n+1)!} + \\cdots
""")
def get_approx_series(self, val_str, val):
#Default to 3 terms
approximation = val - (val**3)/6. + (val**5)/120.
return TexMobject("""
\\sin(%s) \\approx
%s - \\dfrac{(%s)^3}{3!} + \\dfrac{(%s)^5}{5!} \\approx
%.04f
"""%(val_str, val_str, val_str, val_str, approximation))
class LooseConnectionToTriangles(Scene):
def construct(self):
sine = TexMobject("\\sin(x)")
triangle = Polygon(ORIGIN, 2*RIGHT, 2*RIGHT+UP)
arrow = DoubleArrow(LEFT, RIGHT)
sine.next_to(arrow, LEFT)
triangle.next_to(arrow, RIGHT)
q_mark = TextMobject("?").scale(1.5)
q_mark.next_to(arrow, UP)
self.add(sine)
self.play(ShowCreation(arrow))
self.play(ShowCreation(triangle))
self.play(Write(q_mark))
self.dither()
class PhysicsExample(Scene):
def construct(self):
title = TextMobject("Physics")
title.to_corner(UP+LEFT)
parabola = FunctionGraph(
lambda x : (3-x)*(3+x)/4,
x_min = -4,
x_max = 4
)
self.play(Write(title))
self.projectile(parabola)
self.velocity_vector(parabola)
self.approximate_sine()
def projectile(self, parabola):
dot = Dot(radius = 0.15)
kwargs = {
"run_time" : 3,
"rate_func" : None
}
self.play(
MoveAlongPath(dot, parabola.copy(), **kwargs),
ShowCreation(parabola, **kwargs)
)
self.dither()
def velocity_vector(self, parabola):
alpha = 0.7
d_alpha = 0.01
vector_length = 3
p1 = parabola.point_from_proportion(alpha)
p2 = parabola.point_from_proportion(alpha + d_alpha)
vector = vector_length*(p2-p1)/np.linalg.norm(p2-p1)
v_mob = Vector(vector, color = YELLOW)
vx = Vector(vector[0]*RIGHT, color = GREEN_B)
vy = Vector(vector[1]*UP, color = RED)
v_mob.shift(p1)
vx.shift(p1)
vy.shift(vx.get_end())
arc = Arc(
angle_of_vector(vector),
radius = vector_length / 4.
)
arc.shift(p1)
theta = TexMobject("\\theta").scale(0.75)
theta.next_to(arc, RIGHT, buff = 0.1)
v_label = TexMobject("\\vec{v}")
v_label.shift(p1 + RIGHT*vector[0]/4 + UP*vector[1]/2)
v_label.highlight(v_mob.get_color())
vx_label = TexMobject("\\vec{v} \\cos(\\theta)")
vx_label.next_to(vx, UP)
vx_label.highlight(vx.get_color())
vy_label = TexMobject("\\vec{v} \\sin(\\theta)")
vy_label.next_to(vy, RIGHT)
vy_label.highlight(vy.get_color())
kwargs = {"submobject_mode" : "one_at_a_time"}
for v in v_mob, vx, vy:
self.play(
ShowCreation(v, submobject_mode = "one_at_a_time")
)
self.play(
ShowCreation(arc),
Write(theta, run_time = 1)
)
for label in v_label, vx_label, vy_label:
self.play(Write(label, run_time = 1))
self.dither()
def approximate_sine(self):
approx = TexMobject("\\sin(\\theta) \\approx 0.7\\text{-ish}")
morty = Mortimer(mode = "speaking")
morty.flip()
morty.to_corner()
bubble = SpeechBubble(width = 4, height = 3)
bubble.set_fill(BLACK, opacity = 1)
bubble.pin_to(morty)
bubble.position_mobject_inside(approx)
self.play(
FadeIn(morty),
ShowCreation(bubble),
Write(approx),
run_time = 2
)
self.dither()
class LinearAlgebraIntuitions(Scene):
def construct(self):
pass

View file

@ -3,7 +3,7 @@ import numpy as np
from scene import Scene
from mobject.vectorized_mobject import VMobject
from mobject.tex_mobject import TexMobject, TextMobject
from animation.transform import ApplyMatrix, ApplyMethod
from animation.transform import ApplyPointwiseFunction, Transform
from topics.number_line import NumberPlane
from topics.geometry import Vector
@ -41,7 +41,9 @@ class LinearTransformationScene(Scene):
}
def setup(self):
self.background_mobjects = []
self.foreground_mobjects = []
self.transformable_mobject = []
self.moving_vectors = []
self.background_plane = NumberPlane(
color = GREY,
secondary_color = DARK_GREY,
@ -54,13 +56,10 @@ class LinearTransformationScene(Scene):
self.add_to_background(self.background_plane)
if self.include_foreground_plane:
self.plane = NumberPlane(**self.foreground_plane_kwargs)
self.add_to_foreground(self.plane)
self.add_to_transformable(self.plane)
if self.show_basis_vectors:
i_hat = Vector(self.background_plane.num_pair_to_point((1, 0)))
j_hat = Vector(self.background_plane.num_pair_to_point((0, 1)))
i_hat.highlight(self.i_hat_color)
j_hat.highlight(self.j_hat_color)
self.add_to_foreground(i_hat, j_hat)
self.add_vector((1, 0), self.i_hat_color)
self.add_vector((0, 1), self.j_hat_color)
def add_to_background(self, *mobjects):
for mobject in mobjects:
@ -68,18 +67,47 @@ class LinearTransformationScene(Scene):
self.background_mobjects.append(mobject)
self.add(mobject)
def add_to_foreground(self, *mobjects):
def add_to_transformable(self, *mobjects):
for mobject in mobjects:
if mobject not in self.foreground_mobjects:
self.foreground_mobjects.append(mobject)
if mobject not in self.transformable_mobject:
self.transformable_mobject.append(mobject)
self.add(mobject)
def add_vector(self, coords, color = YELLOW):
vector = Vector(self.background_plane.num_pair_to_point(coords))
vector.highlight(color)
self.moving_vectors.append(vector)
return vector
def apply_matrix(self, matrix, **kwargs):
self.play(ApplyMatrix(
matrix,
VMobject(*self.foreground_mobjects),
**kwargs
))
matrix = np.array(matrix)
if matrix.shape == (2, 2):
new_matrix = np.identity(3)
new_matrix[:2, :2] = matrix
matrix = new_matrix
elif matrix.shape != (3, 3):
raise "Matrix has bad dimensions"
transpose = np.transpose(matrix)
def func(point):
return np.dot(point, transpose)
new_vectors = [
Vector(func(v.get_end()), color = v.get_stroke_color())
for v in self.moving_vectors
]
self.play(
ApplyPointwiseFunction(
func,
VMobject(*self.transformable_mobject),
**kwargs
),
Transform(
VMobject(*self.moving_vectors),
VMobject(*new_vectors),
**kwargs
)
)

View file

@ -34,7 +34,7 @@ class TexMobject(SVGMobject):
"next_to_direction" : RIGHT,
"next_to_buff" : 0.25,
"initial_scale_val" : TEX_MOB_SCALE_VAL,
"organize_left_to_right" : True,
"organize_left_to_right" : False,
"propogate_style_to_family" : True,
}
def __init__(self, expression, **kwargs):

View file

@ -237,13 +237,10 @@ class Scene(object):
def write_to_movie(self, name = None):
if len(self.frames) == 0:
print "No frames, I'll just save an image instead"
self.show_frame()
self.save_image(name = name)
print "No frames, so I'm not writing anything"
return
if name is None:
name = str(self)
file_path = self.get_movie_file_path(name, ".mp4")
print "Writing to %s"%file_path

View file

@ -74,11 +74,11 @@ class PiCreature(SVGMobject):
def change_mode(self, mode):
curr_center = self.get_center()
curr_height = self.get_height()
flip = self.is_flipped()
self.__class__.__init__(self, mode)
should_be_flipped = self.is_flipped()
self.__init__(mode)
self.scale_to_fit_height(curr_height)
self.shift(curr_center)
if flip:
if should_be_flipped^self.is_flipped():
self.flip()
return self
@ -129,7 +129,7 @@ class Mortimer(PiCreature):
PiCreature.__init__(self, *args, **kwargs)
self.flip()
class Mathematician(PiCreature):
CONFIG = {
"color" : GREY,

View file

@ -9,8 +9,7 @@ class FunctionGraph(VMobject):
"color" : BLUE_D,
"x_min" : -SPACE_WIDTH,
"x_max" : SPACE_WIDTH,
"space_unit_to_num" : 1,
"epsilon" : 0.5,
"num_steps" : 20,
}
def __init__(self, function, **kwargs):
self.function = function
@ -19,8 +18,7 @@ class FunctionGraph(VMobject):
def generate_points(self):
self.set_anchor_points([
x*RIGHT + self.function(x)*UP
for pre_x in np.arange(self.x_min, self.x_max, self.epsilon)
for x in [self.space_unit_to_num*pre_x]
for x in np.linspace(self.x_min, self.x_max, self.num_steps)
], mode = "smooth")

View file

@ -22,14 +22,12 @@ class Arc(VMobject):
self.scale(self.radius)
def get_unscaled_anchor_points(self):
step = self.angle/self.num_anchors
end_angle = self.start_angle + self.angle
if self.anchors_span_full_range:
end_angle += step
return [
np.cos(a)*RIGHT+np.sin(a)*UP
for a in np.arange(
self.start_angle, end_angle, step
for a in np.linspace(
self.start_angle,
self.start_angle + self.angle,
self.num_anchors
)
]
@ -238,7 +236,7 @@ class Rectangle(VMobject):
"close_new_points" : True,
}
def generate_points(self):
y, x = self.height/2, self.width/2
y, x = self.height/2., self.width/2.
self.set_anchor_points([
x*LEFT+y*UP,
x*RIGHT+y*UP,