Applied digest_config to almost all __init__ functions

This commit is contained in:
Grant Sanderson 2015-09-28 16:25:18 -07:00
parent 8f2a8f032e
commit 0d60cf6207
14 changed files with 436 additions and 397 deletions

View file

@ -14,27 +14,23 @@ from constants import *
from mobject import Mobject, Point
class Animation(object):
def __init__(self,
mobject,
run_time = DEFAULT_ANIMATION_RUN_TIME,
alpha_func = smooth,
name = None):
DEFAULT_CONFIG = {
"run_time" : DEFAULT_ANIMATION_RUN_TIME,
"alpha_func" : smooth,
"name" : None,
}
def __init__(self, mobject, **kwargs):
if isinstance(mobject, type) and issubclass(mobject, Mobject):
self.mobject = mobject()
elif isinstance(mobject, Mobject):
self.mobject = mobject
else:
mobject = mobject()
elif not isinstance(mobject, Mobject):
raise Exception("Invalid mobject parameter, must be \
subclass or instance of Mobject")
digest_config(self, Animation, kwargs, locals())
self.starting_mobject = copy.deepcopy(self.mobject)
self.alpha_func = alpha_func or (lambda x : x)
self.run_time = run_time
#TODO, Adress the idea of filtering the animation
self.filter_functions = []
self.restricted_height = SPACE_HEIGHT
self.restricted_width = SPACE_WIDTH
self.spacial_center = np.zeros(3)
self.name = name or self.__class__.__name__ + str(self.mobject)
if self.alpha_func is None:
self.alpha_func = (lambda x : x)
if self.name is None:
self.name = self.__class__.__name__ + str(self.mobject)
self.update(0)
def __str__(self):

View file

