Up to formal definition of linearity in EoLA chapter 3

This commit is contained in:
Grant Sanderson
2016-07-22 18:45:34 -07:00
parent 46e5dbfa14
commit 17505d4d9e
8 changed files with 244 additions and 77 deletions

View File

@ -98,12 +98,8 @@ class ApplyMethod(Transform):
"the method you want to animate"
)
assert(isinstance(method.im_self, Mobject))
Transform.__init__(
self,
method.im_self,
copy.deepcopy(method)(*args),
**kwargs
)
target = copy.deepcopy(method)(*args)
Transform.__init__(self, method.im_self, target, **kwargs)
class FadeOut(Transform):
CONFIG = {

View File

@ -20,6 +20,10 @@ from mobject.vectorized_mobject import *
from eola.matrix import *
from eola.two_d_space import *
def curvy_squish(point):
x, y, z = point
return (x+np.cos(y))*RIGHT + (y+np.sin(x))*UP
class OpeningQuote(Scene):
def construct(self):
words = TextMobject([
@ -49,7 +53,6 @@ class OpeningQuote(Scene):
self.play(Write(comment))
self.dither()
class Introduction(TeacherStudentsScene):
def construct(self):
title = TextMobject("Matrices as linear transformations")
@ -71,7 +74,6 @@ class Introduction(TeacherStudentsScene):
return (SPACE_WIDTH+SPACE_HEIGHT)*p/np.linalg.norm(p)
self.play(ApplyPointwiseFunction(spread_out, everything))
class ShowGridCreation(Scene):
def construct(self):
plane = NumberPlane()
@ -114,7 +116,6 @@ class IntroduceLinearTransformations(LinearTransformationScene):
)
self.dither()
class SimpleLinearTransformationScene(LinearTransformationScene):
CONFIG = {
"show_basis_vectors" : False,
@ -143,8 +144,7 @@ class SimpleNonlinearTransformationScene(LinearTransformationScene):
self.dither()
def func(self, point):
x, y, z = point
return (x+np.cos(y))*RIGHT + (y+np.sin(x))*UP
return curvy_squish(point)
class MovingOrigin(SimpleNonlinearTransformationScene):
CONFIG = {
@ -185,7 +185,6 @@ class SneakyNonlinearTransformationExplained(SneakyNonlinearTransformation):
self.play(ShowCreation(diag))
self.add_transformable_mobject(diag)
class AnotherLinearTransformation(SimpleLinearTransformationScene):
CONFIG = {
"transposed_matrix" : [
@ -209,7 +208,6 @@ class AnotherLinearTransformation(SimpleLinearTransformationScene):
self.play(Write(text))
self.dither()
class Rotation(SimpleLinearTransformationScene):
CONFIG = {
"angle" : np.pi/3,
@ -221,8 +219,6 @@ class Rotation(SimpleLinearTransformationScene):
]
SimpleLinearTransformationScene.construct(self)
class YetAnotherLinearTransformation(SimpleLinearTransformationScene):
CONFIG = {
"transposed_matrix" : [
@ -231,7 +227,6 @@ class YetAnotherLinearTransformation(SimpleLinearTransformationScene):
]
}
class MoveAroundAllVectors(LinearTransformationScene):
CONFIG = {
"show_basis_vectors" : False,
@ -269,17 +264,15 @@ class MoveAroundAllVectors(LinearTransformationScene):
self.add(vector.copy().highlight(DARK_GREY))
else:
for vector in vectors.split():
self.add_vector(vector)
self.add_vector(vector, animate = False)
self.apply_transposed_matrix([[3, 0], [1, 2]])
self.dither()
class MoveAroundJustOneVector(MoveAroundAllVectors):
CONFIG = {
"focus_on_one_vector" : True,
}
class RotateIHat(LinearTransformationScene):
CONFIG = {
"show_basis_vectors" : False
@ -297,8 +290,6 @@ class RotateIHat(LinearTransformationScene):
self.play(Write(j_label, run_time = 1))
self.dither()
class TransformationsAreFunctions(Scene):
def construct(self):
title = TextMobject([
@ -345,7 +336,6 @@ class TransformationsAreFunctions(Scene):
self.play(Write(v), ShowCreation(a), run_time = 1)
self.dither()
class UsedToThinkinfOfFunctionsAsGraphs(VectorScene):
def construct(self):
self.show_graph()
@ -409,6 +399,158 @@ class UsedToThinkinfOfFunctionsAsGraphs(VectorScene):
)
self.dither()
class TryingToVisualizeFourDimensions(Scene):
def construct(self):
randy = Randolph().to_corner()
bubble = randy.get_bubble()
formula = TexMobject("""
L\\left(\\left[
\\begin{array}{c}
x \\\\
y
\\end{array}
\\right]\\right) =
\\left[
\\begin{array}{c}
2x + y \\\\
x + 2y
\\end{array}
\\right]
""")
formula.next_to(randy, RIGHT)
formula.split()[3].highlight(X_COLOR)
formula.split()[4].highlight(Y_COLOR)
VMobject(*formula.split()[9:9+4]).highlight(MAROON_C)
VMobject(*formula.split()[13:13+4]).highlight(BLUE)
thought = TextMobject("""
Do I imagine plotting
$(x, y, 2x+y, x+2y)$???
""")
thought.split()[-17].highlight(X_COLOR)
thought.split()[-15].highlight(Y_COLOR)
VMobject(*thought.split()[-13:-13+4]).highlight(MAROON_C)
VMobject(*thought.split()[-8:-8+4]).highlight(BLUE)
bubble.position_mobject_inside(thought)
thought.shift(0.2*UP)
self.add(randy)
self.play(
ApplyMethod(randy.look, DOWN+RIGHT),
Write(formula)
)
self.play(
ApplyMethod(randy.change_mode, "pondering"),
ShowCreation(bubble),
Write(thought)
)
self.play(Blink(randy))
self.dither()
self.remove(thought)
bubble.make_green_screen()
self.dither()
self.play(Blink(randy))
self.play(ApplyMethod(randy.change_mode, "confused"))
self.dither()
self.play(Blink(randy))
self.dither()
class ForgetAboutGraphs(Scene):
def construct(self):
self.play(Write("You must unlearn graphs"))
self.dither()
class ThinkAboutFunctionAsMovingVector(LinearTransformationScene):
CONFIG = {
"show_basis_vectors" : False,
"leave_ghost_vectors" : True,
}
def construct(self):
self.setup()
vector = self.add_vector([2, 1])
label = self.add_transformable_label(vector, "v")
self.dither()
self.apply_transposed_matrix([[1, 1], [-3, 1]])
self.dither()
class PrepareForFormalDefinition(TeacherStudentsScene):
def construct(self):
self.setup()
self.teacher_says("Get ready for a formal definition!")
self.dither(3)
bubble = self.student_thinks("")
bubble.make_green_screen()
self.dither(3)
class AdditivityProperty(LinearTransformationScene):
CONFIG = {
"show_basis_vectors" : False,
"give_title" : True,
"transposed_matrix" : [[2, 0], [1, 1]],
"nonlinear_transformation" : None,
"vector_v" : [2, 1],
"vector_w" : [1, -2],
}
def construct(self):
self.setup()
added_anims = []
if self.give_title:
title = TextMobject("""
First fundamental property of
linear transformations
""")
title.to_edge(UP)
title.add_background_rectangle()
self.play(Write(title))
added_anims.append(Animation(title))
self.dither()
# self.play(ApplyMethod(self.plane.fade))
v, w = self.draw_all_vectors()
self.apply_transformation()
self.show_final_sum(v, w)
def draw_all_vectors(self):
v = self.add_vector(self.vector_v, color = MAROON_C)
v_label = self.add_transformable_label(v, "v")
w = self.add_vector(self.vector_w, color = GREEN)
w_label = self.add_transformable_label(w, "w")
new_w = w.copy().fade(0.4)
self.play(ApplyMethod(new_w.shift, v.get_end()))
sum_vect = self.add_vector(new_w.get_end(), color = PINK)
sum_label = self.add_transformable_label(
sum_vect,
"%s + %s"%(v_label.expression, w_label.expression),
rotate = True
)
self.play(FadeOut(new_w))
return v, w
def apply_transformation(selfe):
if self.nonlinear_transformation:
self.apply_nonlinear_transformation(self.nonlinear_transformation)
else:
self.apply_transposed_matrix(
self.transposed_matrix,
added_anims = added_anims
)
self.dither()
def show_final_sum(self, v, w):
new_w = w.copy()
self.play(ApplyMethod(new_w.shift, v.get_end()))
self.dither()
class NonlinearLacksAdditivity(AdditivityProperty):
CONFIG = {
"give_title" : False,
"nonlinear_transformation" : curvy_squish,
"vector_w" : [2, -3],
}

View File

@ -84,28 +84,22 @@ class VectorScene(Scene):
direction = "left",
rotate = False,
color = WHITE,
buff_factor = 2,
label_scale_val = VECTOR_LABEL_SCALE_VAL):
if len(label) == 1:
label = "\\vec{\\textbf{%s}}"%label
label = TexMobject(label)
label.highlight(color)
label.highlight(color)
label.scale(label_scale_val)
if rotate:
label.rotate(vector.get_angle())
label.add_background_rectangle()
vector_vect = vector.get_end() - vector.get_start()
if direction is "left":
rot_angle = -np.pi/2
else:
rot_angle = np.pi/2
boundary_dir = -np.round(rotate_vector(vector_vect, rot_angle))
boundary_point = label.get_critical_point(boundary_dir)
label.shift(buff_factor*boundary_point)
label.shift(vector_vect/2.)
angle = vector.get_angle()
label.shift(label.get_height()*UP)
label.rotate(angle)
label.shift((vector.get_end() - vector.get_start())/2)
if not rotate:
label.rotate_in_place(-angle)
return label
def label_vector(self, vector, label, animate = True, **kwargs):
label = self.get_vector_label(vector, label, **kwargs)
if animate:
@ -252,11 +246,13 @@ class LinearTransformationScene(VectorScene):
"show_basis_vectors" : True,
"i_hat_color" : X_COLOR,
"j_hat_color" : Y_COLOR,
"leave_ghost_vectors" : False,
}
def setup(self):
self.background_mobjects = []
self.transformable_mobject = []
self.transformable_mobjects = []
self.moving_vectors = []
self.transformable_labels = []
self.background_plane = NumberPlane(
**self.background_plane_kwargs
@ -281,8 +277,8 @@ class LinearTransformationScene(VectorScene):
def add_transformable_mobject(self, *mobjects):
for mobject in mobjects:
if mobject not in self.transformable_mobject:
self.transformable_mobject.append(mobject)
if mobject not in self.transformable_mobjects:
self.transformable_mobjects.append(mobject)
self.add(mobject)
def add_vector(self, vector, color = YELLOW, **kwargs):
@ -292,6 +288,14 @@ class LinearTransformationScene(VectorScene):
self.moving_vectors.append(vector)
return vector
def add_transformable_label(self, vector, label, **kwargs):
label_mob = self.label_vector(vector, label, **kwargs)
label_mob.target_text = "L(%s)"%label_mob.expression
label_mob.vector = vector
label_mob.kwargs = kwargs
self.transformable_labels.append(label_mob)
return label_mob
def get_matrix_transformation(self, transposed_matrix):
transposed_matrix = np.array(transposed_matrix)
if transposed_matrix.shape == (2, 2):
@ -302,14 +306,24 @@ class LinearTransformationScene(VectorScene):
raise "Matrix has bad dimensions"
return lambda point: np.dot(point, transposed_matrix)
def get_piece_movement(self, pieces):
start = VMobject(*pieces)
target = VMobject(*[mob.target for mob in pieces])
if self.leave_ghost_vectors:
self.add(start.copy().fade(0.7))
return Transform(start, target, submobject_mode = "all_at_once")
def get_vector_movement(self, func):
start = VMobject(*self.moving_vectors)
target = VMobject(*[
Vector(func(v.get_end()), color = v.get_color())
for v in self.moving_vectors
])
return Transform(start, target)
for v in self.moving_vectors:
v.target = Vector(func(v.get_end()), color = v.get_color())
return self.get_piece_movement(self.moving_vectors)
def get_transformable_label_movement(self):
for l in self.transformable_labels:
l.target = self.get_vector_label(
l.vector.target, l.target_text, **l.kwargs
)
return self.get_piece_movement(self.transformable_labels)
def apply_transposed_matrix(self, transposed_matrix, **kwargs):
func = self.get_matrix_transformation(transposed_matrix)
@ -322,18 +336,20 @@ class LinearTransformationScene(VectorScene):
self.apply_function(func, **kwargs)
def apply_nonlinear_transformation(self, function, **kwargs):
self.plane.prepare_for_nonlinear_transform(100)
self.plane.prepare_for_nonlinear_transform()
self.apply_function(function, **kwargs)
def apply_function(self, function, **kwargs):
def apply_function(self, function, added_anims = [], **kwargs):
if "run_time" not in kwargs:
kwargs["run_time"] = 3
self.play(
ApplyPointwiseFunction(
function,
VMobject(*self.transformable_mobject),
VMobject(*self.transformable_mobjects),
),
self.get_vector_movement(function),
self.get_transformable_label_movement(),
*added_anims,
**kwargs
)

View File

@ -12,6 +12,7 @@ from scipy import linalg
from constants import *
CLOSED_THRESHOLD = 0.01
STRAIGHT_PATH_THRESHOLD = 0.01
def get_smooth_handle_points(points):
num_handles = len(points) - 1
@ -262,7 +263,7 @@ def path_along_arc(arc_angle):
If vect is vector from start to end, [vect[:,1], -vect[:,0]] is
perpendicualr to vect in the left direction.
"""
if arc_angle == 0:
if abs(arc_angle) < STRAIGHT_PATH_THRESHOLD:
return straight_path
def path(start_points, end_points, alpha):
vects = end_points - start_points

View File

@ -314,7 +314,7 @@ class Mobject(object):
start = color_to_rgb(mob.get_color())
end = color_to_rgb(color)
new_rgb = interpolate(start, end, alpha)
mob.highlight(Color(rgb = new_rgb))
mob.highlight(Color(rgb = new_rgb), family = False)
return self
def fade(self, darkness = 0.5):

View File

@ -45,22 +45,22 @@ class VMobject(Mobject):
for mob in mobs:
if stroke_color is not None:
mob.stroke_rgb = color_to_rgb(stroke_color)
if stroke_width is not None:
mob.stroke_width = stroke_width
if fill_color is not None:
mob.fill_rgb = color_to_rgb(fill_color)
if stroke_width is not None:
mob.stroke_width = stroke_width
if fill_opacity is not None:
mob.fill_opacity = fill_opacity
probably_meant_to_change_opacity = reduce(op.and_, [
fill_color is not None,
fill_opacity is None,
mob.fill_opacity == 0
])
if probably_meant_to_change_opacity:
mob.fill_opacity = 1
return self
def set_fill(self, color = None, opacity = None, family = True):
probably_meant_to_change_opacity = reduce(op.and_, [
color is not None,
opacity is None,
self.fill_opacity == 0
])
if probably_meant_to_change_opacity:
opacity = 1
return self.set_style_data(
fill_color = color,
fill_opacity = opacity,
@ -75,8 +75,11 @@ class VMobject(Mobject):
)
def highlight(self, color, family = True):
self.set_fill(color = color, family = family)
self.set_stroke(color = color, family = family)
self.set_style_data(
stroke_color = color,
fill_color = color,
family = family
)
return self
# def fade(self, darkness = 0.5):
@ -203,6 +206,13 @@ class VMobject(Mobject):
self.submobjects
)
def apply_function(self, function, maintain_smoothness = True):
Mobject.apply_function(self, function)
if maintain_smoothness:
self.make_smooth()
return self
## Information about line
def component_curves(self):

View File

@ -226,12 +226,9 @@ class Bubble(SVGMobject):
return mobject
def add_content(self, mobject):
if self.content in self.submobjects:
self.submobjects.remove(self.content)
self.position_mobject_inside(mobject)
self.content = mobject
self.add(self.content)
return self
return self.content
def write(self, text):
self.add_content(TextMobject(text))
@ -296,11 +293,12 @@ class TeacherStudentsScene(Scene):
bubble.add_content(content)
if pi_creature.bubble:
content_intro_anims = [
Transform(pi_creature.bubble, bubble)
Transform(pi_creature.bubble, bubble),
Transform(pi_creature.bubble.content, bubble.content)
]
else:
content_intro_anims = [
FadeIn(bubble.split()[0]),
FadeIn(bubble),
Write(content),
]
pi_creature.bubble = bubble
@ -324,7 +322,10 @@ class TeacherStudentsScene(Scene):
for p in self.get_everyone():
if p.bubble and p is not pi_creature:
added_anims.append(FadeOut(p.bubble))
added_anims += [
FadeOut(p.bubble),
FadeOut(p.bubble.content)
]
p.bubble = None
added_anims.append(ApplyMethod(p.change_mode, "plain"))
@ -335,24 +336,25 @@ class TeacherStudentsScene(Scene):
),
]
self.play(*anims)
return pi_creature.bubble
def teacher_says(self, content, **kwargs):
self.introduce_bubble(
return self.introduce_bubble(
content, "speech", self.get_teacher(), **kwargs
)
def student_says(self, content, student_index = 1, **kwargs):
student = self.get_students()[student_index]
self.introduce_bubble(content, "speech", student, **kwargs)
return self.introduce_bubble(content, "speech", student, **kwargs)
def teacher_thinks(self, content, **kwargs):
self.introduce_bubble(
return self.introduce_bubble(
content, "thought", self.get_teacher(), **kwargs
)
def student_thinks(self, content, student_index = 1, **kwargs):
student = self.get_students()[student_index]
self.introduce_bubble(content, "thought", student, **kwargs)
return self.introduce_bubble(content, "thought", student, **kwargs)
def random_blink(self, num_times = 1):
for x in range(num_times):

View File

@ -240,11 +240,11 @@ class NumberPlane(VMobject):
arrow = Arrow(ORIGIN, coords, **kwargs)
return arrow
def prepare_for_nonlinear_transform(self, num_inserted_anchor_points = 40):
for mob in self.submobject_family():
if mob.get_num_points() > 0:
mob.insert_n_anchor_points(num_inserted_anchor_points)
mob.change_anchor_mode("smooth")
def prepare_for_nonlinear_transform(self, num_inserted_anchor_points = 50):
for mob in self.family_members_with_points():
mob.insert_n_anchor_points(num_inserted_anchor_points)
mob.make_smooth()
return self