First page of chapter 5

This commit is contained in:
Grant Sanderson 2016-08-02 12:26:15 -07:00
parent 014b7bfdef
commit 6fe057c0f9
8 changed files with 419 additions and 20 deletions

355
eola/chapter5.py Normal file
View file

@ -0,0 +1,355 @@
from mobject.tex_mobject import TexMobject
from mobject import Mobject
from mobject.image_mobject import ImageMobject
from mobject.vectorized_mobject import VMobject
from animation.animation import Animation
from animation.transform import *
from animation.simple_animations import *
from topics.geometry import *
from topics.characters import *
from topics.functions import *
from topics.number_line import *
from topics.numerals import *
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.matrix import *
from eola.two_d_space import *
from eola.chapter3 import MatrixVectorMultiplicationAbstract
class Blob(Circle):
CONFIG = {
"stroke_color" : TEAL,
"fill_color" : BLUE_E,
"fill_opacity" : 1,
"random_seed" : 1,
"random_nudge_size" : 0.5,
"height" : 2,
}
def __init__(self, **kwargs):
Circle.__init__(self, **kwargs)
random.seed(self.random_seed)
self.apply_complex_function(
lambda z : z*(1+self.random_nudge_size*(random.random()-0.5))
)
self.scale_to_fit_height(self.height).center()
def probably_contains(self, point):
border_points = np.array(self.get_anchors_and_handles()[0])
distances = map(lambda p : np.linalg.norm(p-point), border_points)
min3 = border_points[np.argsort(distances)[:3]]
center_direction = self.get_center() - point
in_center_direction = map(
lambda p : np.dot(p-point, center_direction) > 0,
min3
)
return sum(in_center_direction) <= 2
class OpeningQuote(Scene):
def construct(self):
words = TextMobject([
"``The purpose of computation is \\\\",
"insight",
", not ",
"numbers.",
"''",
], separate_list_arg_with_spaces = False)
# words.scale_to_fit_width(2*SPACE_WIDTH - 2)
words.to_edge(UP)
words.split()[1].highlight(BLUE)
words.split()[3].highlight(GREEN)
author = TextMobject("-Richard Hamming")
author.highlight(YELLOW)
author.next_to(words, DOWN, buff = 0.5)
self.play(FadeIn(words))
self.dither(2)
self.play(Write(author, run_time = 3))
self.dither()
class MovingForward(TeacherStudentsScene):
def construct(self):
self.setup()
student = self.get_students()[1]
bubble = student.get_bubble(direction = RIGHT, width = 5)
bubble.rotate(-np.pi/12)
bubble.next_to(student, UP, aligned_edge = RIGHT)
bubble.shift(0.5*LEFT)
bubble.make_green_screen()
self.teacher_says("""
Y'all know about linear
transformations, right?
""", width = 7)
self.play(
ShowCreation(bubble),
student.change_mode, "pondering"
)
self.dither(2)
class StretchingTransformation(LinearTransformationScene):
def construct(self):
self.setup()
self.add_title("Generally stretches space")
self.apply_transposed_matrix([[3, 1], [-1, 2]])
self.dither()
class SquishingTransformation(LinearTransformationScene):
CONFIG = {
"foreground_plane_kwargs" : {
"x_radius" : 3*SPACE_WIDTH,
"y_radius" : 3*SPACE_WIDTH,
"secondary_line_ratio" : 0
},
}
def construct(self):
self.setup()
self.add_title("Generally squishes space")
self.apply_transposed_matrix([[1./2, -0.5], [1, 1./3]])
self.dither()
class AskAboutStretching(LinearTransformationScene):
def construct(self):
self.setup()
words = TextMobject("""
Exactly how much are
things being stretched?
""")
words.add_background_rectangle()
words.to_corner(UP+RIGHT)
words.highlight(YELLOW)
self.apply_transposed_matrix(
[[2, 1], [-1, 3]],
added_anims = [Write(words)]
)
self.dither()
class AskAboutStretchingSpecifically(LinearTransformationScene):
def construct(self):
self.setup()
self.add_title(["How much are", "areas", "scaled?"])
hma, areas, scaled = self.title.split()[1].split()
areas.highlight(YELLOW)
blob = Blob().shift(UP+RIGHT)
label = TextMobject("Area")
label.highlight(YELLOW)
label = VMobject(VectorizedPoint(label.get_left()), label)
label.move_to(blob)
target_label = TexMobject(["c \\cdot", "\\text{Area}"])
target_label.split()[1].highlight(YELLOW)
self.add_transformable_mobject(blob)
self.add_moving_mobject(label, target_label)
self.dither()
self.apply_transposed_matrix([[2, -1], [1, 1]])
arrow = Arrow(scaled, label.target.split()[0])
self.play(ShowCreation(arrow))
self.dither()
class BeautyNowUsesLater(TeacherStudentsScene):
def construct(self):
self.setup()
self.teacher_says("Beauty now, uses later")
self.dither()
class DiagonalExample(LinearTransformationScene):
CONFIG = {
"show_square" : False,
"show_coordinates" : True,
"transposed_matrix" : [[3, 0], [0, 2]]
}
def construct(self):
self.setup()
matrix = Matrix(np.array(self.transposed_matrix).transpose())
matrix.highlight_columns(X_COLOR, Y_COLOR)
matrix.next_to(ORIGIN, LEFT).to_edge(UP)
matrix_background = BackgroundRectangle(matrix)
self.play(ShowCreation(matrix_background), Write(matrix))
if self.show_square:
self.add_unit_square(animate = True)
self.add_foreground_mobject(matrix_background, matrix)
self.dither()
self.apply_transposed_matrix([self.transposed_matrix[0], [0, 1]])
self.apply_transposed_matrix([[1, 0], self.transposed_matrix[1]])
self.dither()
if self.show_square:
bottom_brace = Brace(self.i_hat, DOWN)
right_brace = Brace(self.square, RIGHT)
width = TexMobject(str(self.transposed_matrix[0][0]))
height = TexMobject(str(self.transposed_matrix[1][1]))
width.next_to(bottom_brace, DOWN)
height.next_to(right_brace, RIGHT)
for mob in bottom_brace, width, right_brace, height:
mob.add_background_rectangle()
self.play(Write(mob, run_time = 0.5))
self.dither()
width_target, height_target = width.copy(), height.copy()
det = np.linalg.det(self.transposed_matrix)
times, eq_det = map(TexMobject, ["\\times", "=%d"%det])
words = TextMobject("New area $=$")
equation = VMobject(
words, width_target, times, height_target, eq_det
)
equation.arrange_submobjects(RIGHT, buff = 0.2)
equation.next_to(self.square, UP, aligned_edge = LEFT)
equation.shift(0.5*RIGHT)
background_rect = BackgroundRectangle(equation)
self.play(
ShowCreation(background_rect),
Transform(width.copy(), width_target),
Transform(height.copy(), height_target),
*map(Write, [words, times, eq_det])
)
self.dither()
class DiagonalExampleWithSquare(DiagonalExample):
CONFIG = {
"show_square" : True
}
class ShearExample(DiagonalExample):
CONFIG = {
"show_square" : False,
"show_coordinates" : True,
"transposed_matrix" : [[1, 0], [1, 1]]
}
class ShearExampleWithSquare(DiagonalExample):
CONFIG = {
"show_square" : True,
"show_coordinates" : True,
"show_coordinates" : False,
"transposed_matrix" : [[1, 0], [1, 1]]
}
class ThisSquareTellsEverything(LinearTransformationScene):
def construct(self):
self.setup()
self.add_unit_square()
words = TextMobject("""
This square gives you
everything you need.
""")
words.to_corner(UP+RIGHT)
words.highlight(YELLOW)
words.add_background_rectangle()
arrow = Arrow(
words.get_bottom(), self.square.get_right(),
color = WHITE
)
self.play(Write(words, run_time = 2))
self.play(ShowCreation(arrow))
self.add_foreground_mobject(words, arrow)
self.dither()
self.apply_transposed_matrix([[1.5, -0.5], [1, 1.5]])
self.dither()
class WhatHappensToOneSquareHappensToAll(LinearTransformationScene):
def construct(self):
self.setup()
self.add_unit_square()
pairs = [
(2*RIGHT+UP, 1),
(3*LEFT, 2),
(2*LEFT+DOWN, 0.5),
(3.5*RIGHT+2.5*UP, 1.5),
(RIGHT+2*DOWN, 0.25),
(3*LEFT+3*DOWN, 1),
]
squares = VMobject()
for position, side_length in pairs:
square = self.square.copy()
square.scale(side_length)
square.shift(position)
squares.add(square)
self.play(FadeIn(
squares, submobject_mode = "lagged_start",
run_time = 3
))
self.add_transformable_mobject(squares)
self.apply_transposed_matrix([[1, -1], [0.5, 1]])
self.dither()
class BreakBlobIntoGridSquares(LinearTransformationScene):
CONFIG = {
"square_size" : 0.5,
"blob_height" : 3,
}
def construct(self):
self.setup()
blob = Blob(
height = self.blob_height,
random_seed = 5,
random_nudge_size = 0.2,
)
blob.next_to(ORIGIN, UP+RIGHT)
self.add_transformable_mobject(blob)
arange = np.arange(
0, self.blob_height + self.square_size,
self.square_size
)
square = Square(side_length = self.square_size)
square.set_stroke(YELLOW, width = 2)
square.set_fill(YELLOW, opacity = 0.3)
squares = VMobject()
for x, y in it.product(*[arange]*2):
point = x*RIGHT + y*UP
if blob.probably_contains(point):
squares.add(square.copy().shift(point))
self.play(ShowCreation(
squares, submobject_mode = "lagged_start",
run_time = 2,
))
self.add_transformable_mobject(squares)
self.dither()
self.apply_transposed_matrix([[1, -1], [0.5, 1]])
self.dither()
class BreakBlobIntoGridSquaresGranular(BreakBlobIntoGridSquares):
CONFIG = {
"square_size" : 0.25
}
class BreakBlobIntoGridSquaresVeryGranular(BreakBlobIntoGridSquares):
CONFIG = {
"square_size" : 0.1
}
class NameDeterminant(LinearTransformationScene):
CONFIG = {
"t_matrix" : [[1, 0], [2, 1]]
}
def construct(self):
self.setup()
self.add_unit_square()
self.add_title(["The", "``determinant''", "of a transformation"])
self.title.split()[1].split()[1].highlight(YELLOW)
text = TextMobject("Area $=1$")
text.move_to(self.square)
det = np.linalg.det(self.t_matrix)
self.add_moving_mobject(text, TextMobject("Area $=%d$"%det))
self.show_frame()