@ -16,13 +16,15 @@ class DelayByOrder(Animation):
Warning: This will not work on all animation types, but
when it does, it will be pretty cool
"""
def __init__(self, animation, max_power = 5, **kwargs):
self.animation = animation
self.max_power = max_power
kwargs = dict([
DEFAULT_CONFIG = {
"max_power" : 5
}
def __init__(self, animation, **kwargs):
digest_config(self, DelayByOrder, kwargs, locals())
kwargs.update(dict([
(attr, getattr(animation, attr))
for attr in "run_time", "alpha_func"
])
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)
@ -38,45 +40,31 @@ class DelayByOrder(Animation):
self.animation.update_mobject(alpha_array)
class Rotating(Animation):
def __init__(self,
mobject,
axis = None,
axes = [RIGHT, UP],
radians = 2 * np.pi,
run_time = 20.0,
alpha_func = None,
*args, **kwargs):
Animation.__init__(
self, mobject,
run_time = run_time,
alpha_func = alpha_func,
*args, **kwargs
)
self.axes = [axis] if axis is not None else axes
self.radians = radians
DEFAULT_CONFIG = {
"axes" : [RIGHT, UP],
"radians" : 2*np.pi,
"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:
self.mobject.rotate(
self.radians * alpha,
axis
)
self.mobject.rotate(self.radians * alpha, axis)
class RotationAsTransform(Rotating):
def __init__(self, mobject, radians, axis = IN, axes = None,
run_time = DEFAULT_ANIMATION_RUN_TIME,
alpha_func = smooth,
*args, **kwargs):
Rotating.__init__(
self,
mobject,
axis = axis,
axes = axes,
run_time = run_time,
radians = radians,
alpha_func = alpha_func,
)
DEFAULT_CONFIG = {
"axes" : [IN],
"radians" : np.pi / 2,
"run_time" : DEFAULT_ANIMATION_RUN_TIME,
"alpha_func" : smooth,
}
def __init__(self, mobject, **kwargs):
digest_config(self, RotationAsTransform, kwargs, locals())
Rotating.__init__(self, mobject, **kwargs)
class FadeOut(Animation):
def update_mobject(self, alpha):
@ -90,9 +78,9 @@ class FadeIn(Animation):
#TODO, Why do you need to do this? Shouldn't points always align?
class ShimmerIn(DelayByOrder):
def __init__(self, mobject, *args, **kwargs):
def __init__(self, mobject, **kwargs):
mobject.sort_points(lambda p : np.dot(p, DOWN+RIGHT))
DelayByOrder.__init__(self, FadeIn(mobject, *args, **kwargs))
DelayByOrder.__init__(self, FadeIn(mobject, **kwargs))
class ShowCreation(Animation):
@ -107,21 +95,22 @@ class ShowCreation(Animation):
)
class Flash(Animation):
def __init__(self, mobject, color = "white", slow_factor = 0.01,
run_time = 0.1, alpha_func = None,
*args, **kwargs):
Animation.__init__(self, mobject, run_time = run_time,
alpha_func = alpha_func,
*args, **kwargs)
self.intermediate = Mobject(color = color)
DEFAULT_CONFIG = {
"color" : "white",
"slow_factor" : 0.01,
"run_time" : 0.1,
"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)
for point in self.mobject.points
for x in [-1, 1]
for y in [-1, 1]
])
self.reference_mobjects.append(self.intermediate)
self.slow_factor = slow_factor
Animation.__init__(self, mobject, **kwargs)
def update_mobject(self, alpha):
#Makes alpha go from 0 to slow_factor to 0 instead of 0 to 1
@ -134,12 +123,12 @@ class Flash(Animation):
)
class Homotopy(Animation):
def __init__(self, homotopy, *args, **kwargs):
def __init__(self, homotopy, **kwargs):
"""
Homotopy a function from (x, y, z, t) to (x', y', z')
"""
self.homotopy = homotopy
Animation.__init__(self, *args, **kwargs)
digest_config(self, Homotopy, kwargs, locals())
Animation.__init__(self, **kwargs)
def update_mobject(self, alpha):
self.mobject.points = np.array([
@ -148,7 +137,7 @@ class Homotopy(Animation):
])
class ComplexHomotopy(Homotopy):
def __init__(self, complex_homotopy, *args, **kwargs):
def __init__(self, complex_homotopy, **kwargs):
"""
Complex Hootopy a function (z, t) to z'
"""

View file

@ -5,7 +5,7 @@ import copy
import warnings
from animation import Animation
from mobject import Mobject, Point
from mobject import Mobject, Point, Grid
from constants import *
from helpers import *
@ -31,25 +31,25 @@ def counterclockwise_path(start_points, end_points, alpha):
return semi_circular_path(start_points, end_points, alpha, OUT)
class Transform(Animation):
def __init__(self, mobject1, mobject2,
run_time = DEFAULT_TRANSFORM_RUN_TIME,
interpolation_function = straight_path,
black_out_extra_points = False,
*args, **kwargs):
self.interpolation_function = interpolation_function
count1, count2 = mobject1.get_num_points(), mobject2.get_num_points()
DEFAULT_CONFIG = {
"run_time" : DEFAULT_TRANSFORM_RUN_TIME,
"interpolation_function" : straight_path,
"should_black_out_extra_points" : False
}
def __init__(self, mobject, ending_mobject, **kwargs):
digest_config(self, Transform, kwargs, locals())
count1, count2 = mobject.get_num_points(), ending_mobject.get_num_points()
if count2 == 0:
mobject2.add_points([SPACE_WIDTH*RIGHT+SPACE_HEIGHT*UP])
count2 = mobject2.get_num_points()
Mobject.align_data(mobject1, mobject2)
self.ending_mobject = mobject2
if black_out_extra_points and count2 < count1:
ending_mobject.add_points([SPACE_WIDTH*RIGHT+SPACE_HEIGHT*UP])
count2 = ending_mobject.get_num_points()
Mobject.align_data(mobject, ending_mobject)
if self.should_black_out_extra_points and count2 < count1:
self.black_out_extra_points(count1, count2)
Animation.__init__(self, mobject1, run_time = run_time, *args, **kwargs)
self.name += "To" + str(mobject2)
self.mobject.SHOULD_BUFF_POINTS = \
mobject1.SHOULD_BUFF_POINTS and mobject2.SHOULD_BUFF_POINTS
Animation.__init__(self, mobject, **kwargs)
self.name += "To" + str(ending_mobject)
self.mobject.should_buffer_points = \
mobject.should_buffer_points and ending_mobject.should_buffer_points
def black_out_extra_points(self, count1, count2):
#Ensure redundant pixels fade to black
@ -89,53 +89,28 @@ class Transform(Animation):
)
class ClockwiseTransform(Transform):
def __init__(self, mobject1, mobject2, **kwargs):
Transform.__init__(
self, mobject1, mobject2,
interpolation_function = clockwise_path, **kwargs
)
DEFAULT_CONFIG = {
"interpolation_function" : clockwise_path
}
def __init__(self, *args, **kwargs):
digest_config(self, ClockwiseTransform, kwargs)
Transform.__init__(self, *args, **kwargs)
class CounterclockwiseTransform(Transform):
def __init__(self, mobject1, mobject2, **kwargs):
Transform.__init__(
self, mobject1, mobject2,
interpolation_function = counterclockwise_path, **kwargs
)
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):
name = "interpolation_function"
interp_func = kwargs[name] if name in kwargs else counterclockwise_path
dot = Point(mob.get_center(), color = "black")
Transform.__init__(
self, dot, mob,
interpolation_function = interp_func,
**kwargs
)
class FadeToColor(Transform):
def __init__(self, mobject, color, *args, **kwargs):
target = copy.deepcopy(mobject).highlight(color)
Transform.__init__(self, mobject, target, *args, **kwargs)
class Highlight(FadeToColor):
def __init__(self, mobject, color = "red",
run_time = DEFAULT_ANIMATION_RUN_TIME,
alpha_func = there_and_back, *args, **kwargs):
FadeToColor.__init__(
self, mobject, color,
run_time = run_time,
alpha_func = alpha_func,
*args, **kwargs
)
class ScaleInPlace(Transform):
def __init__(self, mobject, scale_factor, *args, **kwargs):
target = copy.deepcopy(mobject)
center = mobject.get_center()
target.shift(-center).scale(scale_factor).shift(center)
Transform.__init__(self, mobject, target, *args, **kwargs)
digest_config(self, SpinInFromNothing, kwargs)
Transform.__init__(self, Point(mob.get_center()), mob, **kwargs)
class ApplyMethod(Transform):
def __init__(self, method, *args, **kwargs):
@ -155,6 +130,14 @@ class ApplyMethod(Transform):
**kwargs
)
class FadeToColor(ApplyMethod):
def __init__(self, mobject, color, **kwargs):
ApplyMethod.__init__(self, mobject.highlight, color, **kwargs)
class ScaleInPlace(ApplyMethod):
def __init__(self, mobject, scale_factor, **kwargs):
ApplyMethod.__init__(self, mobject.scale_in_place, scale_factor, **kwargs)
class ApplyFunction(Transform):
def __init__(self, function, mobject, **kwargs):
Transform.__init__(
@ -165,16 +148,15 @@ class ApplyFunction(Transform):
)
self.name = "ApplyFunctionTo"+str(mobject)
class ApplyPointwiseFunction(Transform):
def __init__(self, function, mobject,
run_time = DEFAULT_ANIMATION_RUN_TIME, **kwargs):
DEFAULT_CONFIG = {
"run_time" : DEFAULT_ANIMATION_RUN_TIME
}
def __init__(self, function, mobject, **kwargs):
digest_config(self, ApplyPointwiseFunction, kwargs)
map_image = copy.deepcopy(mobject)
map_image.points = np.array(map(function, map_image.points))
Transform.__init__(
self, mobject, map_image,
run_time = run_time, **kwargs
)
Transform.__init__(self, mobject, map_image, **kwargs)
self.name = "".join([
"Apply",
"".join([s.capitalize() for s in function.__name__.split("_")]),
@ -201,22 +183,25 @@ class ComplexFunction(ApplyPointwiseFunction):
class TransformAnimations(Transform):
def __init__(self, start_anim, end_anim,
alpha_func = squish_alpha_func(smooth),
**kwargs):
DEFAULT_CONFIG = {
"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:
run_time = kwargs.pop("run_time")
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:
anim.set_run_time(run_time)
self.start_anim, self.end_anim = start_anim, end_anim
Transform.__init__(
self,
start_anim.mobject,
end_anim.mobject,
run_time = max(start_anim.run_time, end_anim.run_time),
alpha_func = alpha_func,
**kwargs
)
if hasattr(anim, "ending_mobject"):
Mobject.align_data(anim.starting_mobject, anim.ending_mobject)
Transform.__init__(self, start_anim.mobject, end_anim.mobject, **kwargs)
#Rewire starting and ending mobjects
start_anim.mobject = self.starting_mobject
end_anim.mobject = self.ending_mobject

View file

@ -2,7 +2,7 @@ import os
import numpy as np
GENERALLY_BUFF_POINTS = True
GENERALLY_BUFFER_POINTS = True
PRODUCTION_QUALITY_DISPLAY_CONFIG = {
"height" : 1440,
@ -27,14 +27,16 @@ SPACE_HEIGHT = 4.0
SPACE_WIDTH = SPACE_HEIGHT * DEFAULT_WIDTH / DEFAULT_HEIGHT
DEFAULT_MOBJECT_TO_EDGE_BUFFER = 0.5
DEFAULT_MOBJECT_TO_MOBJECT_BUFFER = 0.2
#All in seconds
DEFAULT_FRAME_DURATION = 0.04
DEFAULT_ANIMATION_RUN_TIME = 1.0
DEFAULT_TRANSFORM_RUN_TIME = 1.0
DEFAULT_DITHER_TIME = 1.0
DEFAULT_NUM_STARS = 1000
ORIGIN = np.array(( 0, 0, 0))
UP = np.array(( 0, 1, 0))

View file

@ -69,7 +69,7 @@ def paint_mobject(mobject, image_array = None):
flattener = np.array([1, width], dtype = 'int').reshape((2, 1))
indices = np.dot(points, flattener)
indices = indices.reshape(indices.size)
if mobject.should_buffer_points():#Is this alright?
if mobject.should_buffer_points:#Is this alright?
for tweak in [
indices + 1,
indices + width,

View file

@ -9,6 +9,25 @@ import operator as op
from constants import *
def digest_config(obj, Class, kwargs, local_args = {}):
"""
To be used in initializing most-to-all objects.
Sets key word args 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)
def interpolate(start, end, alpha):
return (1-alpha)*start + alpha*end

