mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
Redid digest_config and introduced digest_locals
This commit is contained in:
parent
2e074afb60
commit
468d05d049
22 changed files with 185 additions and 193 deletions
|
@ -1,4 +1,4 @@
|
|||
from animation import *
|
||||
from meta_animations import *
|
||||
from simple_animations import *
|
||||
from transform import *
|
||||
from transform import *
|
||||
|
|
|
@ -9,8 +9,7 @@ import progressbar
|
|||
import inspect
|
||||
|
||||
from helpers import *
|
||||
from mobject import Mobject
|
||||
from topics.geometry import Point
|
||||
from mobject import Mobject, Point
|
||||
|
||||
class Animation(object):
|
||||
DEFAULT_CONFIG = {
|
||||
|
@ -21,7 +20,7 @@ class Animation(object):
|
|||
def __init__(self, mobject, **kwargs):
|
||||
mobject = instantiate(mobject)
|
||||
assert(isinstance(mobject, Mobject))
|
||||
digest_config(self, Animation, kwargs, locals())
|
||||
digest_config(self, kwargs, locals())
|
||||
self.starting_mobject = copy.deepcopy(self.mobject)
|
||||
if self.alpha_func is None:
|
||||
self.alpha_func = (lambda x : x)
|
||||
|
|
|
@ -18,12 +18,12 @@ class DelayByOrder(Animation):
|
|||
"max_power" : 5
|
||||
}
|
||||
def __init__(self, animation, **kwargs):
|
||||
digest_config(self, DelayByOrder, kwargs, locals())
|
||||
digest_locals(self)
|
||||
self.num_mobject_points = animation.mobject.get_num_points()
|
||||
kwargs.update(dict([
|
||||
(attr, getattr(animation, attr))
|
||||
for attr in Animation.DEFAULT_CONFIG
|
||||
]))
|
||||
self.num_mobject_points = animation.mobject.get_num_points()
|
||||
Animation.__init__(self, animation.mobject, **kwargs)
|
||||
self.name = self.__class__.__name__ + str(self.animation)
|
||||
|
||||
|
@ -44,14 +44,13 @@ class TransformAnimations(Transform):
|
|||
"alpha_func" : squish_alpha_func(smooth)
|
||||
}
|
||||
def __init__(self, start_anim, end_anim, **kwargs):
|
||||
digest_config(self, TransformAnimations, kwargs, locals())
|
||||
if "run_time" in kwargs:
|
||||
self.run_time = kwargs.pop("run_time")
|
||||
else:
|
||||
self.run_time = max(start_anim.run_time, end_anim.run_time)
|
||||
for anim in start_anim, end_anim:
|
||||
anim.set_run_time(self.run_time)
|
||||
|
||||
|
||||
if start_anim.starting_mobject.get_num_points() != end_anim.starting_mobject.get_num_points():
|
||||
Mobject.align_data(start_anim.starting_mobject, end_anim.starting_mobject)
|
||||
for anim in start_anim, end_anim:
|
||||
|
|
|
@ -16,10 +16,6 @@ class Rotating(Animation):
|
|||
"run_time" : 20.0,
|
||||
"alpha_func" : None,
|
||||
}
|
||||
def __init__(self, mobject, **kwargs):
|
||||
digest_config(self, Rotating, kwargs, locals())
|
||||
Animation.__init__(self, mobject, **kwargs)
|
||||
|
||||
def update_mobject(self, alpha):
|
||||
self.mobject.points = self.starting_mobject.points
|
||||
for axis in self.axes:
|
||||
|
@ -61,7 +57,6 @@ class Flash(Animation):
|
|||
"alpha_func" : None,
|
||||
}
|
||||
def __init__(self, mobject, **kwargs):
|
||||
digest_config(self, Flash, kwargs, locals())
|
||||
self.intermediate = Mobject(color = self.color)
|
||||
self.intermediate.add_points([
|
||||
point + (x, y, 0)
|
||||
|
@ -82,12 +77,12 @@ class Flash(Animation):
|
|||
)
|
||||
|
||||
class Homotopy(Animation):
|
||||
def __init__(self, homotopy, **kwargs):
|
||||
def __init__(self, homotopy, mobject, **kwargs):
|
||||
"""
|
||||
Homotopy a function from (x, y, z, t) to (x', y', z')
|
||||
"""
|
||||
digest_config(self, Homotopy, kwargs, locals())
|
||||
Animation.__init__(self, **kwargs)
|
||||
digest_locals(self)
|
||||
Animation.__init__(self, mobject, **kwargs)
|
||||
|
||||
def update_mobject(self, alpha):
|
||||
self.mobject.points = np.array([
|
||||
|
|
|
@ -7,9 +7,7 @@ import warnings
|
|||
from helpers import *
|
||||
|
||||
from animation import Animation
|
||||
from mobject import Mobject
|
||||
from topics.geometry import Point
|
||||
from topics.complex_numbers import ComplexPlane
|
||||
from mobject import Mobject, Point
|
||||
|
||||
class Transform(Animation):
|
||||
DEFAULT_CONFIG = {
|
||||
|
@ -18,7 +16,7 @@ class Transform(Animation):
|
|||
}
|
||||
def __init__(self, mobject, ending_mobject, **kwargs):
|
||||
mobject, ending_mobject = map(instantiate, [mobject, ending_mobject])
|
||||
digest_config(self, Transform, kwargs, locals())
|
||||
digest_config(self, kwargs, locals())
|
||||
count1, count2 = mobject.get_num_points(), ending_mobject.get_num_points()
|
||||
if count2 == 0:
|
||||
ending_mobject.add_points([SPACE_WIDTH*RIGHT+SPACE_HEIGHT*UP])
|
||||
|
@ -73,24 +71,17 @@ class ClockwiseTransform(Transform):
|
|||
DEFAULT_CONFIG = {
|
||||
"interpolation_function" : clockwise_path()
|
||||
}
|
||||
def __init__(self, *args, **kwargs):
|
||||
digest_config(self, ClockwiseTransform, kwargs)
|
||||
Transform.__init__(self, *args, **kwargs)
|
||||
|
||||
class CounterclockwiseTransform(Transform):
|
||||
DEFAULT_CONFIG = {
|
||||
"interpolation_function" : counterclockwise_path()
|
||||
}
|
||||
def __init__(self, *args, **kwargs):
|
||||
digest_config(self, ClockwiseTransform, kwargs)
|
||||
Transform.__init__(self, *args, **kwargs)
|
||||
|
||||
class SpinInFromNothing(Transform):
|
||||
DEFAULT_CONFIG = {
|
||||
"interpolation_function" : counterclockwise_path()
|
||||
}
|
||||
def __init__(self, mob, **kwargs):
|
||||
digest_config(self, SpinInFromNothing, kwargs)
|
||||
Transform.__init__(self, Point(mob.get_center()), mob, **kwargs)
|
||||
|
||||
class ApplyMethod(Transform):
|
||||
|
@ -121,7 +112,6 @@ class ApplyPointwiseFunction(ApplyMethod):
|
|||
"run_time" : DEFAULT_POINTWISE_FUNCTION_RUN_TIME
|
||||
}
|
||||
def __init__(self, function, mobject, **kwargs):
|
||||
digest_config(self, ApplyPointwiseFunction, kwargs)
|
||||
ApplyMethod.__init__(
|
||||
self, mobject.apply_function, function, **kwargs
|
||||
)
|
||||
|
|
|
@ -12,23 +12,31 @@ from helpers import *
|
|||
from scene import Scene
|
||||
|
||||
HELP_MESSAGE = """
|
||||
<script name> [<scene name or initials>] [<arg_string>]
|
||||
Usage:
|
||||
python extract_scene.py <module> [<scene name>] [<arg_string>]
|
||||
|
||||
-p preview in low quality
|
||||
-s show and save picture of last frame
|
||||
-w write result to file [this is default if nothing else is stated]
|
||||
-l use low quality
|
||||
-a run and save every scene in the script
|
||||
-m use medium quality
|
||||
-a run and save every scene in the script, or all args for the given scene
|
||||
-q don't pring progress
|
||||
"""
|
||||
SCENE_NOT_FOUND_MESSAGE = """
|
||||
That scene is not in the script
|
||||
"""
|
||||
CHOOSE_NUMBER_MESSAGE = """
|
||||
Choose number corresponding to desired scene arguments.
|
||||
Choose number corresponding to desired scene/arguments.
|
||||
(Use comma separated list for multiple entries)
|
||||
|
||||
Choice(s):"""
|
||||
Choice(s): """
|
||||
INVALID_NUMBER_MESSAGE = "Fine then, if you don't want to give a valid number I'll just quit"
|
||||
|
||||
NO_SCENE_MESSAGE = """
|
||||
There are no scenes inside that module
|
||||
"""
|
||||
|
||||
|
||||
def get_configuration(sys_argv):
|
||||
try:
|
||||
|
@ -72,8 +80,10 @@ def get_configuration(sys_argv):
|
|||
if not any([config[key] for key in actions]):
|
||||
config["write"] = True
|
||||
|
||||
if len(args) > 0:
|
||||
config["module"] = args[0]
|
||||
if len(args) == 0:
|
||||
print HELP_MESSAGE
|
||||
sys.exit()
|
||||
config["module"] = args[0].replace(".py", "")
|
||||
if len(args) > 1:
|
||||
config["scene_name"] = args[1]
|
||||
if len(args) > 2:
|
||||
|
@ -110,16 +120,16 @@ def is_scene(obj):
|
|||
return False
|
||||
return True
|
||||
|
||||
def prompt_user_for_args(args_list, args_to_string):
|
||||
num_to_args = {}
|
||||
for count, args in zip(it.count(1), args_list):
|
||||
print "%d: %s"%(count, args_to_string(*args))
|
||||
num_to_args[count] = args
|
||||
def prompt_user_for_choice(name_to_obj):
|
||||
num_to_name = {}
|
||||
for count, name in zip(it.count(1), name_to_obj):
|
||||
print "%d: %s"%(count, name)
|
||||
num_to_name[count] = name
|
||||
try:
|
||||
choice = raw_input(CHOOSE_NUMBER_MESSAGE)
|
||||
user_input = raw_input(CHOOSE_NUMBER_MESSAGE)
|
||||
return [
|
||||
num_to_args[int(num_str)]
|
||||
for num_str in choice.split(",")
|
||||
name_to_obj[num_to_name[int(num_str)]]
|
||||
for num_str in user_input.split(",")
|
||||
]
|
||||
except:
|
||||
print INVALID_NUMBER_MESSAGE
|
||||
|
@ -139,38 +149,49 @@ def get_scene_args(SceneClass, config):
|
|||
|
||||
if num_args == 0:
|
||||
return [()]
|
||||
elif config["write_all"]:
|
||||
if config["write_all"]:
|
||||
return args_list
|
||||
elif config["args_extension"] in preset_extensions:
|
||||
if config["args_extension"] in preset_extensions:
|
||||
index = preset_extensions.index(config["args_extension"])
|
||||
return [args_list[index]]
|
||||
elif len(args_list) == 1:
|
||||
return [args_list[0]]
|
||||
elif config["args_extension"] == "" :
|
||||
return prompt_user_for_args(args_list, SceneClass.args_to_string)
|
||||
else:
|
||||
return [SceneClass.string_to_args(config["args_extension"])]
|
||||
if config["args_extension"] == "" :
|
||||
name_to_args = dict(zip(preset_extensions, args_list))
|
||||
return prompt_user_for_choice(name_to_args)
|
||||
if len(args_list) == 1:
|
||||
return args_list
|
||||
return [SceneClass.string_to_args(config["args_extension"])]
|
||||
|
||||
def get_scene_classes(scene_names_to_classes, config):
|
||||
if len(scene_names_to_classes) == 0:
|
||||
print NO_SCENE_MESSAGE
|
||||
return []
|
||||
if len(scene_names_to_classes) == 1:
|
||||
return scene_names_to_classes.values()
|
||||
if config["scene_name"] in scene_names_to_classes:
|
||||
return [scene_names_to_classes[config["scene_name"]] ]
|
||||
if config["scene_name"] != "":
|
||||
print SCENE_NOT_FOUND_MESSAGE
|
||||
return []
|
||||
if config["write_all"]:
|
||||
return scene_names_to_classes.values()
|
||||
return prompt_user_for_choice(scene_names_to_classes)
|
||||
|
||||
|
||||
def main():
|
||||
config = get_configuration(sys.argv)
|
||||
module = imp.load_source(config["module_name"], ".")
|
||||
module = imp.load_module(
|
||||
config["module"],
|
||||
*imp.find_module(config["module"])
|
||||
)
|
||||
scene_names_to_classes = dict(
|
||||
inspect.getmembers(module, is_scene)
|
||||
)
|
||||
config["movie_prefix"] = config["module_name"].split(".py")[0]
|
||||
if config["scene_name"] in scene_names_to_classes:
|
||||
scene_classes = [scene_names_to_classes[config["scene_name"]] ]
|
||||
elif config["scene_name"] == "" and config["write_all"]:
|
||||
scene_classes = scene_names_to_classes.values()
|
||||
else:
|
||||
print SCENE_NOT_FOUND_MESSAGE
|
||||
return
|
||||
|
||||
config["movie_prefix"] = config["module"]
|
||||
scene_kwargs = {
|
||||
"display_config" : config["display_config"],
|
||||
"announce_construction" : True
|
||||
}
|
||||
for SceneClass in scene_classes:
|
||||
for SceneClass in get_scene_classes(scene_names_to_classes, config):
|
||||
for args in get_scene_args(SceneClass, config):
|
||||
scene_kwargs["construct_args"] = args
|
||||
try:
|
||||
|
|
49
helpers.py
49
helpers.py
|
@ -1,11 +1,13 @@
|
|||
import numpy as np
|
||||
import itertools as it
|
||||
import operator as op
|
||||
from PIL import Image
|
||||
from colour import Color
|
||||
from random import random
|
||||
import inspect
|
||||
import string
|
||||
import re
|
||||
import operator as op
|
||||
|
||||
|
||||
from constants import *
|
||||
|
||||
|
@ -24,24 +26,37 @@ def instantiate(obj):
|
|||
return obj() if isinstance(obj, type) else obj
|
||||
|
||||
|
||||
def digest_config(obj, Class, kwargs, local_args = {}):
|
||||
def filtered_locals(local_args):
|
||||
result = local_args.copy()
|
||||
ignored_local_args = ["self", "kwargs"]
|
||||
for arg in ignored_local_args:
|
||||
result.pop(arg, local_args)
|
||||
return result
|
||||
|
||||
|
||||
def digest_config(obj, kwargs, local_args = {}):
|
||||
"""
|
||||
To be used in initializing most-to-all objects.
|
||||
Sets key word args as local variables
|
||||
Sets init args and DEFAULT_CONFIG values as local variables
|
||||
"""
|
||||
if hasattr(Class, "DEFAULT_CONFIG"):
|
||||
config = Class.DEFAULT_CONFIG.copy()
|
||||
else:
|
||||
config = {}
|
||||
for key in config.keys():
|
||||
if hasattr(obj, key):
|
||||
config.pop(key)
|
||||
if key in kwargs:
|
||||
config[key] = kwargs.pop(key)
|
||||
for key in local_args:
|
||||
if key not in ["self", "kwargs"]:
|
||||
config[key] = local_args[key]
|
||||
obj.__dict__.update(config)
|
||||
### Assemble list of DEFAULT_CONFIGs from all super classes
|
||||
classes_in_heirarchy = [obj.__class__]
|
||||
default_configs = []
|
||||
while len(classes_in_heirarchy) > 0:
|
||||
Class = classes_in_heirarchy.pop()
|
||||
classes_in_heirarchy += Class.__bases__
|
||||
if hasattr(Class, "DEFAULT_CONFIG"):
|
||||
default_configs.append(Class.DEFAULT_CONFIG)
|
||||
|
||||
#Order matters a lot here, first dicts have higher priority
|
||||
all_dicts = [kwargs, filtered_locals(local_args), obj.__dict__]
|
||||
all_dicts += default_configs
|
||||
item_lists = reversed([d.items() for d in all_dicts])
|
||||
obj.__dict__ = dict(reduce(op.add, item_lists))
|
||||
|
||||
def digest_locals(obj):
|
||||
caller_locals = inspect.currentframe().f_back.f_locals
|
||||
obj.__dict__.update(filtered_locals(caller_locals))
|
||||
|
||||
|
||||
def interpolate(start, end, alpha):
|
||||
return (1-alpha)*start + alpha*end
|
||||
|
|
|
@ -4,6 +4,7 @@ import os
|
|||
from PIL import Image
|
||||
from random import random
|
||||
|
||||
from helpers import *
|
||||
from tex_utils import tex_to_image
|
||||
from mobject import Mobject
|
||||
|
||||
|
@ -20,7 +21,7 @@ class ImageMobject(Mobject):
|
|||
"should_center" : True
|
||||
}
|
||||
def __init__(self, image_file, **kwargs):
|
||||
digest_config(self, ImageMobject, kwargs, locals())
|
||||
digest_locals(self)
|
||||
Mobject.__init__(self, **kwargs)
|
||||
self.filter_rgb = 255 * np.array(Color(self.filter_color).get_rgb()).astype('uint8')
|
||||
self.name = to_cammel_case(
|
||||
|
@ -98,40 +99,3 @@ class ImageMobject(Mobject):
|
|||
points *= 2 * SPACE_WIDTH / width
|
||||
self.add_points(points, rgbs = rgbs)
|
||||
|
||||
#TODO, Make both of these proper mobject classes
|
||||
def text_mobject(text, size = None):
|
||||
size = size or "\\Large" #TODO, auto-adjust?
|
||||
return tex_mobject(text, size, TEMPLATE_TEXT_FILE)
|
||||
|
||||
def tex_mobject(expression,
|
||||
size = None,
|
||||
template_tex_file = TEMPLATE_TEX_FILE):
|
||||
if size == None:
|
||||
if len("".join(expression)) < MAX_LEN_FOR_HUGE_TEX_FONT:
|
||||
size = "\\Huge"
|
||||
else:
|
||||
size = "\\large"
|
||||
#Todo, make this more sophisticated.
|
||||
image_files = tex_to_image(expression, size, template_tex_file)
|
||||
config = {
|
||||
"point_thickness" : 1,
|
||||
"should_center" : False,
|
||||
}
|
||||
if isinstance(image_files, list):
|
||||
#TODO, is checking listiness really the best here?
|
||||
result = CompoundMobject(*[
|
||||
ImageMobject(f, **config)
|
||||
for f in image_files
|
||||
])
|
||||
else:
|
||||
result = ImageMobject(image_files, **config)
|
||||
return result.center().highlight("white")
|
||||
|
||||
|
||||
def underbrace(left, right, buff = 0.2):
|
||||
result = tex_mobject("\\underbrace{%s}"%(14*"\\quad"))
|
||||
result.stretch_to_fit_width(right[0]-left[0])
|
||||
result.shift(left - result.points[0] + buff*DOWN)
|
||||
return result
|
||||
|
||||
|
||||
|
|
23
mobject.py
23
mobject.py
|
@ -24,7 +24,7 @@ class Mobject(object):
|
|||
}
|
||||
DIM = 3
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, Mobject, kwargs)
|
||||
digest_config(self, kwargs)
|
||||
self.color = Color(self.color)
|
||||
if self.name is None:
|
||||
self.name = self.__class__.__name__
|
||||
|
@ -358,10 +358,11 @@ class Mobject1D(Mobject):
|
|||
"density" : DEFAULT_POINT_DENSITY_1D,
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, Mobject1D, kwargs)
|
||||
self.epsilon = 1.0 / self.density
|
||||
digest_config(self, kwargs)
|
||||
self.epsilon = 1.0 / self.density
|
||||
Mobject.__init__(self, **kwargs)
|
||||
|
||||
|
||||
def add_line(self, start, end, min_density = 0.1, color = None):
|
||||
length = np.linalg.norm(end - start)
|
||||
epsilon = self.epsilon / max(length, min_density)
|
||||
|
@ -375,8 +376,8 @@ class Mobject2D(Mobject):
|
|||
"density" : DEFAULT_POINT_DENSITY_2D,
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, Mobject2D, kwargs)
|
||||
self.epsilon = 1.0 / self.density
|
||||
digest_config(self, kwargs)
|
||||
self.epsilon = 1.0 / self.density
|
||||
Mobject.__init__(self, **kwargs)
|
||||
|
||||
class CompoundMobject(Mobject):
|
||||
|
@ -402,6 +403,18 @@ class CompoundMobject(Mobject):
|
|||
curr += num_points
|
||||
return result
|
||||
|
||||
|
||||
class Point(Mobject):
|
||||
DEFAULT_CONFIG = {
|
||||
"color" : BLACK,
|
||||
}
|
||||
def __init__(self, location = ORIGIN, **kwargs):
|
||||
digest_locals(self)
|
||||
Mobject.__init__(self, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
self.add_points([self.location])
|
||||
|
||||
# class CompoundMobject(Mobject):
|
||||
# """
|
||||
# Treats a collection of mobjects as if they were one.
|
||||
|
|
|
@ -10,8 +10,6 @@ from helpers import *
|
|||
from scene import Scene
|
||||
from number_line import NumberLineScene
|
||||
|
||||
MOVIE_PREFIX = "matrix_as_transform_2d/"
|
||||
|
||||
ARROW_CONFIG = {"point_thickness" : 2*DEFAULT_POINT_THICKNESS}
|
||||
LIGHT_RED = RED_E
|
||||
|
||||
|
@ -543,9 +541,3 @@ class Show90DegreeRotation(TransformScene2D):
|
|||
])
|
||||
self.dither()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
command_line_create_scene(MOVIE_PREFIX)
|
|
@ -83,7 +83,7 @@ class RightParen(Mobject):
|
|||
|
||||
class OpenInterval(CompoundMobject):
|
||||
def __init__(self, center_point = ORIGIN, width = 2, **kwargs):
|
||||
digest_config(self, OpenInterval, kwargs, locals())
|
||||
digest_config(self, kwargs, locals())
|
||||
left = LeftParen().shift(LEFT*width/2)
|
||||
right = RightParen().shift(RIGHT*width/2)
|
||||
CompoundMobject.__init__(self, left, right, **kwargs)
|
||||
|
@ -99,7 +99,7 @@ class Piano(ImageMobject):
|
|||
"scale_value" : 0.5
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, Piano, kwargs)
|
||||
digest_config(self, kwargs)
|
||||
ImageMobject.__init__(self, "piano_keyboard")
|
||||
jump = self.get_width()/24
|
||||
self.center()
|
||||
|
@ -137,7 +137,7 @@ class VibratingString(Animation):
|
|||
"alpha_func" : None
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, VibratingString, kwargs)
|
||||
digest_config(self, kwargs)
|
||||
def func(x, t):
|
||||
return sum([
|
||||
(self.amplitude/((k+1)**2.5))*np.sin(2*mult*t)*np.sin(k*mult*x)
|
||||
|
|
|
@ -9,8 +9,6 @@ from scene import Scene
|
|||
from geometry import Polygon
|
||||
from region import region_from_polygon_vertices, region_from_line_boundary
|
||||
|
||||
MOVIE_PREFIX = "pythagorean_proof"
|
||||
|
||||
A_COLOR = BLUE
|
||||
B_COLOR = MAROON_D
|
||||
C_COLOR = YELLOW
|
|
@ -1,3 +1 @@
|
|||
from scene import *
|
||||
from scene_from_video import *
|
||||
from tk_scene import *
|
||||
from scene import *
|
|
@ -13,6 +13,7 @@ from helpers import *
|
|||
|
||||
import displayer as disp
|
||||
from tk_scene import TkSceneRoot
|
||||
from mobject import Mobject
|
||||
|
||||
class Scene(object):
|
||||
DEFAULT_CONFIG = {
|
||||
|
@ -23,7 +24,7 @@ class Scene(object):
|
|||
"announce_construction" : False,
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, Scene, kwargs)
|
||||
digest_config(self, kwargs)
|
||||
if self.announce_construction:
|
||||
print "Constructing %s..."%str(self)
|
||||
self.frame_duration = self.display_config["frame_duration"]
|
||||
|
|
40
tex_utils.py
40
tex_utils.py
|
@ -1,11 +1,45 @@
|
|||
import os
|
||||
import itertools as it
|
||||
from PIL import Image
|
||||
|
||||
from image_mobject import ImageMobject
|
||||
from helpers import *
|
||||
|
||||
#TODO, Cleanup and refactor this file.
|
||||
#TODO, Make both of these proper mobject classes
|
||||
def text_mobject(text, size = None):
|
||||
size = size or "\\Large" #TODO, auto-adjust?
|
||||
return tex_mobject(text, size, TEMPLATE_TEXT_FILE)
|
||||
|
||||
def tex_mobject(expression,
|
||||
size = None,
|
||||
template_tex_file = TEMPLATE_TEX_FILE):
|
||||
if size == None:
|
||||
if len("".join(expression)) < MAX_LEN_FOR_HUGE_TEX_FONT:
|
||||
size = "\\Huge"
|
||||
else:
|
||||
size = "\\large"
|
||||
#Todo, make this more sophisticated.
|
||||
image_files = tex_to_image(expression, size, template_tex_file)
|
||||
config = {
|
||||
"point_thickness" : 1,
|
||||
"should_center" : False,
|
||||
}
|
||||
if isinstance(image_files, list):
|
||||
#TODO, is checking listiness really the best here?
|
||||
result = CompoundMobject(*[
|
||||
ImageMobject(f, **config)
|
||||
for f in image_files
|
||||
])
|
||||
else:
|
||||
result = ImageMobject(image_files, **config)
|
||||
return result.center().highlight("white")
|
||||
|
||||
|
||||
def underbrace(left, right, buff = 0.2):
|
||||
result = tex_mobject("\\underbrace{%s}"%(14*"\\quad"))
|
||||
result.stretch_to_fit_width(right[0]-left[0])
|
||||
result.shift(left - result.points[0] + buff*DOWN)
|
||||
return result
|
||||
|
||||
|
||||
def tex_to_image(expression,
|
||||
size = "\HUGE",
|
||||
template_tex_file = TEMPLATE_TEX_FILE):
|
||||
|
|
|
@ -5,7 +5,5 @@ from complex_numbers import *
|
|||
from functions import *
|
||||
from geometry import *
|
||||
from graph_theory import *
|
||||
from matrix_as_transform_2d import *
|
||||
from number_line import *
|
||||
from pythagorean_proof import *
|
||||
from three_dimensions import *
|
|
@ -1,6 +1,7 @@
|
|||
import numpy as np
|
||||
import itertools as it
|
||||
|
||||
from helpers import *
|
||||
from scene import Scene
|
||||
from animation import Animation
|
||||
|
||||
|
@ -87,8 +88,6 @@ class FlipThroughSymbols(Animation):
|
|||
"end_center" : ORIGIN,
|
||||
}
|
||||
def __init__(self, tex_list, **kwargs):
|
||||
digest_config(self, FlipThroughSymbols, kwargs, locals())
|
||||
self.curr_tex = self.tex_list[0]
|
||||
mobject = tex_mobject(self.curr_tex).shift(start_center)
|
||||
Animation.__init__(self, mobject, **kwargs)
|
||||
|
||||
|
|
|
@ -172,7 +172,6 @@ class Bubble(Mobject):
|
|||
"center_point" : ORIGIN,
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, Bubble, kwargs)
|
||||
Mobject.__init__(self, **kwargs)
|
||||
self.center_offset = self.center_point - Mobject.get_center(self)
|
||||
if self.direction[0] > 0:
|
||||
|
@ -224,9 +223,6 @@ class SpeechBubble(Bubble):
|
|||
"initial_width" : 4,
|
||||
"initial_height" : 2,
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, SpeechBubble, kwargs)
|
||||
Bubble.__init__(self, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
complex_power = 0.9
|
||||
|
@ -261,7 +257,6 @@ class ThoughtBubble(Bubble):
|
|||
"initial_width" : 6
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, ThoughtBubble, kwargs)
|
||||
Bubble.__init__(self, **kwargs)
|
||||
self.index_of_tip = np.argmin(self.points[:,1])
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ from helpers import *
|
|||
|
||||
from number_line import NumberPlane
|
||||
from animation.transform import ApplyPointwiseFunction
|
||||
from animation.animation import Homotopy
|
||||
from animation.simple_animations import Homotopy
|
||||
from scene import Scene
|
||||
|
||||
|
||||
|
@ -18,7 +18,7 @@ class ComplexPlane(NumberPlane):
|
|||
"number_at_center" : complex(0),
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, ComplexPlane, kwargs)
|
||||
digest_config(self, kwargs)
|
||||
kwargs.update({
|
||||
"x_unit_to_spatial_width" : self.unit_to_spatial_width,
|
||||
"y_uint_to_spatial_height" : self.unit_to_spatial_width,
|
||||
|
|
|
@ -13,7 +13,7 @@ class FunctionGraph(Mobject1D):
|
|||
"spatial_radius" : SPACE_WIDTH,
|
||||
}
|
||||
def __init__(self, function, **kwargs):
|
||||
digest_config(self, FunctionGraph, kwargs, locals())
|
||||
self.function = function
|
||||
Mobject1D.__init__(self, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
|
@ -35,14 +35,14 @@ class ParametricFunction(Mobject):
|
|||
"density" : None
|
||||
}
|
||||
def __init__(self, function, **kwargs):
|
||||
digest_config(self, ParametricFunction, kwargs, locals())
|
||||
self.function = function
|
||||
if self.density:
|
||||
self.epsilon = 1.0 / self.density
|
||||
elif self.dim == 1:
|
||||
self.epsilon = 1.0 / self.expected_measure / DEFAULT_POINT_DENSITY_1D
|
||||
else:
|
||||
self.epsilon = 1.0 / np.sqrt(self.expected_measure) / DEFAULT_POINT_DENSITY_2D
|
||||
Mobject.__init__(self, *args, **kwargs)
|
||||
Mobject.__init__(self, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
if self.dim == 1:
|
||||
|
@ -62,4 +62,7 @@ class Axes(CompoundMobject):
|
|||
def __init__(self, **kwargs):
|
||||
x_axis = NumberLine(**kwargs)
|
||||
y_axis = NumberLine(**kwargs).rotate(np.pi/2, OUT)
|
||||
CompoundMobject.__init__(self, x_axis, y_axis)
|
||||
CompoundMobject.__init__(self, x_axis, y_axis)
|
||||
|
||||
|
||||
|
|
@ -3,24 +3,12 @@ from helpers import *
|
|||
from mobject import Mobject, Mobject1D
|
||||
|
||||
|
||||
class Point(Mobject):
|
||||
DEFAULT_CONFIG = {
|
||||
"color" : BLACK,
|
||||
}
|
||||
def __init__(self, location = ORIGIN, **kwargs):
|
||||
digest_config(self, Point, kwargs, locals())
|
||||
Mobject.__init__(self, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
self.add_points([self.location])
|
||||
|
||||
|
||||
class Dot(Mobject1D): #Use 1D density, even though 2D
|
||||
DEFAULT_CONFIG = {
|
||||
"radius" : 0.05
|
||||
}
|
||||
def __init__(self, center_point = ORIGIN, **kwargs):
|
||||
digest_config(self, Dot, kwargs, locals())
|
||||
digest_locals(self)
|
||||
Mobject1D.__init__(self, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
|
@ -37,7 +25,7 @@ class Cross(Mobject1D):
|
|||
"radius" : 0.3
|
||||
}
|
||||
def __init__(self, center_point = ORIGIN, **kwargs):
|
||||
digest_config(self, Cross, kwargs, locals())
|
||||
digest_locals(self)
|
||||
Mobject1D.__init__(self, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
|
@ -54,7 +42,6 @@ class Line(Mobject1D):
|
|||
"min_density" : 0.1
|
||||
}
|
||||
def __init__(self, start, end, **kwargs):
|
||||
digest_config(self, Line, kwargs)
|
||||
self.set_start_and_end(start, end)
|
||||
Mobject1D.__init__(self, **kwargs)
|
||||
|
||||
|
@ -93,7 +80,6 @@ class Arrow(Line):
|
|||
"tip_length" : 0.25
|
||||
}
|
||||
def __init__(self, *args, **kwargs):
|
||||
digest_config(self, Arrow, kwargs)
|
||||
Line.__init__(self, *args, **kwargs)
|
||||
self.add_tip()
|
||||
|
||||
|
@ -150,7 +136,7 @@ class PartialCircle(Mobject1D):
|
|||
"start_angle" : 0
|
||||
}
|
||||
def __init__(self, angle, **kwargs):
|
||||
digest_config(self, PartialCircle, kwargs, locals())
|
||||
digest_locals(self)
|
||||
Mobject1D.__init__(self, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
|
@ -169,7 +155,6 @@ class Circle(PartialCircle):
|
|||
"color" : RED,
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, Circle, kwargs)
|
||||
PartialCircle.__init__(self, angle = 2*np.pi, **kwargs)
|
||||
|
||||
class Polygon(Mobject1D):
|
||||
|
@ -179,8 +164,7 @@ class Polygon(Mobject1D):
|
|||
}
|
||||
def __init__(self, *points, **kwargs):
|
||||
assert len(points) > 1
|
||||
digest_config(self, Polygon, kwargs)
|
||||
self.original_points = points
|
||||
digest_locals(self)
|
||||
Mobject1D.__init__(self, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
|
@ -189,9 +173,7 @@ class Polygon(Mobject1D):
|
|||
else:
|
||||
colors = it.cycle([self.color])
|
||||
self.indices_of_vertices = []
|
||||
points = list(self.original_points)
|
||||
points.append(points[0])
|
||||
for start, end in zip(points, points[1:]):
|
||||
for start, end in adjascent_pairs(self.points):
|
||||
self.indices_of_vertices.append(self.get_num_points())
|
||||
self.add_line(start, end, color = colors.next())
|
||||
|
||||
|
@ -206,10 +188,6 @@ class Rectangle(Mobject1D):
|
|||
"height" : 2.0,
|
||||
"width" : 4.0
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, Rectangle, kwargs)
|
||||
Mobject1D.__init__(self, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
wh = [self.width/2.0, self.height/2.0]
|
||||
self.add_points([
|
||||
|
@ -224,7 +202,13 @@ class Square(Rectangle):
|
|||
"side_length" : 2.0,
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, Square, kwargs)
|
||||
digest_config(self, kwargs)
|
||||
for arg in ["height", "width"]:
|
||||
kwargs[arg] = self.side_length
|
||||
Rectangle.__init__(self, **kwargs)
|
||||
Rectangle.__init__(self, **kwargs)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ class NumberLine(Mobject1D):
|
|||
"longer_tick_multiple" : 2,
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, NumberLine, kwargs)
|
||||
digest_config(self, kwargs)
|
||||
if self.leftmost_tick is None:
|
||||
self.leftmost_tick = -int(self.numerical_radius)
|
||||
self.left_num = self.number_at_center - self.numerical_radius
|
||||
|
@ -107,9 +107,6 @@ class UnitInterval(NumberLine):
|
|||
"number_at_center" : 0.5,
|
||||
"numbers_with_elongated_ticks" : [0, 1],
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, UnitInterval, kwargs)
|
||||
NumberLine.__init__(self, **kwargs)
|
||||
|
||||
|
||||
class NumberPlane(Mobject1D):
|
||||
|
@ -127,10 +124,7 @@ class NumberPlane(Mobject1D):
|
|||
"number_scale_factor" : 0.25,
|
||||
"num_pair_at_center" : np.array((0, 0)),
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, NumberPlane, kwargs)
|
||||
Mobject1D.__init__(self, **kwargs)
|
||||
|
||||
|
||||
def generate_points(self):
|
||||
#TODO, clean this
|
||||
color = self.color
|
||||
|
|
Loading…
Add table
Reference in a new issue