mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
Starting complex number animations
This commit is contained in:
parent
7564beb393
commit
c3cdafcfeb
11 changed files with 314 additions and 63 deletions
|
@ -30,6 +30,15 @@ def clockwise_path(start_points, end_points, alpha):
|
||||||
def counterclockwise_path(start_points, end_points, alpha):
|
def counterclockwise_path(start_points, end_points, alpha):
|
||||||
return semi_circular_path(start_points, end_points, alpha, OUT)
|
return semi_circular_path(start_points, end_points, alpha, OUT)
|
||||||
|
|
||||||
|
def get_best_interpolation_function(angle):
|
||||||
|
angle = (angle + np.pi)%(2*np.pi) - np.pi
|
||||||
|
if abs(angle) < np.pi/2:
|
||||||
|
return straight_path
|
||||||
|
elif angle > 0:
|
||||||
|
return counterclockwise_path
|
||||||
|
else:
|
||||||
|
return clockwise_path
|
||||||
|
|
||||||
class Transform(Animation):
|
class Transform(Animation):
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
"run_time" : DEFAULT_TRANSFORM_RUN_TIME,
|
"run_time" : DEFAULT_TRANSFORM_RUN_TIME,
|
||||||
|
|
|
@ -60,7 +60,7 @@ TEX_IMAGE_DIR = os.path.join(IMAGE_DIR, "Tex")
|
||||||
MOBJECT_DIR = os.path.join(FILE_DIR, "mobjects")
|
MOBJECT_DIR = os.path.join(FILE_DIR, "mobjects")
|
||||||
IMAGE_MOBJECT_DIR = os.path.join(MOBJECT_DIR, "image")
|
IMAGE_MOBJECT_DIR = os.path.join(MOBJECT_DIR, "image")
|
||||||
|
|
||||||
for folder in [IMAGE_DIR, GIF_DIR, MOVIE_DIR, TEX_DIR, TMP_IMAGE_DIR,
|
for folder in [IMAGE_DIR, GIF_DIR, MOVIE_DIR, TEX_DIR,
|
||||||
TEX_IMAGE_DIR, MOBJECT_DIR, IMAGE_MOBJECT_DIR]:
|
TEX_IMAGE_DIR, MOBJECT_DIR, IMAGE_MOBJECT_DIR]:
|
||||||
if not os.path.exists(folder):
|
if not os.path.exists(folder):
|
||||||
os.mkdir(folder)
|
os.mkdir(folder)
|
||||||
|
|
92
displayer.py
92
displayer.py
|
@ -49,49 +49,58 @@ def paint_mobjects(mobjects, image_array = None):
|
||||||
for mobject in mobjects:
|
for mobject in mobjects:
|
||||||
if mobject.get_num_points() == 0:
|
if mobject.get_num_points() == 0:
|
||||||
continue
|
continue
|
||||||
points, rgbs = place_on_screen(mobject.points, mobject.rgbs,
|
points_and_rgbs = np.append(
|
||||||
space_width, space_height)
|
mobject.points,
|
||||||
|
255*mobject.rgbs,
|
||||||
|
axis = 1
|
||||||
|
)
|
||||||
|
points_and_rgbs = place_on_screen(
|
||||||
|
points_and_rgbs,
|
||||||
|
space_width, space_height
|
||||||
|
)
|
||||||
#Map points to pixel space, which requires rescaling and shifting
|
#Map points to pixel space, which requires rescaling and shifting
|
||||||
#Remember, 2*space_height -> height
|
#Remember, 2*space_height -> height
|
||||||
points[:,0] = points[:,0]*width/space_width/2 + width/2
|
points_and_rgbs[:,0] = points_and_rgbs[:,0]*width/space_width/2 + width/2
|
||||||
#Flip on y-axis as you go
|
#Flip on y-axis as you go
|
||||||
points[:,1] = -1*points[:,1]*height/space_height/2 + height/2
|
points_and_rgbs[:,1] = -1*points_and_rgbs[:,1]*height/space_height/2 + height/2
|
||||||
points, rgbs = add_thickness(
|
points_and_rgbs = add_thickness(
|
||||||
points.astype('int'), rgbs,
|
points_and_rgbs.astype('int'),
|
||||||
mobject.point_thickness,
|
mobject.point_thickness,
|
||||||
width, height
|
width, height
|
||||||
)
|
)
|
||||||
|
|
||||||
|
points, rgbs = points_and_rgbs[:,:2], points_and_rgbs[:,2:]
|
||||||
flattener = np.array([[1], [width]], dtype = 'int')
|
flattener = np.array([[1], [width]], dtype = 'int')
|
||||||
indices = np.dot(points, flattener)[:,0]
|
indices = np.dot(points, flattener)[:,0]
|
||||||
pixels[indices] = (255*rgbs).astype('uint8')
|
pixels[indices] = rgbs.astype('uint8')
|
||||||
|
|
||||||
pixels = pixels.reshape((height, width, 3)).astype('uint8')
|
pixels = pixels.reshape((height, width, 3)).astype('uint8')
|
||||||
return pixels
|
return pixels
|
||||||
|
|
||||||
def add_thickness(pixel_indices, rgbs, thickness, width, height):
|
def add_thickness(pixel_indices_and_rgbs, thickness, width, height):
|
||||||
"""
|
"""
|
||||||
Imagine dragging each pixel around like a paintbrush in
|
Imagine dragging each pixel around like a paintbrush in
|
||||||
a plus-sign-shaped pixel arrangement surrounding it
|
a plus-sign-shaped pixel arrangement surrounding it.
|
||||||
|
|
||||||
|
Pass rgb = None to do nothing to them
|
||||||
"""
|
"""
|
||||||
thickness = adjusted_thickness(thickness, width, height)
|
thickness = adjusted_thickness(thickness, width, height)
|
||||||
original = np.array(pixel_indices)
|
original = np.array(pixel_indices_and_rgbs)
|
||||||
original_rgbs = np.array(rgbs)
|
n_extra_columns = pixel_indices_and_rgbs.shape[1] - 2
|
||||||
for nudge in range(-thickness/2+1, thickness/2+1):
|
for nudge in range(-thickness/2+1, thickness/2+1):
|
||||||
if nudge == 0:
|
if nudge == 0:
|
||||||
continue
|
continue
|
||||||
for x, y in [[nudge, 0], [0, nudge]]:
|
for x, y in [[nudge, 0], [0, nudge]]:
|
||||||
pixel_indices = np.append(
|
pixel_indices_and_rgbs = np.append(
|
||||||
pixel_indices,
|
pixel_indices_and_rgbs,
|
||||||
original+[x, y],
|
original+([x, y] + [0]*n_extra_columns),
|
||||||
axis = 0
|
axis = 0
|
||||||
)
|
)
|
||||||
rgbs = np.append(rgbs, original_rgbs, axis = 0)
|
admissibles = (pixel_indices_and_rgbs[:,0] >= 0) & \
|
||||||
admissibles = (pixel_indices[:,0] >= 0) & \
|
(pixel_indices_and_rgbs[:,0] < width) & \
|
||||||
(pixel_indices[:,0] < width) & \
|
(pixel_indices_and_rgbs[:,1] >= 0) & \
|
||||||
(pixel_indices[:,1] >= 0) & \
|
(pixel_indices_and_rgbs[:,1] < height)
|
||||||
(pixel_indices[:,1] < height)
|
return pixel_indices_and_rgbs[admissibles]
|
||||||
return pixel_indices[admissibles], rgbs[admissibles]
|
|
||||||
|
|
||||||
def adjusted_thickness(thickness, width, height):
|
def adjusted_thickness(thickness, width, height):
|
||||||
big_width = PRODUCTION_QUALITY_DISPLAY_CONFIG["width"]
|
big_width = PRODUCTION_QUALITY_DISPLAY_CONFIG["width"]
|
||||||
|
@ -99,38 +108,50 @@ def adjusted_thickness(thickness, width, height):
|
||||||
factor = (big_width + big_height) / (width + height)
|
factor = (big_width + big_height) / (width + height)
|
||||||
return 1 + (thickness-1)/factor
|
return 1 + (thickness-1)/factor
|
||||||
|
|
||||||
def place_on_screen(points, rgbs, space_width, space_height):
|
def place_on_screen(points_and_rgbs, space_width, space_height):
|
||||||
"""
|
"""
|
||||||
Projects points to 2d space and remove those outside a
|
Projects points to 2d space and remove those outside a
|
||||||
the space constraints
|
the space constraints.
|
||||||
|
|
||||||
|
Pass rbgs = None to do nothing to them.
|
||||||
"""
|
"""
|
||||||
# camera_distance = 10
|
# Remove 3rd column
|
||||||
points = np.array(points[:, :2])
|
points_and_rgbs = np.append(
|
||||||
# for i in range(2):
|
points_and_rgbs[:, :2],
|
||||||
# points[:,i] *= camera_distance/(camera_distance-mobject.points[:,2])
|
points_and_rgbs[:, 3:],
|
||||||
rgbs = np.array(rgbs)
|
axis = 1
|
||||||
|
)
|
||||||
|
|
||||||
#Removes points out of space
|
#Removes points out of space
|
||||||
to_keep = (abs(points[:,0]) < space_width) & \
|
to_keep = (abs(points_and_rgbs[:,0]) < space_width) & \
|
||||||
(abs(points[:,1]) < space_height)
|
(abs(points_and_rgbs[:,1]) < space_height)
|
||||||
return points[to_keep], rgbs[to_keep]
|
return points_and_rgbs[to_keep]
|
||||||
|
|
||||||
|
def get_file_path(name, extension):
|
||||||
|
file_path = os.path.join(MOVIE_DIR, name)
|
||||||
|
if not file_path.endswith(".mp4"):
|
||||||
|
file_path += ".mp4"
|
||||||
|
directory = os.path.split(file_path)[0]
|
||||||
|
if not os.path.exists(directory):
|
||||||
|
os.makedirs(directory)
|
||||||
|
return file_path
|
||||||
|
|
||||||
def write_to_gif(scene, name):
|
def write_to_gif(scene, name):
|
||||||
#TODO, find better means of compression
|
#TODO, find better means of compression
|
||||||
if not name.endswith(".gif"):
|
if not name.endswith(".gif"):
|
||||||
name += ".gif"
|
name += ".gif"
|
||||||
filepath = os.path.join(GIF_DIR, name)
|
file_path = os.path.join(GIF_DIR, name)
|
||||||
temppath = os.path.join(GIF_DIR, "Temp.gif")
|
temppath = os.path.join(GIF_DIR, "Temp.gif")
|
||||||
print "Writing " + name + "..."
|
print "Writing " + name + "..."
|
||||||
images = [Image.fromarray(frame) for frame in scene.frames]
|
images = [Image.fromarray(frame) for frame in scene.frames]
|
||||||
writeGif(temppath, images, scene.frame_duration)
|
writeGif(temppath, images, scene.frame_duration)
|
||||||
print "Compressing..."
|
print "Compressing..."
|
||||||
os.system("gifsicle -O " + temppath + " > " + filepath)
|
os.system("gifsicle -O " + temppath + " > " + file_path)
|
||||||
os.system("rm " + temppath)
|
os.system("rm " + temppath)
|
||||||
|
|
||||||
def write_to_movie(scene, name):
|
def write_to_movie(scene, name):
|
||||||
filepath = os.path.join(MOVIE_DIR, name) + ".mp4"
|
file_path = get_file_path(name, ".mp4")
|
||||||
print "Writing to %s"%filepath
|
print "Writing to %s"%file_path
|
||||||
|
|
||||||
fps = int(1/scene.display_config["frame_duration"])
|
fps = int(1/scene.display_config["frame_duration"])
|
||||||
dim = (scene.display_config["width"], scene.display_config["height"])
|
dim = (scene.display_config["width"], scene.display_config["height"])
|
||||||
|
@ -149,12 +170,11 @@ def write_to_movie(scene, name):
|
||||||
'-c:v', 'libx264',
|
'-c:v', 'libx264',
|
||||||
'-pix_fmt', 'yuv420p',
|
'-pix_fmt', 'yuv420p',
|
||||||
'-loglevel', 'error',
|
'-loglevel', 'error',
|
||||||
filepath,
|
file_path,
|
||||||
]
|
]
|
||||||
process = sp.Popen(command, stdin=sp.PIPE)
|
process = sp.Popen(command, stdin=sp.PIPE)
|
||||||
for frame in scene.frames:
|
for frame in scene.frames:
|
||||||
process.stdin.write(frame.tostring())
|
process.stdin.write(frame.tostring())
|
||||||
|
|
||||||
process.stdin.close()
|
process.stdin.close()
|
||||||
process.wait()
|
process.wait()
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import numpy as np
|
||||||
import itertools as it
|
import itertools as it
|
||||||
|
|
||||||
from mobject import Mobject, Mobject1D, Mobject2D, CompoundMobject
|
from mobject import Mobject, Mobject1D, Mobject2D, CompoundMobject
|
||||||
|
from simple_mobjects import Arrow, Line
|
||||||
from image_mobject import tex_mobject
|
from image_mobject import tex_mobject
|
||||||
from constants import *
|
from constants import *
|
||||||
from helpers import *
|
from helpers import *
|
||||||
|
@ -255,9 +256,65 @@ class NumberPlane(Mobject1D):
|
||||||
self.add(*self.get_coordinate_labels(x_vals, y_vals))
|
self.add(*self.get_coordinate_labels(x_vals, y_vals))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def get_vector(self, coords, **kwargs):
|
||||||
|
if len(coords) == 2:
|
||||||
|
coords = tuple(list(coords) + [0])
|
||||||
|
arrow = Arrow(ORIGIN, coords, **kwargs)
|
||||||
|
arrow.remove_tip()
|
||||||
|
arrow.align_data(Line(ORIGIN, SPACE_WIDTH*LEFT))
|
||||||
|
arrow.add_tip()
|
||||||
|
return arrow
|
||||||
|
|
||||||
class ComplexPlane(NumberPlane):
|
class ComplexPlane(NumberPlane):
|
||||||
#TODO
|
DEFAULT_CONFIG = {
|
||||||
pass
|
"color" : "lightgreen",
|
||||||
|
"unit_to_spatial_width" : 1,
|
||||||
|
"line_frequency" : 1,
|
||||||
|
"faded_line_frequency" : 0.5,
|
||||||
|
"number_at_center" : complex(0),
|
||||||
|
}
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
digest_config(self, ComplexPlane, kwargs)
|
||||||
|
kwargs.update({
|
||||||
|
"x_unit_to_spatial_width" : self.unit_to_spatial_width,
|
||||||
|
"y_uint_to_spatial_height" : self.unit_to_spatial_width,
|
||||||
|
"x_line_frequency" : self.line_frequency,
|
||||||
|
"x_faded_line_frequency" : self.faded_line_frequency,
|
||||||
|
"y_line_frequency" : self.line_frequency,
|
||||||
|
"y_faded_line_frequency" : self.faded_line_frequency,
|
||||||
|
"num_pair_at_center" : (self.number_at_center.real, self.number_at_center.imag),
|
||||||
|
})
|
||||||
|
NumberPlane.__init__(self, **kwargs)
|
||||||
|
|
||||||
|
def number_to_point(self, number):
|
||||||
|
number = complex(number)
|
||||||
|
return self.num_pair_to_point((number.real, number.imag))
|
||||||
|
|
||||||
|
def get_coordinate_labels(self, *numbers):
|
||||||
|
result = []
|
||||||
|
nudge = 0.1*(DOWN+RIGHT)
|
||||||
|
if len(numbers) == 0:
|
||||||
|
numbers = range(-int(self.x_radius), int(self.x_radius))
|
||||||
|
numbers += [
|
||||||
|
complex(0, y)
|
||||||
|
for y in range(-int(self.y_radius), int(self.y_radius))
|
||||||
|
]
|
||||||
|
for number in numbers:
|
||||||
|
point = self.number_to_point(number)
|
||||||
|
if number == 0:
|
||||||
|
num_str = "0"
|
||||||
|
else:
|
||||||
|
num_str = str(number).replace("j", "i")
|
||||||
|
num = tex_mobject(num_str)
|
||||||
|
num.scale(self.number_scale_factor)
|
||||||
|
num.shift(point-num.get_corner(UP+LEFT)+nudge)
|
||||||
|
result.append(num)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def add_coordinates(self, *numbers):
|
||||||
|
self.add(*self.get_coordinate_labels(*numbers))
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -321,18 +321,17 @@ class Mobject(object):
|
||||||
#Typically implemented in subclass, unless purposefully left blank
|
#Typically implemented in subclass, unless purposefully left blank
|
||||||
pass
|
pass
|
||||||
|
|
||||||
### Static Methods ###
|
def align_data(self, mobject):
|
||||||
def align_data(mobject1, mobject2):
|
count1, count2 = self.get_num_points(), mobject.get_num_points()
|
||||||
count1, count2 = mobject1.get_num_points(), mobject2.get_num_points()
|
|
||||||
if count1 == 0:
|
if count1 == 0:
|
||||||
mobject1.add_points([(0, 0, 0)])
|
self.add_points([(0, 0, 0)])
|
||||||
if count2 == 0:
|
if count2 == 0:
|
||||||
mobject2.add_points([(0, 0, 0)])
|
mobject.add_points([(0, 0, 0)])
|
||||||
if count1 == count2:
|
if count1 == count2:
|
||||||
return
|
return
|
||||||
for attr in ['points', 'rgbs']:
|
for attr in ['points', 'rgbs']:
|
||||||
new_arrays = make_even(getattr(mobject1, attr), getattr(mobject2, attr))
|
new_arrays = make_even(getattr(self, attr), getattr(mobject, attr))
|
||||||
for array, mobject in zip(new_arrays, [mobject1, mobject2]):
|
for array, mobject in zip(new_arrays, [self, mobject]):
|
||||||
setattr(mobject, attr, np.array(array))
|
setattr(mobject, attr, np.array(array))
|
||||||
|
|
||||||
def interpolate(mobject1, mobject2, target_mobject, alpha):
|
def interpolate(mobject1, mobject2, target_mobject, alpha):
|
||||||
|
@ -342,9 +341,10 @@ class Mobject(object):
|
||||||
"""
|
"""
|
||||||
Mobject.align_data(mobject1, mobject2)
|
Mobject.align_data(mobject1, mobject2)
|
||||||
for attr in ['points', 'rgbs']:
|
for attr in ['points', 'rgbs']:
|
||||||
new_array = (1 - alpha) * getattr(mobject1, attr) + \
|
setattr(target_mobject, attr, interpolate(
|
||||||
alpha * getattr(mobject2, attr)
|
getattr(mobject1, attr),
|
||||||
setattr(target_mobject, attr, new_array)
|
getattr(mobject2, attr),
|
||||||
|
alpha))
|
||||||
|
|
||||||
#TODO, Make the two implementations bellow not redundant
|
#TODO, Make the two implementations bellow not redundant
|
||||||
class Mobject1D(Mobject):
|
class Mobject1D(Mobject):
|
||||||
|
|
|
@ -16,9 +16,10 @@ from script_wrapper import command_line_create_scene
|
||||||
|
|
||||||
class SampleScene(Scene):
|
class SampleScene(Scene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
circle = Circle().repeat(6)
|
c = ComplexPlane().add_coordinates()
|
||||||
self.play(Transform(circle, Square(), run_time = 3))
|
|
||||||
self.dither()
|
self.add_local_mobjects()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -51,7 +51,7 @@ class Scene(object):
|
||||||
return self.name
|
return self.name
|
||||||
return self.__class__.__name__ + \
|
return self.__class__.__name__ + \
|
||||||
self.args_to_string(*self.construct_args)
|
self.args_to_string(*self.construct_args)
|
||||||
|
|
||||||
def set_name(self, name):
|
def set_name(self, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
return self
|
return self
|
||||||
|
@ -62,6 +62,8 @@ class Scene(object):
|
||||||
in the order with which they are entered.
|
in the order with which they are entered.
|
||||||
"""
|
"""
|
||||||
for mobject in mobjects:
|
for mobject in mobjects:
|
||||||
|
if not isinstance(mobject, Mobject):
|
||||||
|
raise Exception("Adding something which is not a mobject")
|
||||||
#In case it's already in there, it should
|
#In case it's already in there, it should
|
||||||
#now be closer to the foreground.
|
#now be closer to the foreground.
|
||||||
self.remove(mobject)
|
self.remove(mobject)
|
||||||
|
@ -86,6 +88,15 @@ class Scene(object):
|
||||||
self.mobjects.remove(mobject)
|
self.mobjects.remove(mobject)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def bring_to_front(self, mobject):
|
||||||
|
self.add(mobject)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def bring_to_back(self, mobject):
|
||||||
|
self.remove(mobject)
|
||||||
|
self.mobjects = [mobject] + self.mobjects
|
||||||
|
return self
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
self.reset_background()
|
self.reset_background()
|
||||||
self.remove(*self.mobjects)
|
self.remove(*self.mobjects)
|
||||||
|
@ -174,11 +185,8 @@ class Scene(object):
|
||||||
animation.clean_up()
|
animation.clean_up()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def apply(self, mob_to_anim_func, **kwargs):
|
def apply(self, mobject_method, *args, **kwargs):
|
||||||
self.play(*[
|
self.play(ApplyMethod(mobject_method, *args, **kwargs))
|
||||||
mob_to_anim_func(mobject)
|
|
||||||
for mobject in self.mobjects
|
|
||||||
])
|
|
||||||
|
|
||||||
def get_frame(self):
|
def get_frame(self):
|
||||||
return disp.paint_mobjects(self.mobjects, self.background)
|
return disp.paint_mobjects(self.mobjects, self.background)
|
||||||
|
|
|
@ -7,7 +7,7 @@ import time
|
||||||
class TkSceneRoot(Tkinter.Tk):
|
class TkSceneRoot(Tkinter.Tk):
|
||||||
def __init__(self, scene):
|
def __init__(self, scene):
|
||||||
if scene.frames == []:
|
if scene.frames == []:
|
||||||
raise str(scene) + " has no frames!"
|
raise Exception(str(scene) + " has no frames!")
|
||||||
Tkinter.Tk.__init__(self)
|
Tkinter.Tk.__init__(self)
|
||||||
|
|
||||||
height, width = scene.shape
|
height, width = scene.shape
|
||||||
|
|
|
@ -66,7 +66,7 @@ def get_configuration(sys_argv):
|
||||||
if len(args) > 0:
|
if len(args) > 0:
|
||||||
config["scene_name"] = args[0]
|
config["scene_name"] = args[0]
|
||||||
if len(args) > 1:
|
if len(args) > 1:
|
||||||
config["args_extension"] = args[1]
|
config["args_extension"] = " ".join(args[1:])
|
||||||
return config
|
return config
|
||||||
|
|
||||||
def handle_scene(scene, **config):
|
def handle_scene(scene, **config):
|
||||||
|
@ -128,13 +128,13 @@ def command_line_create_scene(movie_prefix = ""):
|
||||||
"announce_construction" : True
|
"announce_construction" : True
|
||||||
}
|
}
|
||||||
for SceneClass in scene_classes:
|
for SceneClass in scene_classes:
|
||||||
args_list = SceneClass.args_list or [()]
|
args_list = SceneClass.args_list
|
||||||
preset_extensions = [
|
preset_extensions = [
|
||||||
SceneClass.args_to_string(*args)
|
SceneClass.args_to_string(*args)
|
||||||
for args in args_list
|
for args in args_list
|
||||||
]
|
]
|
||||||
if config["write_all"]:
|
if config["write_all"]:
|
||||||
args_to_run = args_list
|
args_to_run = args_list or [()]
|
||||||
elif config["args_extension"] in preset_extensions:
|
elif config["args_extension"] in preset_extensions:
|
||||||
index = preset_extensions.index(config["args_extension"])
|
index = preset_extensions.index(config["args_extension"])
|
||||||
args_to_run = [args_list[index]]
|
args_to_run = [args_list[index]]
|
||||||
|
|
157
scripts/complex_actions.py
Normal file
157
scripts/complex_actions.py
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
#!/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 *
|
||||||
|
from scene import Scene
|
||||||
|
from script_wrapper import command_line_create_scene
|
||||||
|
|
||||||
|
MOVIE_PREFIX = "complex_actions/"
|
||||||
|
|
||||||
|
class ComplexMultiplication(Scene):
|
||||||
|
@staticmethod
|
||||||
|
def args_to_string(multiplier):
|
||||||
|
return filter(lambda c : c not in "()", str(multiplier))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def string_to_args(num_string):
|
||||||
|
return complex(num_string)
|
||||||
|
|
||||||
|
def construct(self, multiplier):
|
||||||
|
norm = np.linalg.norm(multiplier)
|
||||||
|
arg = np.log(multiplier).imag
|
||||||
|
plane_config = {
|
||||||
|
"faded_line_frequency" : 0
|
||||||
|
}
|
||||||
|
if norm > 1:
|
||||||
|
plane_config["density"] = norm*DEFAULT_POINT_DENSITY_1D
|
||||||
|
radius = SPACE_WIDTH
|
||||||
|
if norm > 0 and norm < 1:
|
||||||
|
radius /= norm
|
||||||
|
plane_config["x_radius"] = plane_config["y_radius"] = radius
|
||||||
|
plane = ComplexPlane(**plane_config)
|
||||||
|
self.anim_config = {
|
||||||
|
"run_time" : 2.0,
|
||||||
|
"interpolation_function" : get_best_interpolation_function(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
background = ComplexPlane(color = "grey")
|
||||||
|
labels = background.get_coordinate_labels()
|
||||||
|
self.paint_into_background(background, *labels)
|
||||||
|
arrow, new_arrow = [
|
||||||
|
plane.get_vector(plane.number_to_point(z), color = "skyblue")
|
||||||
|
for z in [1, multiplier]
|
||||||
|
]
|
||||||
|
self.add(arrow)
|
||||||
|
self.additional_animations = [Transform(
|
||||||
|
arrow, new_arrow, **self.anim_config
|
||||||
|
)]
|
||||||
|
|
||||||
|
self.mobjects_to_multiply = [plane]
|
||||||
|
self.mobjects_to_move_without_molding = []
|
||||||
|
self.multiplier = multiplier
|
||||||
|
self.plane = plane
|
||||||
|
if self.__class__ == ComplexMultiplication:
|
||||||
|
self.apply_multiplication()
|
||||||
|
|
||||||
|
def apply_multiplication(self):
|
||||||
|
def func((x, y, z)):
|
||||||
|
complex_num = self.multiplier*complex(x, y)
|
||||||
|
return (complex_num.real, complex_num.imag, z)
|
||||||
|
mobjects = self.mobjects_to_multiply+self.mobjects_to_move_without_molding
|
||||||
|
mobjects += [anim.mobject for anim in self.additional_animations]
|
||||||
|
self.add(*mobjects)
|
||||||
|
full_multiplications = [
|
||||||
|
ApplyMethod(mobject.apply_function, func, **self.anim_config)
|
||||||
|
for mobject in self.mobjects_to_multiply
|
||||||
|
]
|
||||||
|
movements_with_plane = [
|
||||||
|
ApplyMethod(
|
||||||
|
mobject.shift,
|
||||||
|
func(mobject.get_center())-mobject.get_center(),
|
||||||
|
**self.anim_config
|
||||||
|
)
|
||||||
|
for mobject in self.mobjects_to_move_without_molding
|
||||||
|
]
|
||||||
|
self.dither()
|
||||||
|
self.play(*reduce(op.add, [
|
||||||
|
full_multiplications,
|
||||||
|
movements_with_plane,
|
||||||
|
self.additional_animations
|
||||||
|
]))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class MultiplicationWithDot(ComplexMultiplication):
|
||||||
|
@staticmethod
|
||||||
|
def args_to_string(multiplier, dot_coords):
|
||||||
|
start = ComplexMultiplication.args_to_string(multiplier)
|
||||||
|
return start + "WithDotAt%d-%d"%dot_coords[:2]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def string_to_args(arg_string):
|
||||||
|
parts = arg_string.split()
|
||||||
|
if len(parts) < 2 or len(parts) > 3:
|
||||||
|
raise Exception("Invalid arguments")
|
||||||
|
multiplier = complex(parts[0])
|
||||||
|
tup_string = filter(lambda c : c not in "()", parts[1])
|
||||||
|
nums = tuple(map(int, tup_string.split(",")))[:2]
|
||||||
|
return multiplier, nums
|
||||||
|
|
||||||
|
def construct(self, multiplier, dot_coords):
|
||||||
|
ComplexMultiplication.construct(self, multiplier)
|
||||||
|
self.mobjects_to_move_without_molding.append(
|
||||||
|
Dot().shift(dot_coords)
|
||||||
|
)
|
||||||
|
self.apply_multiplication()
|
||||||
|
|
||||||
|
|
||||||
|
class ShowComplexPower(ComplexMultiplication):
|
||||||
|
@staticmethod
|
||||||
|
def args_to_string(multiplier, num_repeats):
|
||||||
|
start = ComplexMultiplication.args_to_string(multiplier)
|
||||||
|
return start + "ToThe%d"%num_repeats
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def string_to_args(arg_string):
|
||||||
|
parts = arg_string.split()
|
||||||
|
if len(parts) < 2 or len(parts) > 3:
|
||||||
|
raise Exception("Invalid arguments")
|
||||||
|
multiplier = complex(parts[0])
|
||||||
|
num_repeats = int(parts[1])
|
||||||
|
return multiplier, num_repeats
|
||||||
|
|
||||||
|
def construct(self, multiplier, num_repeats):
|
||||||
|
ComplexMultiplication.construct(self, multiplier)
|
||||||
|
for x in range(num_repeats):
|
||||||
|
arrow_transform = Transform(*[
|
||||||
|
self.plane.get_vector(point, color = "skyblue")
|
||||||
|
for z in [multiplier**(x), multiplier**(x+1)]
|
||||||
|
for point in [self.plane.number_to_point(z)]
|
||||||
|
], **self.anim_config)
|
||||||
|
self.remove(*[anim.mobject for anim in self.additional_animations])
|
||||||
|
self.additional_animations = [arrow_transform]
|
||||||
|
self.apply_multiplication()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
command_line_create_scene(MOVIE_PREFIX)
|
|
@ -224,7 +224,6 @@ class ShowMatrixTransform(TransformScene2D):
|
||||||
self.add_x_y_arrows()
|
self.add_x_y_arrows()
|
||||||
else:
|
else:
|
||||||
self.add_number_plane(**number_plane_config)
|
self.add_number_plane(**number_plane_config)
|
||||||
self.save_image()
|
|
||||||
if show_matrix:
|
if show_matrix:
|
||||||
self.add(matrix_mobject(matrix).to_corner(UP+LEFT))
|
self.add(matrix_mobject(matrix).to_corner(UP+LEFT))
|
||||||
def func(mobject):
|
def func(mobject):
|
||||||
|
|
Loading…
Add table
Reference in a new issue