View file

@ -7,41 +7,42 @@ from constants import *
from helpers import *
class FunctionGraph(Mobject1D):
DEFAULT_COLOR = "lightblue"
def __init__(self, function, x_range = [-10, 10], *args, **kwargs):
self.function = function
self.x_min = x_range[0] / SPACE_WIDTH
self.x_max = x_range[1] / SPACE_WIDTH
Mobject1D.__init__(self, *args, **kwargs)
DEFAULT_CONFIG = {
"color" : "lightblue",
"x_min" : -10,
"x_max" : 10,
"spacial_radius" : SPACE_WIDTH,
}
def __init__(self, function, **kwargs):
digest_config(self, FunctionGraph, kwargs, locals())
Mobject1D.__init__(self, **kwargs)
def generate_points(self):
scale_factor = 2.0 * SPACE_WIDTH / (self.x_max - self.x_min)
self.epsilon /= scale_factor
numerical_radius = (self.x_max - self.x_min)/2
numerical_center = (self.x_max + self.x_min)/2
ratio = numerical_radius / self.spacial_radius
epsilon = self.epsilon * ratio
self.add_points([
np.array([x, self.function(x), 0])
np.array([(x-numerical_center)/ratio, self.function(x), 0])
for x in np.arange(self.x_min, self.x_max, self.epsilon)
])
self.scale(scale_factor)
class ParametricFunction(Mobject):
DEFAULT_COLOR = "white"
def __init__(self,
function,
dim = 1,
expected_measure = 10.0,
density = None,
*args,
**kwargs):
self.function = function
self.dim = dim
self.expected_measure = expected_measure
if density:
self.epsilon = 1.0 / density
DEFAULT_CONFIG = {
"color" : "white",
"dim" : 1,
"expected_measure" : 10.0,
"density" : None
}
def __init__(self, function, **kwargs):
digest_config(self, ParametricFunction, kwargs, locals())
if self.density:
self.epsilon = 1.0 / self.density
elif self.dim == 1:
self.epsilon = 1.0 / expected_measure / DEFAULT_POINT_DENSITY_1D
self.epsilon = 1.0 / self.expected_measure / DEFAULT_POINT_DENSITY_1D
else:
self.epsilon = 1.0 / np.sqrt(expected_measure) / DEFAULT_POINT_DENSITY_2D
self.epsilon = 1.0 / np.sqrt(self.expected_measure) / DEFAULT_POINT_DENSITY_2D
Mobject.__init__(self, *args, **kwargs)
def generate_points(self):
@ -58,16 +59,15 @@ class ParametricFunction(Mobject):
])
class Grid(Mobject1D):
DEFAULT_COLOR = "green"
def __init__(self,
radius = max(SPACE_HEIGHT, SPACE_WIDTH),
interval_size = 1.0,
subinterval_size = 0.5,
*args, **kwargs):
self.radius = radius
self.interval_size = interval_size
self.subinterval_size = subinterval_size
Mobject1D.__init__(self, *args, **kwargs)
DEFAULT_CONFIG = {
"color" : "green",
"radius" : max(SPACE_HEIGHT, SPACE_WIDTH),
"interval_size" : 1.0,
"subinterval_size" : 0.5,
}
def __init__(self, **kwargs):
digest_config(self, Grid, kwargs)
Mobject1D.__init__(self, **kwargs)
def generate_points(self):
self.add_points([
@ -91,27 +91,20 @@ class Grid(Mobject1D):
], color = color)
class NumberLine(Mobject1D):
def __init__(self,
radius = SPACE_WIDTH,
unit_length_to_spacial_width = 1,
tick_size = 0.1,
tick_frequency = 0.5,
number_at_center = 0,
numbers_with_elongated_ticks = [0],
longer_tick_multiple = 2,
**kwargs):
#TODO, There must be better (but still safe) way to add all
#these config arguments as attributes.
self.radius = radius
self.unit_length_to_spacial_width = unit_length_to_spacial_width
self.tick_size = tick_size
self.tick_frequency = tick_frequency
self.numbers_with_elongated_ticks = numbers_with_elongated_ticks
self.number_at_center = number_at_center
self.longer_tick_multiple = longer_tick_multiple
numerical_radius = float(radius) / unit_length_to_spacial_width
self.left_num = number_at_center - numerical_radius
self.right_num = number_at_center + numerical_radius
DEFAULT_CONFIG = {
"radius" : SPACE_WIDTH,
"unit_length_to_spacial_width" : 1,
"tick_size" : 0.1,
"tick_frequency" : 0.5,
"number_at_center" : 0,
"numbers_with_elongated_ticks" : [0],
"longer_tick_multiple" : 2,
}
def __init__(self, **kwargs):
digest_config(self, NumberLine, kwargs)
numerical_radius = float(self.radius) / self.unit_length_to_spacial_width
self.left_num = self.number_at_center - numerical_radius
self.right_num = self.number_at_center + numerical_radius
Mobject1D.__init__(self, **kwargs)
def generate_points(self):
@ -170,15 +163,14 @@ class UnitInterval(NumberLine):
"numbers_with_elongated_ticks" : [0, 1],
}
def __init__(self, **kwargs):
config = self.DEFAULT_CONFIG
config.update(kwargs)
NumberLine.__init__(self, **config)
digest_config(self, UnitInterval, kwargs)
NumberLine.__init__(self, **kwargs)
class Axes(CompoundMobject):
def __init__(self, *args, **kwargs):
x_axis = NumberLine(*args, **kwargs)
y_axis = NumberLine(*args, **kwargs).rotate(np.pi/2, OUT)
def __init__(self, **kwargs):
x_axis = NumberLine(**kwargs)
y_axis = NumberLine(**kwargs).rotate(np.pi/2, OUT)
CompoundMobject.__init__(self, x_axis, y_axis)

