mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
Applied digest_config to almost all __init__ functions
This commit is contained in:
parent
8f2a8f032e
commit
0d60cf6207
14 changed files with 436 additions and 397 deletions
|
@ -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):
|
||||||
|
|
|
@ -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'
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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,
|
||||||
|
|
19
helpers.py
19
helpers.py
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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()
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Add table
Reference in a new issue