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 from mobject import Mobject, Point
class Animation(object): class Animation(object):
def __init__(self, DEFAULT_CONFIG = {
mobject, "run_time" : DEFAULT_ANIMATION_RUN_TIME,
run_time = DEFAULT_ANIMATION_RUN_TIME, "alpha_func" : smooth,
alpha_func = smooth, "name" : None,
name = None): }
def __init__(self, mobject, **kwargs):
if isinstance(mobject, type) and issubclass(mobject, Mobject): if isinstance(mobject, type) and issubclass(mobject, Mobject):
self.mobject = mobject() mobject = mobject()
elif isinstance(mobject, Mobject): elif not isinstance(mobject, Mobject):
self.mobject = mobject
else:
raise Exception("Invalid mobject parameter, must be \ raise Exception("Invalid mobject parameter, must be \
subclass or instance of Mobject") subclass or instance of Mobject")
digest_config(self, Animation, kwargs, locals())
self.starting_mobject = copy.deepcopy(self.mobject) self.starting_mobject = copy.deepcopy(self.mobject)
self.alpha_func = alpha_func or (lambda x : x) if self.alpha_func is None:
self.run_time = run_time self.alpha_func = (lambda x : x)
#TODO, Adress the idea of filtering the animation if self.name is None:
self.filter_functions = [] self.name = self.__class__.__name__ + str(self.mobject)
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)
self.update(0) self.update(0)
def __str__(self): def __str__(self):

View file

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

View file

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

View file