View file

@ -7,23 +7,24 @@ from random import random
from tex_utils import *
from mobject import *
class ImageMobject(Mobject2D):
class ImageMobject(Mobject):
"""
Automatically filters out black pixels
"""
# SHOULD_BUFF_POINTS = False
def __init__(self,
image_file,
filter_color = "black",
invert = True,
use_cache = True,
*args, **kwargs):
Mobject2D.__init__(self, *args, **kwargs)
self.filter_rgb = 255 * np.array(Color(filter_color).get_rgb()).astype('uint8')
DEFAULT_CONFIG = {
"filter_color" : "black",
"invert" : True,
"use_cache" : True,
"should_buffer_points" : False,
"scale_value" : 1.0
}
def __init__(self, image_file, **kwargs):
digest_config(self, ImageMobject, kwargs, locals())
Mobject.__init__(self, **kwargs)
self.filter_rgb = 255 * np.array(Color(self.filter_color).get_rgb()).astype('uint8')
self.name = to_cammel_case(
os.path.split(image_file)[-1].split(".")[0]
)
self.use_cache = use_cache
possible_paths = [
image_file,
os.path.join(IMAGE_DIR, image_file),
@ -32,31 +33,33 @@ class ImageMobject(Mobject2D):
]
for path in possible_paths:
if os.path.exists(path):
self.generate_points_from_file(path, invert)
self.generate_points_from_file(path)
self.scale(self.scale_value)
self.center()
return
raise IOError("File not Found")
def generate_points_from_file(self, path, invert):
if self.use_cache and self.read_in_cached_attrs(path, invert):
def generate_points_from_file(self, path):
if self.use_cache and self.read_in_cached_attrs(path):
return
image = Image.open(path).convert('RGB')
if invert:
if self.invert:
image = invert_image(image)
self.generate_points_from_image_array(np.array(image))
self.cache_attrs(path, invert)
self.cache_attrs(path)
def get_cached_attr_files(self, path, invert, attrs):
def get_cached_attr_files(self, path, attrs):
#Hash should be unique to (path, invert) pair
unique_hash = str(hash(path+str(invert)))
unique_hash = str(hash(path+str(self.invert)))
return [
os.path.join(IMAGE_MOBJECT_DIR, unique_hash)+"."+attr
for attr in attrs
]
def read_in_cached_attrs(self, path, invert,
def read_in_cached_attrs(self, path,
attrs = ("points", "rgbs"),
dtype = "float64"):
cached_attr_files = self.get_cached_attr_files(path, invert, attrs)
cached_attr_files = self.get_cached_attr_files(path, attrs)
if all(map(os.path.exists, cached_attr_files)):
for attr, cache_file in zip(attrs, cached_attr_files):
arr = np.fromfile(cache_file, dtype = dtype)
@ -65,10 +68,10 @@ class ImageMobject(Mobject2D):
return True
return False
def cache_attrs(self, path, invert,
def cache_attrs(self, path,
attrs = ("points", "rgbs"),
dtype = "float64"):
cached_attr_files = self.get_cached_attr_files(path, invert, attrs)
cached_attr_files = self.get_cached_attr_files(path, attrs)
for attr, cache_file in zip(attrs, cached_attr_files):
getattr(self, attr).astype(dtype).tofile(cache_file)
@ -98,20 +101,26 @@ class ImageMobject(Mobject2D):
return False
class Face(ImageMobject):
def __init__(self, mode = "simple", *args, **kwargs):
DEFAULT_CONFIG = {
"mode" : "simple",
"scale_value" : 0.5
}
def __init__(self, **kwargs):
"""
Mode can be "simple", "talking", "straight"
"""
ImageMobject.__init__(self, mode + "_face", *args, **kwargs)
self.scale(0.5)
self.center()
digest_config(self, Face, kwargs)
ImageMobject.__init__(self, self.mode + "_face", **kwargs)
class VideoIcon(ImageMobject):
def __init__(self, *args, **kwargs):
ImageMobject.__init__(self, "video_icon", *args, **kwargs)
self.scale(0.3)
self.center()
DEFAULT_CONFIG = {
"scale_value" : 0.3
}
def __init__(self, **kwargs):
digest_config(self, VideoIcon, kwargs)
ImageMobject.__init__(self, "video_icon", **kwargs)
#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)
@ -130,8 +139,8 @@ def tex_mobject(expression,
#TODO, is checking listiness really the best here?
result = CompoundMobject(*map(ImageMobject, image_files))
else:
result = ImageMobject(image_files)
return result.highlight("white").center()
result = ImageMobject(image_files, should_buffer_points = True)
return result.highlight("white")

View file

@ -17,26 +17,20 @@ class Mobject(object):
Mathematical Object
"""
#Number of numbers used to describe a point (3 for pos, 3 for normal vector)
DEFAULT_CONFIG = {
"color" : "white",
"should_buffer_points" : GENERALLY_BUFFER_POINTS,
"name" : None,
}
DIM = 3
DEFAULT_COLOR = Color("skyblue")
SHOULD_BUFF_POINTS = GENERALLY_BUFF_POINTS
EDGE_BUFFER = 0.5
NEXT_TO_BUFFER = 0.2
def __init__(self,
color = None,
name = None,
center = None,
**kwargs
):
self.color = Color(color) if color else Color(self.DEFAULT_COLOR)
if not hasattr(self, "name"):
self.name = name or self.__class__.__name__
def __init__(self, **kwargs):
digest_config(self, Mobject, kwargs)
self.color = Color(self.color)
if self.name is None:
self.name = self.__class__.__name__
self.has_normals = hasattr(self, 'unit_normal')
self.init_points()
self.generate_points()
if center:
self.center().shift(center)
def init_points(self):
self.points = np.zeros((0, 3))
@ -130,13 +124,13 @@ class Mobject(object):
return self
#Wrapper functions for better naming
def to_corner(self, corner = LEFT+DOWN, buff = EDGE_BUFFER):
def to_corner(self, corner = LEFT+DOWN, buff = DEFAULT_MOBJECT_TO_EDGE_BUFFER):
return self.align_on_border(corner, buff)
def to_edge(self, edge = LEFT, buff = EDGE_BUFFER):
def to_edge(self, edge = LEFT, buff = DEFAULT_MOBJECT_TO_EDGE_BUFFER):
return self.align_on_border(edge, buff)
def align_on_border(self, direction, buff = EDGE_BUFFER):
def align_on_border(self, direction, buff = DEFAULT_MOBJECT_TO_EDGE_BUFFER):
"""
Direction just needs to be a vector pointing towards side or
corner in the 2d plane.
@ -155,7 +149,7 @@ class Mobject(object):
def next_to(self, mobject,
direction = RIGHT,
buff = NEXT_TO_BUFFER,
buff = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER,
aligned_edge = None):
if aligned_edge is not None:
anchor_point = self.get_corner(aligned_edge-direction)
@ -231,6 +225,11 @@ class Mobject(object):
self.rgbs[:,:] = rgb
return self
def set_color(self, color):
self.highlight(color)
self.color = Color(color)
return self
def to_original_color(self):
self.highlight(self.color)
return self
@ -315,7 +314,7 @@ class Mobject(object):
### Stuff subclasses should deal with ###
def should_buffer_points(self):
# potentially changed in subclasses
return GENERALLY_BUFF_POINTS
return GENERALLY_BUFFER_POINTS
def generate_points(self):
#Typically implemented in subclass, unless purposefully left blank
@ -346,16 +345,24 @@ class Mobject(object):
alpha * getattr(mobject2, attr)
setattr(target_mobject, attr, new_array)
#TODO, Make the two implementations bellow not redundant
class Mobject1D(Mobject):
def __init__(self, density = DEFAULT_POINT_DENSITY_1D, *args, **kwargs):
self.epsilon = 1.0 / density
Mobject.__init__(self, *args, **kwargs)
DEFAULT_CONFIG = {
"density" : DEFAULT_POINT_DENSITY_1D,
}
def __init__(self, **kwargs):
digest_config(self, Mobject1D, kwargs)
self.epsilon = 1.0 / self.density
Mobject.__init__(self, **kwargs)
class Mobject2D(Mobject):
def __init__(self, density = DEFAULT_POINT_DENSITY_2D, *args, **kwargs):
self.epsilon = 1.0 / density
Mobject.__init__(self, *args, **kwargs)
DEFAULT_CONFIG = {
"density" : DEFAULT_POINT_DENSITY_2D,
}
def __init__(self, **kwargs):
digest_config(self, Mobject1D, kwargs)
self.epsilon = 1.0 / self.density
Mobject.__init__(self, **kwargs)
class CompoundMobject(Mobject):
def __init__(self, *mobjects):

View file

@ -8,28 +8,24 @@ from helpers import *
class Point(Mobject):
DEFAULT_COLOR = "black"
def __init__(self, location = ORIGIN, *args, **kwargs):
self.location = np.array(location)
Mobject.__init__(self, *args, **kwargs)
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.reshape((1, 3)))
self.add_points(self.location)
class Dot(Mobject1D): #Use 1D density, even though 2D
DEFAULT_COLOR = "white"
DEFAULT_RADIUS = 0.05
def __init__(self, center = ORIGIN, radius = DEFAULT_RADIUS,
*args, **kwargs):
center = np.array(center)
if center.size == 1:
raise Exception("Center must have 2 or 3 coordinates!")
elif center.size == 2:
center = np.append(center, [0])
self.center_point = center
self.radius = radius
Mobject1D.__init__(self, *args, **kwargs)
DEFAULT_CONFIG = {
"radius" : 0.05
}
def __init__(self, center_point = ORIGIN, **kwargs):
digest_config(self, Dot, kwargs, locals())
Mobject1D.__init__(self, **kwargs)
def generate_points(self):
self.add_points([
@ -40,22 +36,30 @@ class Dot(Mobject1D): #Use 1D density, even though 2D
])
class Cross(Mobject1D):
RADIUS = 0.3
DEFAULT_COLOR = "white"
DEFAULT_CONFIG = {
"color" : "yellow",
"radius" : 0.3
}
def __init__(self, center_point = ORIGIN, **kwargs):
digest_config(self, Cross, kwargs, locals())
Mobject1D.__init__(self, **kwargs)
def generate_points(self):
self.add_points([
(sgn * x, x, 0)
for x in np.arange(-self.RADIUS / 2, self.RADIUS/2, self.epsilon)
for x in np.arange(-self.radius / 2, self.radius/2, self.epsilon)
for sgn in [-1, 1]
])
self.shift(self.center_point)
class Line(Mobject1D):
MIN_DENSITY = 0.1
def __init__(self, start, end, density = DEFAULT_POINT_DENSITY_1D,
*args, **kwargs):
DEFAULT_CONFIG = {
"min_density" : 0.1
}
def __init__(self, start, end, **kwargs):
digest_config(self, Line, kwargs)
self.set_start_and_end(start, end)
density *= max(self.get_length(), self.MIN_DENSITY)
Mobject1D.__init__(self, density = density, *args, **kwargs)
Mobject1D.__init__(self, **kwargs)
def set_start_and_end(self, start, end):
preliminary_start, preliminary_end = [
@ -74,9 +78,11 @@ class Line(Mobject1D):
]
def generate_points(self):
length = np.linalg.norm(self.end - self.start)
epsilon = self.epsilon / max(length, self.min_density)
self.add_points([
interpolate(self.start, self.end, t)
for t in np.arange(0, 1, self.epsilon)
for t in np.arange(0, 1, epsilon)
])
def get_length(self):
@ -90,22 +96,21 @@ class Line(Mobject1D):
return rise/run
class Arrow(Line):
DEFAULT_COLOR = "white"
DEFAULT_TIP_LENGTH = 0.25
DEFAULT_CONFIG = {
"color" : "white",
"tip_length" : 0.25
}
def __init__(self, *args, **kwargs):
if "tip_length" in kwargs:
tip_length = kwargs.pop("tip_length")
else:
tip_length = self.DEFAULT_TIP_LENGTH
digest_config(self, Arrow, kwargs)
Line.__init__(self, *args, **kwargs)
self.add_tip(tip_length)
self.add_tip()
def add_tip(self, tip_length):
def add_tip(self):
vect = self.start-self.end
vect = vect*tip_length/np.linalg.norm(vect)
vect = vect*self.tip_length/np.linalg.norm(vect)
self.add_points([
interpolate(self.end, self.end+v, t)
for t in np.arange(0, 1, tip_length*self.epsilon)
for t in np.arange(0, 1, self.tip_length*self.epsilon)
for v in [
rotate_vector(vect, np.pi/4, axis)
for axis in IN, OUT
@ -113,7 +118,7 @@ class Arrow(Line):
])
class CurvedLine(Line):
def __init__(self, start, end, via = None, *args, **kwargs):
def __init__(self, start, end, via = None, **kwargs):
self.set_start_and_end(start, end)
if via == None:
self.via = rotate_vector(
@ -124,7 +129,7 @@ class CurvedLine(Line):
self.via = via.get_center()
else:
self.via = via
Line.__init__(self, start, end, *args, **kwargs)
Line.__init__(self, start, end, **kwargs)
def generate_points(self):
self.add_points([
@ -137,9 +142,12 @@ class CurvedLine(Line):
])
class Circle(Mobject1D):
DEFAULT_COLOR = "red"
def __init__(self, radius = 1.0, **kwargs):
self.radius = radius
DEFAULT_CONFIG = {
"color" : "red",
"radius" : 1.0
}
def __init__(self, **kwargs):
digest_config(self, Circle, kwargs)
Mobject1D.__init__(self, **kwargs)
def generate_points(self):
@ -149,9 +157,13 @@ class Circle(Mobject1D):
])
class Rectangle(Mobject1D):
DEFAULT_COLOR = "yellow"
def __init__(self, height = 2.0, width = 2.0, **kwargs):
self.height, self.width = height, width
DEFAULT_CONFIG = {
"color" : "yellow",
"height" : 2.0,
"width" : 4.0
}
def __init__(self, **kwargs):
digest_config(self, Rectangle, kwargs)
Mobject1D.__init__(self, **kwargs)
def generate_points(self):
@ -164,17 +176,27 @@ class Rectangle(Mobject1D):
])
class Square(Rectangle):
def __init__(self, side_length = 2.0, **kwargs):
Rectangle.__init__(self, side_length, side_length, **kwargs)
DEFAULT_CONFIG = {
"height" : 2.0,
"width" : 2.0,
}
def __init__(self, **kwargs):
digest_config(self, Square, kwargs)
Rectangle.__init__(self, **kwargs)
class Bubble(Mobject):
def __init__(self, direction = LEFT, index_of_tip = -1, center = ORIGIN):
self.direction = direction
self.content = Mobject()
self.index_of_tip = index_of_tip
self.center_offset = center - Mobject.get_center(self)
if direction[0] > 0:
DEFAULT_CONFIG = {
"direction" : LEFT,
"index_of_tip" : -1,
"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:
self.rotate(np.pi, UP)
self.content = Mobject()
def get_tip(self):
return self.points[self.index_of_tip]
@ -217,13 +239,19 @@ class Bubble(Mobject):
return self
class SpeechBubble(Bubble):
INITIAL_WIDTH = 4
INITIAL_HEIGHT = 2
def __init__(self, *args, **kwargs):
Mobject.__init__(self, *args, **kwargs)
DEFAULT_CONFIG = {
"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
radius = self.INITIAL_WIDTH/2
circle = Circle(density = radius*DEFAULT_POINT_DENSITY_1D)
radius = self.initial_width/2
circle = Circle(radius = radius)
circle.scale(1.0/radius)
circle.apply_complex_function(lambda z : z**complex_power)
circle.scale(radius)
boundary_point_as_complex = radius*complex(-1)**complex_power
@ -243,28 +271,28 @@ class SpeechBubble(Bubble):
)
self.highlight("white")
self.rotate(np.pi/2)
self.points[:,1] *= float(self.INITIAL_HEIGHT)/self.INITIAL_WIDTH
Bubble.__init__(self, direction = LEFT)
self.points[:,1] *= float(self.initial_height)/self.initial_width
class ThoughtBubble(Bubble):
NUM_BULGES = 7
INITIAL_INNER_RADIUS = 1.8
INITIAL_WIDTH = 6
def __init__(self, *args, **kwargs):
Mobject.__init__(self, *args, **kwargs)
DEFAULT_CONFIG = {
"num_bulges" : 7,
"initial_inner_radius" : 1.8,
"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])
def generate_points(self):
self.add(Circle().scale(0.15).shift(2.5*DOWN+2*LEFT))
self.add(Circle().scale(0.3).shift(2*DOWN+1.5*LEFT))
for n in range(self.NUM_BULGES):
theta = 2*np.pi*n/self.NUM_BULGES
for n in range(self.num_bulges):
theta = 2*np.pi*n/self.num_bulges
self.add(Circle().shift((np.cos(theta), np.sin(theta), 0)))
self.filter_out(lambda p : np.linalg.norm(p) < self.INITIAL_INNER_RADIUS)
self.stretch_to_fit_width(self.INITIAL_WIDTH)
self.filter_out(lambda p : np.linalg.norm(p) < self.initial_inner_radius)
self.stretch_to_fit_width(self.initial_width)
self.highlight("white")
Bubble.__init__(
self,
index_of_tip = np.argmin(self.points[:,1]),
**kwargs
)

View file

@ -7,15 +7,14 @@ from constants import *
from helpers import *
class Stars(Mobject):
DEFAULT_COLOR = "white"
SHOULD_BUFF_POINTS = False
def __init__(self,
radius = SPACE_WIDTH,
num_points = DEFAULT_NUM_STARS,
*args, **kwargs):
self.num_points = num_points
self.radius = radius
Mobject.__init__(self, *args, **kwargs)
DEFAULT_CONFIG = {
"should_buffer_points" : False,
"radius" : SPACE_WIDTH,
"num_points" : 1000,
}
def __init__(self, **kwargs):
digest_config(self, Stars, kwargs)
Mobject.__init__(self, **kwargs)
def generate_points(self):
self.add_points([
@ -42,12 +41,12 @@ class CubeWithFaces(Mobject2D):
for sgn in [-1, 1]
])
self.pose_at_angle()
self.set_color("skyblue")
def unit_normal(self, coords):
return np.array(map(lambda x : 1 if abs(x) == 1 else 0, coords))
class Cube(Mobject1D):
DEFAULT_COLOR = "yellow"
def generate_points(self):
self.add_points([
([a, b, c][p[0]], [a, b, c][p[1]], [a, b, c][p[2]])
@ -55,9 +54,9 @@ class Cube(Mobject1D):
for a, b, c in it.product([-1, 1], [-1, 1], np.arange(-1, 1, self.epsilon))
])
self.pose_at_angle()
self.set_color("yellow")
class Octohedron(Mobject1D):
DEFAULT_COLOR = "pink"
def generate_points(self):
x = np.array([1, 0, 0])
y = np.array([0, 1, 0])
@ -72,9 +71,9 @@ class Octohedron(Mobject1D):
Line(pair[0], pair[1], density = 1/self.epsilon).points
)
self.pose_at_angle()
self.set_color("pink")
class Dodecahedron(Mobject1D):
DEFAULT_COLOR = "limegreen"
def generate_points(self):
phi = (1 + np.sqrt(5)) / 2
x = np.array([1, 0, 0])
@ -99,6 +98,7 @@ class Dodecahedron(Mobject1D):
matrix = b*np.array([x[perm], y[perm], z[perm]])
self.add_points(np.dot(five_lines_points, matrix))
self.pose_at_angle()
self.set_color("limegreen")
class Sphere(Mobject2D):
def generate_points(self):
@ -111,6 +111,9 @@ class Sphere(Mobject2D):
for phi in np.arange(self.epsilon, np.pi, self.epsilon)
for theta in np.arange(0, 2 * np.pi, 2 * self.epsilon / np.sin(phi))
])
self.set_color("blue")
def unit_normal(self, coords):
return np.array(coords) / np.linalg.norm(coords)
return np.array(coords) / np.linalg.norm(coords)

