2015-10-06 12:16:55 -07:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
import numpy as np
|
|
|
|
import itertools as it
|
|
|
|
from copy import deepcopy
|
|
|
|
import sys
|
|
|
|
|
|
|
|
|
|
|
|
from animation import *
|
|
|
|
from mobject import *
|
|
|
|
from constants import *
|
|
|
|
from region import *
|
2015-10-06 19:33:40 -07:00
|
|
|
from scene import Scene, NumberLineScene
|
2015-10-06 12:16:55 -07:00
|
|
|
from script_wrapper import command_line_create_scene
|
|
|
|
|
|
|
|
MOVIE_PREFIX = "matrix_as_transform_2d/"
|
|
|
|
|
2015-10-06 18:42:12 -07:00
|
|
|
def matrix_to_string(matrix):
|
|
|
|
return "--".join(["-".join(map(str, row)) for row in matrix])
|
|
|
|
|
2015-10-06 19:33:40 -07:00
|
|
|
def matrix_mobject(matrix):
|
|
|
|
return text_mobject(
|
|
|
|
"""
|
|
|
|
\\left(
|
|
|
|
\\begin{array}{%s}
|
|
|
|
%d & %d \\\\
|
|
|
|
%d & %d
|
|
|
|
\\end{array}
|
|
|
|
\\right)
|
|
|
|
"""%tuple(["c"*matrix.shape[1]] + list(matrix.flatten())),
|
|
|
|
size = "\\Huge"
|
|
|
|
)
|
|
|
|
|
|
|
|
class ShowMultiplication(NumberLineScene):
|
|
|
|
args_list = [
|
|
|
|
(2, False),
|
|
|
|
(0.5, False),
|
|
|
|
(-3, False),
|
|
|
|
(-3, True),
|
|
|
|
(2, True),
|
|
|
|
]
|
|
|
|
@staticmethod
|
|
|
|
def args_to_string(num, show_original_line):
|
|
|
|
end_string = "WithCopiedOriginalLine" if show_original_line else ""
|
|
|
|
return str(num) + end_string
|
|
|
|
|
|
|
|
def construct(self, num, show_original_line):
|
|
|
|
NumberLineScene.construct(self, density = abs(num)*DEFAULT_POINT_DENSITY_1D)
|
|
|
|
if show_original_line:
|
|
|
|
self.copy_original_line()
|
|
|
|
kwargs = {
|
|
|
|
"run_time" : 2.0,
|
|
|
|
"interpolation_function" : straight_path if num > 0 else counterclockwise_path
|
|
|
|
}
|
|
|
|
self.dither()
|
|
|
|
new_number_line = deepcopy(self.number_line)
|
|
|
|
new_number_line.stretch(num, 0)
|
|
|
|
self.play(
|
|
|
|
Transform(self.number_line, new_number_line, **kwargs),
|
|
|
|
*[
|
|
|
|
ApplyFunction(
|
|
|
|
lambda m : m.do_in_place(m.stretch, 1.0/num, 0).stretch(num, 0),
|
|
|
|
mobject,
|
|
|
|
**kwargs
|
|
|
|
)
|
|
|
|
for mobject in self.number_mobs
|
|
|
|
]
|
|
|
|
)
|
|
|
|
self.dither()
|
|
|
|
|
2015-10-06 18:42:12 -07:00
|
|
|
|
2015-10-06 19:33:40 -07:00
|
|
|
def copy_original_line(self):
|
|
|
|
copied_line = deepcopy(self.number_line)
|
|
|
|
copied_num_mobs = deepcopy(self.number_mobs)
|
|
|
|
self.play(
|
|
|
|
ApplyFunction(
|
|
|
|
lambda m : m.shift(DOWN).highlight("green"),
|
|
|
|
copied_line
|
|
|
|
), *[
|
|
|
|
ApplyMethod(mob.shift, DOWN)
|
|
|
|
for mob in copied_num_mobs
|
|
|
|
]
|
|
|
|
)
|
|
|
|
self.dither()
|
2015-10-06 18:42:12 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
########################################################
|
|
|
|
|
2015-10-06 12:16:55 -07:00
|
|
|
class TransformScene2D(Scene):
|
2015-10-06 18:42:12 -07:00
|
|
|
def add_number_plane(self, density_factor, use_faded_lines = True):
|
|
|
|
config = {
|
|
|
|
"x_radius" : 2*SPACE_WIDTH,
|
|
|
|
"y_radius" : 2*SPACE_HEIGHT,
|
|
|
|
"density" : DEFAULT_POINT_DENSITY_1D*density_factor
|
|
|
|
}
|
|
|
|
if not use_faded_lines:
|
|
|
|
config["x_faded_line_frequency"] = None
|
|
|
|
config["y_faded_line_frequency"] = None
|
|
|
|
self.number_plane = NumberPlane(**config)
|
|
|
|
self.add(self.number_plane)
|
|
|
|
|
|
|
|
def add_background(self):
|
|
|
|
self.paint_into_background(
|
|
|
|
NumberPlane(color = "grey").add_coordinates()
|
|
|
|
)
|
|
|
|
|
|
|
|
def add_x_y_arrows(self):
|
|
|
|
self.x_arrow = Arrow(
|
|
|
|
ORIGIN,
|
|
|
|
self.number_plane.num_pair_to_point((1, 0)),
|
|
|
|
color = "green"
|
|
|
|
)
|
|
|
|
self.y_arrow = Arrow(
|
|
|
|
ORIGIN,
|
|
|
|
self.number_plane.num_pair_to_point((0, 1)),
|
|
|
|
color = "red"
|
|
|
|
)
|
|
|
|
self.add(self.x_arrow, self.y_arrow)
|
|
|
|
self.number_plane.filter_out(
|
2015-10-06 19:33:40 -07:00
|
|
|
lambda (x, y, z) : (0 < x) and (x < 1) and (abs(y) < 0.1)
|
2015-10-06 18:42:12 -07:00
|
|
|
)
|
|
|
|
self.number_plane.filter_out(
|
2015-10-06 19:33:40 -07:00
|
|
|
lambda (x, y, z) : (0 < y) and (y < 1) and (abs(x) < 0.1)
|
2015-10-06 18:42:12 -07:00
|
|
|
)
|
|
|
|
return self
|
|
|
|
|
2015-10-06 12:16:55 -07:00
|
|
|
|
2015-10-06 18:42:12 -07:00
|
|
|
class ShowMatrixTransform(TransformScene2D):
|
|
|
|
args_list = [
|
2015-10-06 19:33:40 -07:00
|
|
|
([[1, 2], [3, 4]], True, False),
|
|
|
|
([[1, 3], [-2, 0]], False, False),
|
|
|
|
([[1, 3], [-2, 0]], True, True),
|
|
|
|
([[0, -1], [1, 0]], True, False),
|
|
|
|
([[0, -1], [1, 0]], False, False),
|
|
|
|
([[-1, 0], [0, -1]], True, False),
|
|
|
|
([[-1, 0], [0, -1]], False, False),
|
2015-10-06 18:42:12 -07:00
|
|
|
]
|
|
|
|
@staticmethod
|
2015-10-06 19:33:40 -07:00
|
|
|
def args_to_string(matrix, with_background, show_matrix):
|
2015-10-06 18:42:12 -07:00
|
|
|
background_string = "WithBackground" if with_background else "WithoutBackground"
|
2015-10-06 19:33:40 -07:00
|
|
|
show_string = "ShowingMatrix" if show_matrix else ""
|
|
|
|
return matrix_to_string(matrix) + background_string + show_string
|
2015-10-06 18:42:12 -07:00
|
|
|
|
2015-10-06 19:33:40 -07:00
|
|
|
def construct(self, matrix, with_background, show_matrix):
|
2015-10-06 18:42:12 -07:00
|
|
|
matrix = np.array(matrix)
|
|
|
|
number_plane_config = {
|
|
|
|
"density_factor" : self.get_density_factor(matrix)
|
|
|
|
}
|
|
|
|
if with_background:
|
|
|
|
self.add_background()
|
|
|
|
number_plane_config["use_faded_lines"] = False
|
2015-10-06 19:33:40 -07:00
|
|
|
self.add_number_plane(**number_plane_config)
|
2015-10-06 18:42:12 -07:00
|
|
|
self.add_x_y_arrows()
|
2015-10-06 19:33:40 -07:00
|
|
|
else:
|
|
|
|
self.add_number_plane(**number_plane_config)
|
|
|
|
if show_matrix:
|
|
|
|
self.add(matrix_mobject(matrix).to_corner(UP+LEFT))
|
2015-10-06 18:42:12 -07:00
|
|
|
def func(mobject):
|
|
|
|
mobject.points[:, :2] = np.dot(mobject.points[:, :2], np.transpose(matrix))
|
|
|
|
return mobject
|
2015-10-06 19:33:40 -07:00
|
|
|
|
2015-10-06 12:16:55 -07:00
|
|
|
self.dither()
|
2015-10-06 18:42:12 -07:00
|
|
|
kwargs = {
|
|
|
|
"run_time" : 2.0,
|
|
|
|
"interpolation_function" : self.get_interpolation_function(matrix)
|
|
|
|
}
|
|
|
|
anims = [ApplyFunction(func, self.number_plane, **kwargs)]
|
|
|
|
if hasattr(self, "x_arrow") and hasattr(self, "y_arrow"):
|
|
|
|
for arrow, index in (self.x_arrow, 0), (self.y_arrow, 1):
|
|
|
|
new_arrow = Arrow(
|
|
|
|
ORIGIN,
|
|
|
|
self.number_plane.num_pair_to_point(matrix[:,index]),
|
|
|
|
color = arrow.get_color()
|
|
|
|
)
|
|
|
|
arrow.remove_tip()
|
|
|
|
new_arrow.remove_tip()
|
|
|
|
Mobject.align_data(arrow, new_arrow)
|
|
|
|
arrow.add_tip()
|
|
|
|
new_arrow.add_tip()
|
2015-10-06 19:33:40 -07:00
|
|
|
anims.append(Transform(arrow, new_arrow, **kwargs))
|
2015-10-06 18:42:12 -07:00
|
|
|
self.play(*anims)
|
2015-10-06 12:16:55 -07:00
|
|
|
self.dither()
|
|
|
|
|
2015-10-06 18:42:12 -07:00
|
|
|
def get_density_factor(self, matrix):
|
|
|
|
max_norm = max([
|
|
|
|
abs(np.linalg.norm(column))
|
|
|
|
for column in np.transpose(matrix)
|
|
|
|
])
|
|
|
|
return max(max_norm, 1)
|
|
|
|
|
|
|
|
def get_interpolation_function(self, matrix):
|
|
|
|
rotational_components = [
|
|
|
|
sign*np.arccos(matrix[i,i]/np.linalg.norm(matrix[:,i]))
|
|
|
|
for i in [0, 1]
|
|
|
|
for sign in [((-1)**i)*np.sign(matrix[1-i, i])]
|
|
|
|
]
|
|
|
|
average_rotation = sum(rotational_components)/2
|
|
|
|
if abs(average_rotation) < np.pi / 2:
|
|
|
|
return straight_path
|
|
|
|
elif average_rotation > 0:
|
|
|
|
return counterclockwise_path
|
|
|
|
else:
|
|
|
|
return clockwise_path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-10-06 12:16:55 -07:00
|
|
|
if __name__ == "__main__":
|
|
|
|
command_line_create_scene(MOVIE_PREFIX)
|