@ -2,7 +2,7 @@ import os
import numpy as np import numpy as np
GENERALLY_BUFF_POINTS = True GENERALLY_BUFFER_POINTS = True
PRODUCTION_QUALITY_DISPLAY_CONFIG = { PRODUCTION_QUALITY_DISPLAY_CONFIG = {
"height" : 1440, "height" : 1440,
@ -27,14 +27,16 @@ SPACE_HEIGHT = 4.0
SPACE_WIDTH = SPACE_HEIGHT * DEFAULT_WIDTH / DEFAULT_HEIGHT 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 #All in seconds
DEFAULT_FRAME_DURATION = 0.04 DEFAULT_FRAME_DURATION = 0.04
DEFAULT_ANIMATION_RUN_TIME = 1.0 DEFAULT_ANIMATION_RUN_TIME = 1.0
DEFAULT_TRANSFORM_RUN_TIME = 1.0 DEFAULT_TRANSFORM_RUN_TIME = 1.0
DEFAULT_DITHER_TIME = 1.0 DEFAULT_DITHER_TIME = 1.0
DEFAULT_NUM_STARS = 1000
ORIGIN = np.array(( 0, 0, 0)) ORIGIN = np.array(( 0, 0, 0))
UP = np.array(( 0, 1, 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)) flattener = np.array([1, width], dtype = 'int').reshape((2, 1))
indices = np.dot(points, flattener) indices = np.dot(points, flattener)
indices = indices.reshape(indices.size) indices = indices.reshape(indices.size)
if mobject.should_buffer_points():#Is this alright? if mobject.should_buffer_points:#Is this alright?
for tweak in [ for tweak in [
indices + 1, indices + 1,
indices + width, indices + width,

View file

@ -9,6 +9,25 @@ import operator as op
from constants import * 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): def interpolate(start, end, alpha):
return (1-alpha)*start + alpha*end return (1-alpha)*start + alpha*end

View file

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

View file

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

View file

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

View file

@ -8,28 +8,24 @@ from helpers import *
class Point(Mobject): class Point(Mobject):
DEFAULT_COLOR = "black" DEFAULT_CONFIG = {
def __init__(self, location = ORIGIN, *args, **kwargs): "color" : "black",
self.location = np.array(location) }
Mobject.__init__(self, *args, **kwargs) def __init__(self, location = ORIGIN, **kwargs):
digest_config(self, Point, kwargs, locals())
Mobject.__init__(self, **kwargs)
def generate_points(self): 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 class Dot(Mobject1D): #Use 1D density, even though 2D
DEFAULT_COLOR = "white" DEFAULT_CONFIG = {
DEFAULT_RADIUS = 0.05 "radius" : 0.05
def __init__(self, center = ORIGIN, radius = DEFAULT_RADIUS, }
*args, **kwargs): def __init__(self, center_point = ORIGIN, **kwargs):
center = np.array(center) digest_config(self, Dot, kwargs, locals())
if center.size == 1: Mobject1D.__init__(self, **kwargs)
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)
def generate_points(self): def generate_points(self):
self.add_points([ self.add_points([
@ -40,22 +36,30 @@ class Dot(Mobject1D): #Use 1D density, even though 2D
]) ])
class Cross(Mobject1D): class Cross(Mobject1D):
RADIUS = 0.3 DEFAULT_CONFIG = {
DEFAULT_COLOR = "white" "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): def generate_points(self):
self.add_points([ self.add_points([
(sgn * x, x, 0) (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] for sgn in [-1, 1]
]) ])
self.shift(self.center_point)
class Line(Mobject1D): class Line(Mobject1D):
MIN_DENSITY = 0.1 DEFAULT_CONFIG = {
def __init__(self, start, end, density = DEFAULT_POINT_DENSITY_1D, "min_density" : 0.1
*args, **kwargs): }
def __init__(self, start, end, **kwargs):
digest_config(self, Line, kwargs)
self.set_start_and_end(start, end) self.set_start_and_end(start, end)
density *= max(self.get_length(), self.MIN_DENSITY) Mobject1D.__init__(self, **kwargs)
Mobject1D.__init__(self, density = density, *args, **kwargs)
def set_start_and_end(self, start, end): def set_start_and_end(self, start, end):
preliminary_start, preliminary_end = [ preliminary_start, preliminary_end = [
@ -74,9 +78,11 @@ class Line(Mobject1D):
] ]
def generate_points(self): def generate_points(self):
length = np.linalg.norm(self.end - self.start)
epsilon = self.epsilon / max(length, self.min_density)
self.add_points([ self.add_points([
interpolate(self.start, self.end, t) 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): def get_length(self):
@ -90,22 +96,21 @@ class Line(Mobject1D):
return rise/run return rise/run
class Arrow(Line): class Arrow(Line):
DEFAULT_COLOR = "white" DEFAULT_CONFIG = {
DEFAULT_TIP_LENGTH = 0.25 "color" : "white",
"tip_length" : 0.25
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
if "tip_length" in kwargs: digest_config(self, Arrow, kwargs)
tip_length = kwargs.pop("tip_length")
else:
tip_length = self.DEFAULT_TIP_LENGTH
Line.__init__(self, *args, **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 = self.start-self.end
vect = vect*tip_length/np.linalg.norm(vect) vect = vect*self.tip_length/np.linalg.norm(vect)
self.add_points([ self.add_points([
interpolate(self.end, self.end+v, t) 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 [ for v in [
rotate_vector(vect, np.pi/4, axis) rotate_vector(vect, np.pi/4, axis)
for axis in IN, OUT for axis in IN, OUT
@ -113,7 +118,7 @@ class Arrow(Line):
]) ])
class CurvedLine(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) self.set_start_and_end(start, end)
if via == None: if via == None:
self.via = rotate_vector( self.via = rotate_vector(
@ -124,7 +129,7 @@ class CurvedLine(Line):
self.via = via.get_center() self.via = via.get_center()
else: else:
self.via = via self.via = via
Line.__init__(self, start, end, *args, **kwargs) Line.__init__(self, start, end, **kwargs)
def generate_points(self): def generate_points(self):
self.add_points([ self.add_points([
@ -137,9 +142,12 @@ class CurvedLine(Line):
]) ])
class Circle(Mobject1D): class Circle(Mobject1D):
DEFAULT_COLOR = "red" DEFAULT_CONFIG = {
def __init__(self, radius = 1.0, **kwargs): "color" : "red",
self.radius = radius "radius" : 1.0
}
def __init__(self, **kwargs):
digest_config(self, Circle, kwargs)
Mobject1D.__init__(self, **kwargs) Mobject1D.__init__(self, **kwargs)
def generate_points(self): def generate_points(self):
@ -149,9 +157,13 @@ class Circle(Mobject1D):
]) ])
class Rectangle(Mobject1D): class Rectangle(Mobject1D):
DEFAULT_COLOR = "yellow" DEFAULT_CONFIG = {
def __init__(self, height = 2.0, width = 2.0, **kwargs): "color" : "yellow",
self.height, self.width = height, width "height" : 2.0,
"width" : 4.0
}
def __init__(self, **kwargs):
digest_config(self, Rectangle, kwargs)
Mobject1D.__init__(self, **kwargs) Mobject1D.__init__(self, **kwargs)
def generate_points(self): def generate_points(self):
@ -164,17 +176,27 @@ class Rectangle(Mobject1D):
]) ])
class Square(Rectangle): class Square(Rectangle):
def __init__(self, side_length = 2.0, **kwargs): DEFAULT_CONFIG = {
Rectangle.__init__(self, side_length, side_length, **kwargs) "height" : 2.0,
"width" : 2.0,
}
def __init__(self, **kwargs):
digest_config(self, Square, kwargs)
Rectangle.__init__(self, **kwargs)
class Bubble(Mobject): class Bubble(Mobject):
def __init__(self, direction = LEFT, index_of_tip = -1, center = ORIGIN): DEFAULT_CONFIG = {
self.direction = direction "direction" : LEFT,
self.content = Mobject() "index_of_tip" : -1,
self.index_of_tip = index_of_tip "center_point" : ORIGIN,
self.center_offset = center - Mobject.get_center(self) }
if direction[0] > 0: 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.rotate(np.pi, UP)
self.content = Mobject()
def get_tip(self): def get_tip(self):
return self.points[self.index_of_tip] return self.points[self.index_of_tip]
@ -217,13 +239,19 @@ class Bubble(Mobject):
return self return self
class SpeechBubble(Bubble): class SpeechBubble(Bubble):
INITIAL_WIDTH = 4 DEFAULT_CONFIG = {
INITIAL_HEIGHT = 2 "initial_width" : 4,
def __init__(self, *args, **kwargs): "initial_height" : 2,
Mobject.__init__(self, *args, **kwargs) }
def __init__(self, **kwargs):
digest_config(self, SpeechBubble, kwargs)
Bubble.__init__(self, **kwargs)
def generate_points(self):
complex_power = 0.9 complex_power = 0.9
radius = self.INITIAL_WIDTH/2 radius = self.initial_width/2
circle = Circle(density = radius*DEFAULT_POINT_DENSITY_1D) circle = Circle(radius = radius)
circle.scale(1.0/radius)
circle.apply_complex_function(lambda z : z**complex_power) circle.apply_complex_function(lambda z : z**complex_power)
circle.scale(radius) circle.scale(radius)
boundary_point_as_complex = radius*complex(-1)**complex_power boundary_point_as_complex = radius*complex(-1)**complex_power
@ -243,28 +271,28 @@ class SpeechBubble(Bubble):
) )
self.highlight("white") self.highlight("white")
self.rotate(np.pi/2) self.rotate(np.pi/2)
self.points[:,1] *= float(self.INITIAL_HEIGHT)/self.INITIAL_WIDTH self.points[:,1] *= float(self.initial_height)/self.initial_width
Bubble.__init__(self, direction = LEFT)
class ThoughtBubble(Bubble): class ThoughtBubble(Bubble):
NUM_BULGES = 7 DEFAULT_CONFIG = {
INITIAL_INNER_RADIUS = 1.8 "num_bulges" : 7,
INITIAL_WIDTH = 6 "initial_inner_radius" : 1.8,
def __init__(self, *args, **kwargs): "initial_width" : 6
Mobject.__init__(self, *args, **kwargs) }
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.15).shift(2.5*DOWN+2*LEFT))
self.add(Circle().scale(0.3).shift(2*DOWN+1.5*LEFT)) self.add(Circle().scale(0.3).shift(2*DOWN+1.5*LEFT))
for n in range(self.NUM_BULGES): for n in range(self.num_bulges):
theta = 2*np.pi*n/self.NUM_BULGES theta = 2*np.pi*n/self.num_bulges
self.add(Circle().shift((np.cos(theta), np.sin(theta), 0))) 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.filter_out(lambda p : np.linalg.norm(p) < self.initial_inner_radius)
self.stretch_to_fit_width(self.INITIAL_WIDTH) self.stretch_to_fit_width(self.initial_width)
self.highlight("white") 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 * from helpers import *
class Stars(Mobject): class Stars(Mobject):
DEFAULT_COLOR = "white" DEFAULT_CONFIG = {
SHOULD_BUFF_POINTS = False "should_buffer_points" : False,
def __init__(self, "radius" : SPACE_WIDTH,
radius = SPACE_WIDTH, "num_points" : 1000,
num_points = DEFAULT_NUM_STARS, }
*args, **kwargs): def __init__(self, **kwargs):
self.num_points = num_points digest_config(self, Stars, kwargs)
self.radius = radius Mobject.__init__(self, **kwargs)
Mobject.__init__(self, *args, **kwargs)
def generate_points(self): def generate_points(self):
self.add_points([ self.add_points([
@ -42,12 +41,12 @@ class CubeWithFaces(Mobject2D):
for sgn in [-1, 1] for sgn in [-1, 1]
]) ])
self.pose_at_angle() self.pose_at_angle()
self.set_color("skyblue")
def unit_normal(self, coords): def unit_normal(self, coords):
return np.array(map(lambda x : 1 if abs(x) == 1 else 0, coords)) return np.array(map(lambda x : 1 if abs(x) == 1 else 0, coords))
class Cube(Mobject1D): class Cube(Mobject1D):
DEFAULT_COLOR = "yellow"
def generate_points(self): def generate_points(self):
self.add_points([ self.add_points([
([a, b, c][p[0]], [a, b, c][p[1]], [a, b, c][p[2]]) ([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)) for a, b, c in it.product([-1, 1], [-1, 1], np.arange(-1, 1, self.epsilon))
]) ])
self.pose_at_angle() self.pose_at_angle()
self.set_color("yellow")
class Octohedron(Mobject1D): class Octohedron(Mobject1D):
DEFAULT_COLOR = "pink"
def generate_points(self): def generate_points(self):
x = np.array([1, 0, 0]) x = np.array([1, 0, 0])
y = np.array([0, 1, 0]) y = np.array([0, 1, 0])
@ -72,9 +71,9 @@ class Octohedron(Mobject1D):
Line(pair[0], pair[1], density = 1/self.epsilon).points Line(pair[0], pair[1], density = 1/self.epsilon).points
) )
self.pose_at_angle() self.pose_at_angle()
self.set_color("pink")
class Dodecahedron(Mobject1D): class Dodecahedron(Mobject1D):
DEFAULT_COLOR = "limegreen"
def generate_points(self): def generate_points(self):
phi = (1 + np.sqrt(5)) / 2 phi = (1 + np.sqrt(5)) / 2
x = np.array([1, 0, 0]) x = np.array([1, 0, 0])
@ -99,6 +98,7 @@ class Dodecahedron(Mobject1D):
matrix = b*np.array([x[perm], y[perm], z[perm]]) matrix = b*np.array([x[perm], y[perm], z[perm]])
self.add_points(np.dot(five_lines_points, matrix)) self.add_points(np.dot(five_lines_points, matrix))
self.pose_at_angle() self.pose_at_angle()
self.set_color("limegreen")
class Sphere(Mobject2D): class Sphere(Mobject2D):
def generate_points(self): def generate_points(self):
@ -111,6 +111,9 @@ class Sphere(Mobject2D):
for phi in np.arange(self.epsilon, np.pi, self.epsilon) 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)) for theta in np.arange(0, 2 * np.pi, 2 * self.epsilon / np.sin(phi))
]) ])
self.set_color("blue")
def unit_normal(self, coords): 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): class SampleScene(Scene):
def construct(self): def construct(self):
pass square = Square()
self.add(square)
self.dither()
self.play(Flash(square))
if __name__ == "__main__": if __name__ == "__main__":
command_line_create_scene() command_line_create_scene()

View file

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

View file

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