View file

@ -16,7 +16,10 @@ from script_wrapper import command_line_create_scene
class SampleScene(Scene):
def construct(self):
pass
square = Square()
self.add(square)
self.dither()
self.play(Flash(square))
if __name__ == "__main__":
command_line_create_scene()

View file

@ -15,16 +15,18 @@ import displayer as disp
from tk_scene import TkSceneRoot
class Scene(object):
def __init__(self,
display_config = PRODUCTION_QUALITY_DISPLAY_CONFIG,
construct_args = [],
background = None,
start_dither_time = DEFAULT_DITHER_TIME):
self.display_config = display_config
self.frame_duration = display_config["frame_duration"]
DEFAULT_CONFIG = {
"display_config" : PRODUCTION_QUALITY_DISPLAY_CONFIG,
"construct_args" : [],
"background" : None,
"start_dither_time" : DEFAULT_DITHER_TIME
}
def __init__(self, **kwargs):
digest_config(self, Scene, kwargs)
self.frame_duration = self.display_config["frame_duration"]
self.frames = []
self.mobjects = []
if background:
if self.background:
self.original_background = np.array(background)
#TODO, Error checking?
else:
@ -35,7 +37,7 @@ class Scene(object):
self.background = self.original_background
self.shape = self.background.shape[:2]
#TODO, space shape
self.construct(*construct_args)
self.construct(*self.construct_args)
def construct(self):
pass #To be implemented in subclasses