View file

@ -9,7 +9,8 @@ from animation.transform import ApplyPointwiseFunction, Transform, \
ApplyMethod, FadeOut, ApplyFunction
from animation.simple_animations import ShowCreation, Write
from topics.number_line import NumberPlane, Axes
from topics.geometry import Vector, Line, Circle, Arrow, Dot, BackgroundRectangle
from topics.geometry import Vector, Line, Circle, Arrow, Dot, \
BackgroundRectangle, Square
from helpers import *
from eola.matrix import Matrix, VECTOR_LABEL_SCALE_VAL, vector_coordinate_label
@ -260,8 +261,9 @@ class LinearTransformationScene(VectorScene):
self.background_mobjects = []
self.foreground_mobjects = []
self.transformable_mobjects = []
self.moving_vectors = []
self.moving_vectors = []
self.transformable_labels = []
self.moving_mobjects = []
self.background_plane = NumberPlane(
@ -301,6 +303,24 @@ class LinearTransformationScene(VectorScene):
def add_transformable_mobject(self, *mobjects):
self.add_special_mobjects(self.transformable_mobjects, *mobjects)
def add_moving_mobject(self, mobject, target_mobject = None):
mobject.target = target_mobject
self.add_special_mobjects(self.moving_mobjects, mobject)
def add_unit_square(self, color = YELLOW, opacity = 0.3, animate = False):
square = Square(color = color, side_length = 1)
square.shift(-square.get_corner(DOWN+LEFT))
if animate:
added_anims = map(Animation, self.moving_vectors)
self.play(ShowCreation(square), *added_anims)
self.play(square.set_fill, color, opacity, *added_anims)
else:
square.set_fill(color, opacity)
self.add_transformable_mobject(square)
self.bring_to_front(*self.moving_vectors)
self.square = square
return self
def add_vector(self, vector, color = YELLOW, **kwargs):
vector = VectorScene.add_vector(
self, vector, color = color, **kwargs
@ -334,6 +354,7 @@ class LinearTransformationScene(VectorScene):
if animate:
self.play(Write(title))
self.add_foreground_mobject(title)
self.title = title
return self
def get_matrix_transformation(self, transposed_matrix):
@ -353,6 +374,14 @@ class LinearTransformationScene(VectorScene):
self.add(start.copy().fade(0.7))
return Transform(start, target, submobject_mode = "all_at_once")
def get_moving_mobject_movement(self, func):
for m in self.moving_mobjects:
if m.target is None:
m.target = m.copy()
target_point = func(m.get_center())
m.target.move_to(target_point)
return self.get_piece_movement(self.moving_mobjects)
def get_vector_movement(self, func):
for v in self.moving_vectors:
v.target = Vector(func(v.get_end()), color = v.get_color())
@ -382,13 +411,18 @@ class LinearTransformationScene(VectorScene):
def apply_function(self, function, added_anims = [], **kwargs):
if "run_time" not in kwargs:
kwargs["run_time"] = 3
added_anims.append(self.get_vector_movement(function))
added_anims.append(self.get_transformable_label_movement())
added_anims += map(Animation, self.foreground_mobjects)
function_application = ApplyPointwiseFunction(
function, VMobject(*self.transformable_mobjects)
)
self.play(function_application, *added_anims, **kwargs)
anims = [
ApplyPointwiseFunction(function, t_mob)
for t_mob in self.transformable_mobjects
] + [
self.get_vector_movement(function),
self.get_transformable_label_movement(),
self.get_moving_mobject_movement(function),
] + [
Animation(f_mob)
for f_mob in self.foreground_mobjects
] + added_anims
self.play(*anims, **kwargs)

View file

@ -122,7 +122,8 @@ def is_scene(obj):
def prompt_user_for_choice(name_to_obj):
num_to_name = {}
for count, name in zip(it.count(1), name_to_obj):
names = sorted(name_to_obj.keys())
for count, name in zip(it.count(1), names):
print "%d: %s"%(count, name)
num_to_name[count] = name
try:

View file

@ -224,12 +224,12 @@ class Mobject(object):
self.shift(target_point - point_to_align + buff*direction)
return self
def shift_onto_screen(self):
def shift_onto_screen(self, **kwargs):
space_lengths = [SPACE_WIDTH, SPACE_HEIGHT]
for vect in UP, DOWN, LEFT, RIGHT:
dim = np.argmax(np.abs(vect))
if abs(self.get_edge_center(vect)[dim]) > space_lengths[dim]:
self.to_edge(vect)
self.to_edge(vect, **kwargs)
def stretch_to_fit(self, length, dim, stretch = True):
old_length = self.length_over_dim(dim)

View file

@ -14,6 +14,7 @@ class VMobject(Mobject):
"is_subpath" : False,
"close_new_points" : False,
"mark_paths_closed" : False,
"considered_smooth" : True,
"propogate_style_to_family" : False,
}
def __init__(self, *args, **kwargs):
@ -181,6 +182,7 @@ class VMobject(Mobject):
return self
def make_smooth(self):
self.considered_smooth = True
return self.change_anchor_mode("smooth")
def make_jagged(self):
@ -211,7 +213,7 @@ class VMobject(Mobject):
def apply_function(self, function, maintain_smoothness = True):
Mobject.apply_function(self, function)
if maintain_smoothness:
if maintain_smoothness and self.considered_smooth:
self.make_smooth()
return self

View file

@ -114,13 +114,13 @@ class Scene(object):
)
return self
def bring_to_front(self, mobject):
self.add(mobject)
def bring_to_front(self, *mobjects):
self.add(*mobjects)
return self
def bring_to_back(self, mobject):
self.remove(mobject)
self.mobjects = [mobject] + self.mobjects
def bring_to_back(self, *mobjects):
self.remove(*mobjects)
self.mobjects = mobjects + self.mobjects
return self
def clear(self):