View file

@ -65,21 +65,27 @@ def zero_to_one_interval():
return interval
class OpenInterval(Mobject):
def __init__(self, center = ORIGIN, width = 2, **kwargs):
def __init__(self, center_point = ORIGIN, width = 2, **kwargs):
digest_config(self, OpenInterval, kwargs, locals())
Mobject.__init__(self, **kwargs)
self.add(tex_mobject("(").shift(LEFT))
self.add(tex_mobject(")").shift(RIGHT))
scale_factor = width / 2.0
self.stretch(scale_factor, 0)
self.stretch(0.5+0.5*scale_factor, 1)
self.shift(center)
self.shift(center_point)
class Piano(ImageMobject):
SHOULD_BUFF_POINTS = False
DEFAULT_CONFIG = {
"should_buffer_points" : False,
"invert" : False,
"scale_value" : 0.5
}
def __init__(self, **kwargs):
ImageMobject.__init__(self, "piano_keyboard", invert = False)
digest_config(self, Piano, kwargs)
ImageMobject.__init__(self, "piano_keyboard")
jump = self.get_width()/24
self.scale(0.5).center()
self.center()
self.half_note_jump = self.get_width()/24
self.ivory_jump = self.get_width()/14
@ -100,28 +106,26 @@ class Piano(ImageMobject):
class VibratingString(Animation):
def __init__(self,
num_periods = 1,
overtones = 4,
amplitude = 0.5,
radius = INTERVAL_RADIUS,
center = ORIGIN,
color = "white",
run_time = 3.0,
alpha_func = None,
**kwargs):
self.radius = radius
self.center = center
DEFAULT_CONFIG = {
"num_periods" : 1,
"overtones" : 4,
"amplitude" : 0.5,
"radius" : INTERVAL_RADIUS,
"center" : ORIGIN,
"color" : "white",
"run_time" : 3.0,
"alpha_func" : None
}
def __init__(self, **kwargs):
digest_config(self, VibratingString, kwargs)
def func(x, t):
return sum([
(amplitude/((k+1)**2.5))*np.sin(2*mult*t)*np.sin(k*mult*x)
for k in range(overtones)
for mult in [(num_periods+k)*np.pi]
(self.amplitude/((k+1)**2.5))*np.sin(2*mult*t)*np.sin(k*mult*x)
for k in range(self.overtones)
for mult in [(self.num_periods+k)*np.pi]
])
self.func = func
kwargs["run_time"] = run_time
kwargs["alpha_func"] = alpha_func
Animation.__init__(self, Mobject1D(color = color), **kwargs)
Animation.__init__(self, Mobject1D(color = self.color), **kwargs)
def update_mobject(self, alpha):
self.mobject.init_points()