View file

@ -189,6 +189,7 @@ class Bubble(SVGMobject):
self.stretch_to_fit_width(self.width)
if self.direction[0] > 0:
Mobject.flip(self)
self.direction_was_specified = ("direction" in kwargs)
self.content = Mobject()
def get_tip(self):
@ -207,9 +208,11 @@ class Bubble(SVGMobject):
self.direction = -np.array(self.direction)
return self
def pin_to(self, mobject, allow_flipping = True):
def pin_to(self, mobject):
mob_center = mobject.get_center()
if np.sign(mob_center[0]) != np.sign(self.direction[0]) and allow_flipping:
want_to_filp = np.sign(mob_center[0]) != np.sign(self.direction[0])
can_flip = not self.direction_was_specified
if want_to_filp and can_flip:
self.flip()
boundary_point = mobject.get_critical_point(UP-self.direction)
vector_from_center = 1.0*(boundary_point-mob_center)

View file

@ -56,6 +56,7 @@ class Dot(Circle): #Use 1D density, even though 2D
class Line(VMobject):
CONFIG = {
"buff" : 0,
"considered_smooth" : False,
}
def __init__(self, start, end, **kwargs):
digest_config(self, kwargs)
@ -214,6 +215,7 @@ class Polygon(VMobject):
"color" : GREEN_D,
"mark_paths_closed" : True,
"close_new_points" : True,
"considered_smooth" : False,
}
def __init__(self, *vertices, **kwargs):
assert len(vertices) > 1
@ -244,6 +246,7 @@ class Rectangle(VMobject):
"width" : 4.0,
"mark_paths_closed" : True,
"close_new_points" : True,
"considered_smooth" : False,
}
def generate_points(self):
y, x = self.height/2., self.width/2.
@ -302,6 +305,7 @@ class Grid(VMobject):
CONFIG = {
"height" : 6.0,
"width" : 6.0,
"considered_smooth" : False,
}
def __init__(self, rows, columns, **kwargs):
digest_config(self, kwargs, locals())