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
|
||||
|
||||
class Animation(object):
|
||||
def __init__(self,
|
||||
mobject,
|
||||
run_time = DEFAULT_ANIMATION_RUN_TIME,
|
||||
alpha_func = smooth,
|
||||
name = None):
|
||||
DEFAULT_CONFIG = {
|
||||
"run_time" : DEFAULT_ANIMATION_RUN_TIME,
|
||||
"alpha_func" : smooth,
|
||||
"name" : None,
|
||||
}
|
||||
def __init__(self, mobject, **kwargs):
|
||||
if isinstance(mobject, type) and issubclass(mobject, Mobject):
|
||||
self.mobject = mobject()
|
||||
elif isinstance(mobject, Mobject):
|
||||
self.mobject = mobject
|
||||
else:
|
||||
mobject = mobject()
|
||||
elif not isinstance(mobject, Mobject):
|
||||
raise Exception("Invalid mobject parameter, must be \
|
||||
subclass or instance of Mobject")
|
||||
digest_config(self, Animation, kwargs, locals())
|
||||
self.starting_mobject = copy.deepcopy(self.mobject)
|
||||
self.alpha_func = alpha_func or (lambda x : x)
|
||||
self.run_time = run_time
|
||||
#TODO, Adress the idea of filtering the animation
|
||||
self.filter_functions = []
|
||||
self.restricted_height = SPACE_HEIGHT
|
||||
self.restricted_width = SPACE_WIDTH
|
||||
self.spacial_center = np.zeros(3)
|
||||
self.name = name or self.__class__.__name__ + str(self.mobject)
|
||||
if self.alpha_func is None:
|
||||
self.alpha_func = (lambda x : x)
|
||||
if self.name is None:
|
||||
self.name = self.__class__.__name__ + str(self.mobject)
|
||||
self.update(0)
|
||||
|
||||
def __str__(self):
|
||||
|
|
|
@ -16,13 +16,15 @@ class DelayByOrder(Animation):
|
|||
Warning: This will not work on all animation types, but
|
||||
when it does, it will be pretty cool
|
||||
"""
|
||||
def __init__(self, animation, max_power = 5, **kwargs):
|
||||
self.animation = animation
|
||||
self.max_power = max_power
|
||||
kwargs = dict([
|
||||
DEFAULT_CONFIG = {
|
||||
"max_power" : 5
|
||||
}
|
||||
def __init__(self, animation, **kwargs):
|
||||
digest_config(self, DelayByOrder, kwargs, locals())
|
||||
kwargs.update(dict([
|
||||
(attr, getattr(animation, attr))
|
||||
for attr in "run_time", "alpha_func"
|
||||
])
|
||||
for attr in Animation.DEFAULT_CONFIG
|
||||
]))
|
||||
self.num_mobject_points = animation.mobject.get_num_points()
|
||||
Animation.__init__(self, animation.mobject, **kwargs)
|
||||
self.name = self.__class__.__name__ + str(self.animation)
|
||||
|
@ -38,45 +40,31 @@ class DelayByOrder(Animation):
|
|||
self.animation.update_mobject(alpha_array)
|
||||
|
||||
class Rotating(Animation):
|
||||
def __init__(self,
|
||||
mobject,
|
||||
axis = None,
|
||||
axes = [RIGHT, UP],
|
||||
radians = 2 * np.pi,
|
||||
run_time = 20.0,
|
||||
alpha_func = None,
|
||||
*args, **kwargs):
|
||||
Animation.__init__(
|
||||
self, mobject,
|
||||
run_time = run_time,
|
||||
alpha_func = alpha_func,
|
||||
*args, **kwargs
|
||||
)
|
||||
self.axes = [axis] if axis is not None else axes
|
||||
self.radians = radians
|
||||
DEFAULT_CONFIG = {
|
||||
"axes" : [RIGHT, UP],
|
||||
"radians" : 2*np.pi,
|
||||
"run_time" : 20.0,
|
||||
"alpha_func" : None,
|
||||
}
|
||||
def __init__(self, mobject, **kwargs):
|
||||
digest_config(self, Rotating, kwargs, locals())
|
||||
Animation.__init__(self, mobject, **kwargs)
|
||||
|
||||
def update_mobject(self, alpha):
|
||||
self.mobject.points = self.starting_mobject.points
|
||||
for axis in self.axes:
|
||||
self.mobject.rotate(
|
||||
self.radians * alpha,
|
||||
axis
|
||||
)
|
||||
self.mobject.rotate(self.radians * alpha, axis)
|
||||
|
||||
class RotationAsTransform(Rotating):
|
||||
def __init__(self, mobject, radians, axis = IN, axes = None,
|
||||
run_time = DEFAULT_ANIMATION_RUN_TIME,
|
||||
alpha_func = smooth,
|
||||
*args, **kwargs):
|
||||
Rotating.__init__(
|
||||
self,
|
||||
mobject,
|
||||
axis = axis,
|
||||
axes = axes,
|
||||
run_time = run_time,
|
||||
radians = radians,
|
||||
alpha_func = alpha_func,
|
||||
)
|
||||
DEFAULT_CONFIG = {
|
||||
"axes" : [IN],
|
||||
"radians" : np.pi / 2,
|
||||
"run_time" : DEFAULT_ANIMATION_RUN_TIME,
|
||||
"alpha_func" : smooth,
|
||||
}
|
||||
def __init__(self, mobject, **kwargs):
|
||||
digest_config(self, RotationAsTransform, kwargs, locals())
|
||||
Rotating.__init__(self, mobject, **kwargs)
|
||||
|
||||
class FadeOut(Animation):
|
||||
def update_mobject(self, alpha):
|
||||
|
@ -90,9 +78,9 @@ class FadeIn(Animation):
|
|||
#TODO, Why do you need to do this? Shouldn't points always align?
|
||||
|
||||
class ShimmerIn(DelayByOrder):
|
||||
def __init__(self, mobject, *args, **kwargs):
|
||||
def __init__(self, mobject, **kwargs):
|
||||
mobject.sort_points(lambda p : np.dot(p, DOWN+RIGHT))
|
||||
DelayByOrder.__init__(self, FadeIn(mobject, *args, **kwargs))
|
||||
DelayByOrder.__init__(self, FadeIn(mobject, **kwargs))
|
||||
|
||||
|
||||
class ShowCreation(Animation):
|
||||
|
@ -107,21 +95,22 @@ class ShowCreation(Animation):
|
|||
)
|
||||
|
||||
class Flash(Animation):
|
||||
def __init__(self, mobject, color = "white", slow_factor = 0.01,
|
||||
run_time = 0.1, alpha_func = None,
|
||||
*args, **kwargs):
|
||||
Animation.__init__(self, mobject, run_time = run_time,
|
||||
alpha_func = alpha_func,
|
||||
*args, **kwargs)
|
||||
self.intermediate = Mobject(color = color)
|
||||
DEFAULT_CONFIG = {
|
||||
"color" : "white",
|
||||
"slow_factor" : 0.01,
|
||||
"run_time" : 0.1,
|
||||
"alpha_func" : None,
|
||||
}
|
||||
def __init__(self, mobject, **kwargs):
|
||||
digest_config(self, Flash, kwargs, locals())
|
||||
self.intermediate = Mobject(color = self.color)
|
||||
self.intermediate.add_points([
|
||||
point + (x, y, 0)
|
||||
for point in self.mobject.points
|
||||
for x in [-1, 1]
|
||||
for y in [-1, 1]
|
||||
])
|
||||
self.reference_mobjects.append(self.intermediate)
|
||||
self.slow_factor = slow_factor
|
||||
Animation.__init__(self, mobject, **kwargs)
|
||||
|
||||
def update_mobject(self, alpha):
|
||||
#Makes alpha go from 0 to slow_factor to 0 instead of 0 to 1
|
||||
|
@ -134,12 +123,12 @@ class Flash(Animation):
|
|||
)
|
||||
|
||||
class Homotopy(Animation):
|
||||
def __init__(self, homotopy, *args, **kwargs):
|
||||
def __init__(self, homotopy, **kwargs):
|
||||
"""
|
||||
Homotopy a function from (x, y, z, t) to (x', y', z')
|
||||
"""
|
||||
self.homotopy = homotopy
|
||||
Animation.__init__(self, *args, **kwargs)
|
||||
digest_config(self, Homotopy, kwargs, locals())
|
||||
Animation.__init__(self, **kwargs)
|
||||
|
||||
def update_mobject(self, alpha):
|
||||
self.mobject.points = np.array([
|
||||
|
@ -148,7 +137,7 @@ class Homotopy(Animation):
|
|||
])
|
||||
|
||||
class ComplexHomotopy(Homotopy):
|
||||
def __init__(self, complex_homotopy, *args, **kwargs):
|
||||
def __init__(self, complex_homotopy, **kwargs):
|
||||
"""
|
||||
Complex Hootopy a function (z, t) to z'
|
||||
"""
|
||||
|
|
|
@ -5,7 +5,7 @@ import copy
|
|||
import warnings
|
||||
|
||||
from animation import Animation
|
||||
from mobject import Mobject, Point
|
||||
from mobject import Mobject, Point, Grid
|
||||
from constants import *
|
||||
from helpers import *
|
||||
|
||||
|
@ -31,25 +31,25 @@ def counterclockwise_path(start_points, end_points, alpha):
|
|||
return semi_circular_path(start_points, end_points, alpha, OUT)
|
||||
|
||||
class Transform(Animation):
|
||||
def __init__(self, mobject1, mobject2,
|
||||
run_time = DEFAULT_TRANSFORM_RUN_TIME,
|
||||
interpolation_function = straight_path,
|
||||
black_out_extra_points = False,
|
||||
*args, **kwargs):
|
||||
self.interpolation_function = interpolation_function
|
||||
count1, count2 = mobject1.get_num_points(), mobject2.get_num_points()
|
||||
DEFAULT_CONFIG = {
|
||||
"run_time" : DEFAULT_TRANSFORM_RUN_TIME,
|
||||
"interpolation_function" : straight_path,
|
||||
"should_black_out_extra_points" : False
|
||||
}
|
||||
def __init__(self, mobject, ending_mobject, **kwargs):
|
||||
digest_config(self, Transform, kwargs, locals())
|
||||
count1, count2 = mobject.get_num_points(), ending_mobject.get_num_points()
|
||||
if count2 == 0:
|
||||
mobject2.add_points([SPACE_WIDTH*RIGHT+SPACE_HEIGHT*UP])
|
||||
count2 = mobject2.get_num_points()
|
||||
Mobject.align_data(mobject1, mobject2)
|
||||
self.ending_mobject = mobject2
|
||||
if black_out_extra_points and count2 < count1:
|
||||
ending_mobject.add_points([SPACE_WIDTH*RIGHT+SPACE_HEIGHT*UP])
|
||||
count2 = ending_mobject.get_num_points()
|
||||
Mobject.align_data(mobject, ending_mobject)
|
||||
if self.should_black_out_extra_points and count2 < count1:
|
||||
self.black_out_extra_points(count1, count2)
|
||||
|
||||
Animation.__init__(self, mobject1, run_time = run_time, *args, **kwargs)
|
||||
self.name += "To" + str(mobject2)
|
||||
self.mobject.SHOULD_BUFF_POINTS = \
|
||||
mobject1.SHOULD_BUFF_POINTS and mobject2.SHOULD_BUFF_POINTS
|
||||
Animation.__init__(self, mobject, **kwargs)
|
||||
self.name += "To" + str(ending_mobject)
|
||||
self.mobject.should_buffer_points = \
|
||||
mobject.should_buffer_points and ending_mobject.should_buffer_points
|
||||
|
||||
def black_out_extra_points(self, count1, count2):
|
||||
#Ensure redundant pixels fade to black
|
||||
|
@ -89,53 +89,28 @@ class Transform(Animation):
|
|||
)
|
||||
|
||||
class ClockwiseTransform(Transform):
|
||||
def __init__(self, mobject1, mobject2, **kwargs):
|
||||
Transform.__init__(
|
||||
self, mobject1, mobject2,
|
||||
interpolation_function = clockwise_path, **kwargs
|
||||
)
|
||||
DEFAULT_CONFIG = {
|
||||
"interpolation_function" : clockwise_path
|
||||
}
|
||||
def __init__(self, *args, **kwargs):
|
||||
digest_config(self, ClockwiseTransform, kwargs)
|
||||
Transform.__init__(self, *args, **kwargs)
|
||||
|
||||
class CounterclockwiseTransform(Transform):
|
||||
def __init__(self, mobject1, mobject2, **kwargs):
|
||||
Transform.__init__(
|
||||
self, mobject1, mobject2,
|
||||
interpolation_function = counterclockwise_path, **kwargs
|
||||
)
|
||||
DEFAULT_CONFIG = {
|
||||
"interpolation_function" : counterclockwise_path
|
||||
}
|
||||
def __init__(self, *args, **kwargs):
|
||||
digest_config(self, ClockwiseTransform, kwargs)
|
||||
Transform.__init__(self, *args, **kwargs)
|
||||
|
||||
class SpinInFromNothing(Transform):
|
||||
DEFAULT_CONFIG = {
|
||||
"interpolation_function" : counterclockwise_path
|
||||
}
|
||||
def __init__(self, mob, **kwargs):
|
||||
name = "interpolation_function"
|
||||
interp_func = kwargs[name] if name in kwargs else counterclockwise_path
|
||||
dot = Point(mob.get_center(), color = "black")
|
||||
Transform.__init__(
|
||||
self, dot, mob,
|
||||
interpolation_function = interp_func,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
||||
class FadeToColor(Transform):
|
||||
def __init__(self, mobject, color, *args, **kwargs):
|
||||
target = copy.deepcopy(mobject).highlight(color)
|
||||
Transform.__init__(self, mobject, target, *args, **kwargs)
|
||||
|
||||
class Highlight(FadeToColor):
|
||||
def __init__(self, mobject, color = "red",
|
||||
run_time = DEFAULT_ANIMATION_RUN_TIME,
|
||||
alpha_func = there_and_back, *args, **kwargs):
|
||||
FadeToColor.__init__(
|
||||
self, mobject, color,
|
||||
run_time = run_time,
|
||||
alpha_func = alpha_func,
|
||||
*args, **kwargs
|
||||
)
|
||||
|
||||
class ScaleInPlace(Transform):
|
||||
def __init__(self, mobject, scale_factor, *args, **kwargs):
|
||||
target = copy.deepcopy(mobject)
|
||||
center = mobject.get_center()
|
||||
target.shift(-center).scale(scale_factor).shift(center)
|
||||
Transform.__init__(self, mobject, target, *args, **kwargs)
|
||||
digest_config(self, SpinInFromNothing, kwargs)
|
||||
Transform.__init__(self, Point(mob.get_center()), mob, **kwargs)
|
||||
|
||||
class ApplyMethod(Transform):
|
||||
def __init__(self, method, *args, **kwargs):
|
||||
|
@ -155,6 +130,14 @@ class ApplyMethod(Transform):
|
|||
**kwargs
|
||||
)
|
||||
|
||||
class FadeToColor(ApplyMethod):
|
||||
def __init__(self, mobject, color, **kwargs):
|
||||
ApplyMethod.__init__(self, mobject.highlight, color, **kwargs)
|
||||
|
||||
class ScaleInPlace(ApplyMethod):
|
||||
def __init__(self, mobject, scale_factor, **kwargs):
|
||||
ApplyMethod.__init__(self, mobject.scale_in_place, scale_factor, **kwargs)
|
||||
|
||||
class ApplyFunction(Transform):
|
||||
def __init__(self, function, mobject, **kwargs):
|
||||
Transform.__init__(
|
||||
|
@ -165,16 +148,15 @@ class ApplyFunction(Transform):
|
|||
)
|
||||
self.name = "ApplyFunctionTo"+str(mobject)
|
||||
|
||||
|
||||
class ApplyPointwiseFunction(Transform):
|
||||
def __init__(self, function, mobject,
|
||||
run_time = DEFAULT_ANIMATION_RUN_TIME, **kwargs):
|
||||
DEFAULT_CONFIG = {
|
||||
"run_time" : DEFAULT_ANIMATION_RUN_TIME
|
||||
}
|
||||
def __init__(self, function, mobject, **kwargs):
|
||||
digest_config(self, ApplyPointwiseFunction, kwargs)
|
||||
map_image = copy.deepcopy(mobject)
|
||||
map_image.points = np.array(map(function, map_image.points))
|
||||
Transform.__init__(
|
||||
self, mobject, map_image,
|
||||
run_time = run_time, **kwargs
|
||||
)
|
||||
Transform.__init__(self, mobject, map_image, **kwargs)
|
||||
self.name = "".join([
|
||||
"Apply",
|
||||
"".join([s.capitalize() for s in function.__name__.split("_")]),
|
||||
|
@ -201,22 +183,25 @@ class ComplexFunction(ApplyPointwiseFunction):
|
|||
|
||||
|
||||
class TransformAnimations(Transform):
|
||||
def __init__(self, start_anim, end_anim,
|
||||
alpha_func = squish_alpha_func(smooth),
|
||||
**kwargs):
|
||||
DEFAULT_CONFIG = {
|
||||
"alpha_func" : squish_alpha_func(smooth)
|
||||
}
|
||||
def __init__(self, start_anim, end_anim, **kwargs):
|
||||
digest_config(self, TransformAnimations, kwargs, locals())
|
||||
if "run_time" in kwargs:
|
||||
run_time = kwargs.pop("run_time")
|
||||
self.run_time = kwargs.pop("run_time")
|
||||
else:
|
||||
self.run_time = max(start_anim.run_time, end_anim.run_time)
|
||||
for anim in start_anim, end_anim:
|
||||
anim.set_run_time(self.run_time)
|
||||
|
||||
if start_anim.starting_mobject.get_num_points() != end_anim.starting_mobject.get_num_points():
|
||||
Mobject.align_data(start_anim.starting_mobject, end_anim.starting_mobject)
|
||||
for anim in start_anim, end_anim:
|
||||
anim.set_run_time(run_time)
|
||||
self.start_anim, self.end_anim = start_anim, end_anim
|
||||
Transform.__init__(
|
||||
self,
|
||||
start_anim.mobject,
|
||||
end_anim.mobject,
|
||||
run_time = max(start_anim.run_time, end_anim.run_time),
|
||||
alpha_func = alpha_func,
|
||||
**kwargs
|
||||
)
|
||||
if hasattr(anim, "ending_mobject"):
|
||||
Mobject.align_data(anim.starting_mobject, anim.ending_mobject)
|
||||
|
||||
Transform.__init__(self, start_anim.mobject, end_anim.mobject, **kwargs)
|
||||
#Rewire starting and ending mobjects
|
||||
start_anim.mobject = self.starting_mobject
|
||||
end_anim.mobject = self.ending_mobject
|
||||
|
|
|
@ -2,7 +2,7 @@ import os
|
|||
import numpy as np
|
||||
|
||||
|
||||
GENERALLY_BUFF_POINTS = True
|
||||
GENERALLY_BUFFER_POINTS = True
|
||||
|
||||
PRODUCTION_QUALITY_DISPLAY_CONFIG = {
|
||||
"height" : 1440,
|
||||
|
@ -27,14 +27,16 @@ SPACE_HEIGHT = 4.0
|
|||
SPACE_WIDTH = SPACE_HEIGHT * DEFAULT_WIDTH / DEFAULT_HEIGHT
|
||||
|
||||
|
||||
DEFAULT_MOBJECT_TO_EDGE_BUFFER = 0.5
|
||||
DEFAULT_MOBJECT_TO_MOBJECT_BUFFER = 0.2
|
||||
|
||||
|
||||
#All in seconds
|
||||
DEFAULT_FRAME_DURATION = 0.04
|
||||
DEFAULT_ANIMATION_RUN_TIME = 1.0
|
||||
DEFAULT_TRANSFORM_RUN_TIME = 1.0
|
||||
DEFAULT_DITHER_TIME = 1.0
|
||||
|
||||
DEFAULT_NUM_STARS = 1000
|
||||
|
||||
|
||||
ORIGIN = np.array(( 0, 0, 0))
|
||||
UP = np.array(( 0, 1, 0))
|
||||
|
|
|
@ -69,7 +69,7 @@ def paint_mobject(mobject, image_array = None):
|
|||
flattener = np.array([1, width], dtype = 'int').reshape((2, 1))
|
||||
indices = np.dot(points, flattener)
|
||||
indices = indices.reshape(indices.size)
|
||||
if mobject.should_buffer_points():#Is this alright?
|
||||
if mobject.should_buffer_points:#Is this alright?
|
||||
for tweak in [
|
||||
indices + 1,
|
||||
indices + width,
|
||||
|
|
19
helpers.py
19
helpers.py
|
@ -9,6 +9,25 @@ import operator as op
|
|||
|
||||
from constants import *
|
||||
|
||||
def digest_config(obj, Class, kwargs, local_args = {}):
|
||||
"""
|
||||
To be used in initializing most-to-all objects.
|
||||
Sets key word args as local variables
|
||||
"""
|
||||
if hasattr(Class, "DEFAULT_CONFIG"):
|
||||
config = Class.DEFAULT_CONFIG.copy()
|
||||
else:
|
||||
config = {}
|
||||
for key in config.keys():
|
||||
if hasattr(obj, key):
|
||||
config.pop(key)
|
||||
if key in kwargs:
|
||||
config[key] = kwargs.pop(key)
|
||||
for key in local_args:
|
||||
if key not in ["self", "kwargs"]:
|
||||
config[key] = local_args[key]
|
||||
obj.__dict__.update(config)
|
||||
|
||||
def interpolate(start, end, alpha):
|
||||
return (1-alpha)*start + alpha*end
|
||||
|
||||
|
|
|
@ -7,41 +7,42 @@ from constants import *
|
|||
from helpers import *
|
||||
|
||||
class FunctionGraph(Mobject1D):
|
||||
DEFAULT_COLOR = "lightblue"
|
||||
def __init__(self, function, x_range = [-10, 10], *args, **kwargs):
|
||||
self.function = function
|
||||
self.x_min = x_range[0] / SPACE_WIDTH
|
||||
self.x_max = x_range[1] / SPACE_WIDTH
|
||||
Mobject1D.__init__(self, *args, **kwargs)
|
||||
DEFAULT_CONFIG = {
|
||||
"color" : "lightblue",
|
||||
"x_min" : -10,
|
||||
"x_max" : 10,
|
||||
"spacial_radius" : SPACE_WIDTH,
|
||||
}
|
||||
def __init__(self, function, **kwargs):
|
||||
digest_config(self, FunctionGraph, kwargs, locals())
|
||||
Mobject1D.__init__(self, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
scale_factor = 2.0 * SPACE_WIDTH / (self.x_max - self.x_min)
|
||||
self.epsilon /= scale_factor
|
||||
numerical_radius = (self.x_max - self.x_min)/2
|
||||
numerical_center = (self.x_max + self.x_min)/2
|
||||
ratio = numerical_radius / self.spacial_radius
|
||||
epsilon = self.epsilon * ratio
|
||||
self.add_points([
|
||||
np.array([x, self.function(x), 0])
|
||||
np.array([(x-numerical_center)/ratio, self.function(x), 0])
|
||||
for x in np.arange(self.x_min, self.x_max, self.epsilon)
|
||||
])
|
||||
self.scale(scale_factor)
|
||||
|
||||
|
||||
class ParametricFunction(Mobject):
|
||||
DEFAULT_COLOR = "white"
|
||||
def __init__(self,
|
||||
function,
|
||||
dim = 1,
|
||||
expected_measure = 10.0,
|
||||
density = None,
|
||||
*args,
|
||||
**kwargs):
|
||||
self.function = function
|
||||
self.dim = dim
|
||||
self.expected_measure = expected_measure
|
||||
if density:
|
||||
self.epsilon = 1.0 / density
|
||||
DEFAULT_CONFIG = {
|
||||
"color" : "white",
|
||||
"dim" : 1,
|
||||
"expected_measure" : 10.0,
|
||||
"density" : None
|
||||
}
|
||||
def __init__(self, function, **kwargs):
|
||||
digest_config(self, ParametricFunction, kwargs, locals())
|
||||
if self.density:
|
||||
self.epsilon = 1.0 / self.density
|
||||
elif self.dim == 1:
|
||||
self.epsilon = 1.0 / expected_measure / DEFAULT_POINT_DENSITY_1D
|
||||
self.epsilon = 1.0 / self.expected_measure / DEFAULT_POINT_DENSITY_1D
|
||||
else:
|
||||
self.epsilon = 1.0 / np.sqrt(expected_measure) / DEFAULT_POINT_DENSITY_2D
|
||||
self.epsilon = 1.0 / np.sqrt(self.expected_measure) / DEFAULT_POINT_DENSITY_2D
|
||||
Mobject.__init__(self, *args, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
|
@ -58,16 +59,15 @@ class ParametricFunction(Mobject):
|
|||
])
|
||||
|
||||
class Grid(Mobject1D):
|
||||
DEFAULT_COLOR = "green"
|
||||
def __init__(self,
|
||||
radius = max(SPACE_HEIGHT, SPACE_WIDTH),
|
||||
interval_size = 1.0,
|
||||
subinterval_size = 0.5,
|
||||
*args, **kwargs):
|
||||
self.radius = radius
|
||||
self.interval_size = interval_size
|
||||
self.subinterval_size = subinterval_size
|
||||
Mobject1D.__init__(self, *args, **kwargs)
|
||||
DEFAULT_CONFIG = {
|
||||
"color" : "green",
|
||||
"radius" : max(SPACE_HEIGHT, SPACE_WIDTH),
|
||||
"interval_size" : 1.0,
|
||||
"subinterval_size" : 0.5,
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, Grid, kwargs)
|
||||
Mobject1D.__init__(self, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
self.add_points([
|
||||
|
@ -91,27 +91,20 @@ class Grid(Mobject1D):
|
|||
], color = color)
|
||||
|
||||
class NumberLine(Mobject1D):
|
||||
def __init__(self,
|
||||
radius = SPACE_WIDTH,
|
||||
unit_length_to_spacial_width = 1,
|
||||
tick_size = 0.1,
|
||||
tick_frequency = 0.5,
|
||||
number_at_center = 0,
|
||||
numbers_with_elongated_ticks = [0],
|
||||
longer_tick_multiple = 2,
|
||||
**kwargs):
|
||||
#TODO, There must be better (but still safe) way to add all
|
||||
#these config arguments as attributes.
|
||||
self.radius = radius
|
||||
self.unit_length_to_spacial_width = unit_length_to_spacial_width
|
||||
self.tick_size = tick_size
|
||||
self.tick_frequency = tick_frequency
|
||||
self.numbers_with_elongated_ticks = numbers_with_elongated_ticks
|
||||
self.number_at_center = number_at_center
|
||||
self.longer_tick_multiple = longer_tick_multiple
|
||||
numerical_radius = float(radius) / unit_length_to_spacial_width
|
||||
self.left_num = number_at_center - numerical_radius
|
||||
self.right_num = number_at_center + numerical_radius
|
||||
DEFAULT_CONFIG = {
|
||||
"radius" : SPACE_WIDTH,
|
||||
"unit_length_to_spacial_width" : 1,
|
||||
"tick_size" : 0.1,
|
||||
"tick_frequency" : 0.5,
|
||||
"number_at_center" : 0,
|
||||
"numbers_with_elongated_ticks" : [0],
|
||||
"longer_tick_multiple" : 2,
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, NumberLine, kwargs)
|
||||
numerical_radius = float(self.radius) / self.unit_length_to_spacial_width
|
||||
self.left_num = self.number_at_center - numerical_radius
|
||||
self.right_num = self.number_at_center + numerical_radius
|
||||
Mobject1D.__init__(self, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
|
@ -170,15 +163,14 @@ class UnitInterval(NumberLine):
|
|||
"numbers_with_elongated_ticks" : [0, 1],
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
config = self.DEFAULT_CONFIG
|
||||
config.update(kwargs)
|
||||
NumberLine.__init__(self, **config)
|
||||
digest_config(self, UnitInterval, kwargs)
|
||||
NumberLine.__init__(self, **kwargs)
|
||||
|
||||
|
||||
class Axes(CompoundMobject):
|
||||
def __init__(self, *args, **kwargs):
|
||||
x_axis = NumberLine(*args, **kwargs)
|
||||
y_axis = NumberLine(*args, **kwargs).rotate(np.pi/2, OUT)
|
||||
def __init__(self, **kwargs):
|
||||
x_axis = NumberLine(**kwargs)
|
||||
y_axis = NumberLine(**kwargs).rotate(np.pi/2, OUT)
|
||||
CompoundMobject.__init__(self, x_axis, y_axis)
|
||||
|
||||
|
||||
|
|
|
@ -7,23 +7,24 @@ from random import random
|
|||
from tex_utils import *
|
||||
from mobject import *
|
||||
|
||||
class ImageMobject(Mobject2D):
|
||||
class ImageMobject(Mobject):
|
||||
"""
|
||||
Automatically filters out black pixels
|
||||
"""
|
||||
# SHOULD_BUFF_POINTS = False
|
||||
def __init__(self,
|
||||
image_file,
|
||||
filter_color = "black",
|
||||
invert = True,
|
||||
use_cache = True,
|
||||
*args, **kwargs):
|
||||
Mobject2D.__init__(self, *args, **kwargs)
|
||||
self.filter_rgb = 255 * np.array(Color(filter_color).get_rgb()).astype('uint8')
|
||||
DEFAULT_CONFIG = {
|
||||
"filter_color" : "black",
|
||||
"invert" : True,
|
||||
"use_cache" : True,
|
||||
"should_buffer_points" : False,
|
||||
"scale_value" : 1.0
|
||||
}
|
||||
def __init__(self, image_file, **kwargs):
|
||||
digest_config(self, ImageMobject, kwargs, locals())
|
||||
Mobject.__init__(self, **kwargs)
|
||||
self.filter_rgb = 255 * np.array(Color(self.filter_color).get_rgb()).astype('uint8')
|
||||
self.name = to_cammel_case(
|
||||
os.path.split(image_file)[-1].split(".")[0]
|
||||
)
|
||||
self.use_cache = use_cache
|
||||
possible_paths = [
|
||||
image_file,
|
||||
os.path.join(IMAGE_DIR, image_file),
|
||||
|
@ -32,31 +33,33 @@ class ImageMobject(Mobject2D):
|
|||
]
|
||||
for path in possible_paths:
|
||||
if os.path.exists(path):
|
||||
self.generate_points_from_file(path, invert)
|
||||
self.generate_points_from_file(path)
|
||||
self.scale(self.scale_value)
|
||||
self.center()
|
||||
return
|
||||
raise IOError("File not Found")
|
||||
|
||||
def generate_points_from_file(self, path, invert):
|
||||
if self.use_cache and self.read_in_cached_attrs(path, invert):
|
||||
def generate_points_from_file(self, path):
|
||||
if self.use_cache and self.read_in_cached_attrs(path):
|
||||
return
|
||||
image = Image.open(path).convert('RGB')
|
||||
if invert:
|
||||
if self.invert:
|
||||
image = invert_image(image)
|
||||
self.generate_points_from_image_array(np.array(image))
|
||||
self.cache_attrs(path, invert)
|
||||
self.cache_attrs(path)
|
||||
|
||||
def get_cached_attr_files(self, path, invert, attrs):
|
||||
def get_cached_attr_files(self, path, attrs):
|
||||
#Hash should be unique to (path, invert) pair
|
||||
unique_hash = str(hash(path+str(invert)))
|
||||
unique_hash = str(hash(path+str(self.invert)))
|
||||
return [
|
||||
os.path.join(IMAGE_MOBJECT_DIR, unique_hash)+"."+attr
|
||||
for attr in attrs
|
||||
]
|
||||
|
||||
def read_in_cached_attrs(self, path, invert,
|
||||
def read_in_cached_attrs(self, path,
|
||||
attrs = ("points", "rgbs"),
|
||||
dtype = "float64"):
|
||||
cached_attr_files = self.get_cached_attr_files(path, invert, attrs)
|
||||
cached_attr_files = self.get_cached_attr_files(path, attrs)
|
||||
if all(map(os.path.exists, cached_attr_files)):
|
||||
for attr, cache_file in zip(attrs, cached_attr_files):
|
||||
arr = np.fromfile(cache_file, dtype = dtype)
|
||||
|
@ -65,10 +68,10 @@ class ImageMobject(Mobject2D):
|
|||
return True
|
||||
return False
|
||||
|
||||
def cache_attrs(self, path, invert,
|
||||
def cache_attrs(self, path,
|
||||
attrs = ("points", "rgbs"),
|
||||
dtype = "float64"):
|
||||
cached_attr_files = self.get_cached_attr_files(path, invert, attrs)
|
||||
cached_attr_files = self.get_cached_attr_files(path, attrs)
|
||||
for attr, cache_file in zip(attrs, cached_attr_files):
|
||||
getattr(self, attr).astype(dtype).tofile(cache_file)
|
||||
|
||||
|
@ -98,20 +101,26 @@ class ImageMobject(Mobject2D):
|
|||
return False
|
||||
|
||||
class Face(ImageMobject):
|
||||
def __init__(self, mode = "simple", *args, **kwargs):
|
||||
DEFAULT_CONFIG = {
|
||||
"mode" : "simple",
|
||||
"scale_value" : 0.5
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
"""
|
||||
Mode can be "simple", "talking", "straight"
|
||||
"""
|
||||
ImageMobject.__init__(self, mode + "_face", *args, **kwargs)
|
||||
self.scale(0.5)
|
||||
self.center()
|
||||
digest_config(self, Face, kwargs)
|
||||
ImageMobject.__init__(self, self.mode + "_face", **kwargs)
|
||||
|
||||
class VideoIcon(ImageMobject):
|
||||
def __init__(self, *args, **kwargs):
|
||||
ImageMobject.__init__(self, "video_icon", *args, **kwargs)
|
||||
self.scale(0.3)
|
||||
self.center()
|
||||
DEFAULT_CONFIG = {
|
||||
"scale_value" : 0.3
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, VideoIcon, kwargs)
|
||||
ImageMobject.__init__(self, "video_icon", **kwargs)
|
||||
|
||||
#TODO, Make both of these proper mobject classes
|
||||
def text_mobject(text, size = None):
|
||||
size = size or "\\Large" #TODO, auto-adjust?
|
||||
return tex_mobject(text, size, TEMPLATE_TEXT_FILE)
|
||||
|
@ -130,8 +139,8 @@ def tex_mobject(expression,
|
|||
#TODO, is checking listiness really the best here?
|
||||
result = CompoundMobject(*map(ImageMobject, image_files))
|
||||
else:
|
||||
result = ImageMobject(image_files)
|
||||
return result.highlight("white").center()
|
||||
result = ImageMobject(image_files, should_buffer_points = True)
|
||||
return result.highlight("white")
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -17,26 +17,20 @@ class Mobject(object):
|
|||
Mathematical Object
|
||||
"""
|
||||
#Number of numbers used to describe a point (3 for pos, 3 for normal vector)
|
||||
DEFAULT_CONFIG = {
|
||||
"color" : "white",
|
||||
"should_buffer_points" : GENERALLY_BUFFER_POINTS,
|
||||
"name" : None,
|
||||
}
|
||||
DIM = 3
|
||||
DEFAULT_COLOR = Color("skyblue")
|
||||
SHOULD_BUFF_POINTS = GENERALLY_BUFF_POINTS
|
||||
EDGE_BUFFER = 0.5
|
||||
NEXT_TO_BUFFER = 0.2
|
||||
|
||||
def __init__(self,
|
||||
color = None,
|
||||
name = None,
|
||||
center = None,
|
||||
**kwargs
|
||||
):
|
||||
self.color = Color(color) if color else Color(self.DEFAULT_COLOR)
|
||||
if not hasattr(self, "name"):
|
||||
self.name = name or self.__class__.__name__
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, Mobject, kwargs)
|
||||
self.color = Color(self.color)
|
||||
if self.name is None:
|
||||
self.name = self.__class__.__name__
|
||||
self.has_normals = hasattr(self, 'unit_normal')
|
||||
self.init_points()
|
||||
self.generate_points()
|
||||
if center:
|
||||
self.center().shift(center)
|
||||
|
||||
def init_points(self):
|
||||
self.points = np.zeros((0, 3))
|
||||
|
@ -130,13 +124,13 @@ class Mobject(object):
|
|||
return self
|
||||
|
||||
#Wrapper functions for better naming
|
||||
def to_corner(self, corner = LEFT+DOWN, buff = EDGE_BUFFER):
|
||||
def to_corner(self, corner = LEFT+DOWN, buff = DEFAULT_MOBJECT_TO_EDGE_BUFFER):
|
||||
return self.align_on_border(corner, buff)
|
||||
|
||||
def to_edge(self, edge = LEFT, buff = EDGE_BUFFER):
|
||||
def to_edge(self, edge = LEFT, buff = DEFAULT_MOBJECT_TO_EDGE_BUFFER):
|
||||
return self.align_on_border(edge, buff)
|
||||
|
||||
def align_on_border(self, direction, buff = EDGE_BUFFER):
|
||||
def align_on_border(self, direction, buff = DEFAULT_MOBJECT_TO_EDGE_BUFFER):
|
||||
"""
|
||||
Direction just needs to be a vector pointing towards side or
|
||||
corner in the 2d plane.
|
||||
|
@ -155,7 +149,7 @@ class Mobject(object):
|
|||
|
||||
def next_to(self, mobject,
|
||||
direction = RIGHT,
|
||||
buff = NEXT_TO_BUFFER,
|
||||
buff = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER,
|
||||
aligned_edge = None):
|
||||
if aligned_edge is not None:
|
||||
anchor_point = self.get_corner(aligned_edge-direction)
|
||||
|
@ -231,6 +225,11 @@ class Mobject(object):
|
|||
self.rgbs[:,:] = rgb
|
||||
return self
|
||||
|
||||
def set_color(self, color):
|
||||
self.highlight(color)
|
||||
self.color = Color(color)
|
||||
return self
|
||||
|
||||
def to_original_color(self):
|
||||
self.highlight(self.color)
|
||||
return self
|
||||
|
@ -315,7 +314,7 @@ class Mobject(object):
|
|||
### Stuff subclasses should deal with ###
|
||||
def should_buffer_points(self):
|
||||
# potentially changed in subclasses
|
||||
return GENERALLY_BUFF_POINTS
|
||||
return GENERALLY_BUFFER_POINTS
|
||||
|
||||
def generate_points(self):
|
||||
#Typically implemented in subclass, unless purposefully left blank
|
||||
|
@ -346,16 +345,24 @@ class Mobject(object):
|
|||
alpha * getattr(mobject2, attr)
|
||||
setattr(target_mobject, attr, new_array)
|
||||
|
||||
#TODO, Make the two implementations bellow not redundant
|
||||
class Mobject1D(Mobject):
|
||||
def __init__(self, density = DEFAULT_POINT_DENSITY_1D, *args, **kwargs):
|
||||
self.epsilon = 1.0 / density
|
||||
|
||||
Mobject.__init__(self, *args, **kwargs)
|
||||
DEFAULT_CONFIG = {
|
||||
"density" : DEFAULT_POINT_DENSITY_1D,
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, Mobject1D, kwargs)
|
||||
self.epsilon = 1.0 / self.density
|
||||
Mobject.__init__(self, **kwargs)
|
||||
|
||||
class Mobject2D(Mobject):
|
||||
def __init__(self, density = DEFAULT_POINT_DENSITY_2D, *args, **kwargs):
|
||||
self.epsilon = 1.0 / density
|
||||
Mobject.__init__(self, *args, **kwargs)
|
||||
DEFAULT_CONFIG = {
|
||||
"density" : DEFAULT_POINT_DENSITY_2D,
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, Mobject1D, kwargs)
|
||||
self.epsilon = 1.0 / self.density
|
||||
Mobject.__init__(self, **kwargs)
|
||||
|
||||
class CompoundMobject(Mobject):
|
||||
def __init__(self, *mobjects):
|
||||
|
|
|
@ -8,28 +8,24 @@ from helpers import *
|
|||
|
||||
|
||||
class Point(Mobject):
|
||||
DEFAULT_COLOR = "black"
|
||||
def __init__(self, location = ORIGIN, *args, **kwargs):
|
||||
self.location = np.array(location)
|
||||
Mobject.__init__(self, *args, **kwargs)
|
||||
DEFAULT_CONFIG = {
|
||||
"color" : "black",
|
||||
}
|
||||
def __init__(self, location = ORIGIN, **kwargs):
|
||||
digest_config(self, Point, kwargs, locals())
|
||||
Mobject.__init__(self, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
self.add_points(self.location.reshape((1, 3)))
|
||||
self.add_points(self.location)
|
||||
|
||||
|
||||
class Dot(Mobject1D): #Use 1D density, even though 2D
|
||||
DEFAULT_COLOR = "white"
|
||||
DEFAULT_RADIUS = 0.05
|
||||
def __init__(self, center = ORIGIN, radius = DEFAULT_RADIUS,
|
||||
*args, **kwargs):
|
||||
center = np.array(center)
|
||||
if center.size == 1:
|
||||
raise Exception("Center must have 2 or 3 coordinates!")
|
||||
elif center.size == 2:
|
||||
center = np.append(center, [0])
|
||||
self.center_point = center
|
||||
self.radius = radius
|
||||
Mobject1D.__init__(self, *args, **kwargs)
|
||||
DEFAULT_CONFIG = {
|
||||
"radius" : 0.05
|
||||
}
|
||||
def __init__(self, center_point = ORIGIN, **kwargs):
|
||||
digest_config(self, Dot, kwargs, locals())
|
||||
Mobject1D.__init__(self, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
self.add_points([
|
||||
|
@ -40,22 +36,30 @@ class Dot(Mobject1D): #Use 1D density, even though 2D
|
|||
])
|
||||
|
||||
class Cross(Mobject1D):
|
||||
RADIUS = 0.3
|
||||
DEFAULT_COLOR = "white"
|
||||
DEFAULT_CONFIG = {
|
||||
"color" : "yellow",
|
||||
"radius" : 0.3
|
||||
}
|
||||
def __init__(self, center_point = ORIGIN, **kwargs):
|
||||
digest_config(self, Cross, kwargs, locals())
|
||||
Mobject1D.__init__(self, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
self.add_points([
|
||||
(sgn * x, x, 0)
|
||||
for x in np.arange(-self.RADIUS / 2, self.RADIUS/2, self.epsilon)
|
||||
for x in np.arange(-self.radius / 2, self.radius/2, self.epsilon)
|
||||
for sgn in [-1, 1]
|
||||
])
|
||||
self.shift(self.center_point)
|
||||
|
||||
class Line(Mobject1D):
|
||||
MIN_DENSITY = 0.1
|
||||
def __init__(self, start, end, density = DEFAULT_POINT_DENSITY_1D,
|
||||
*args, **kwargs):
|
||||
DEFAULT_CONFIG = {
|
||||
"min_density" : 0.1
|
||||
}
|
||||
def __init__(self, start, end, **kwargs):
|
||||
digest_config(self, Line, kwargs)
|
||||
self.set_start_and_end(start, end)
|
||||
density *= max(self.get_length(), self.MIN_DENSITY)
|
||||
Mobject1D.__init__(self, density = density, *args, **kwargs)
|
||||
Mobject1D.__init__(self, **kwargs)
|
||||
|
||||
def set_start_and_end(self, start, end):
|
||||
preliminary_start, preliminary_end = [
|
||||
|
@ -74,9 +78,11 @@ class Line(Mobject1D):
|
|||
]
|
||||
|
||||
def generate_points(self):
|
||||
length = np.linalg.norm(self.end - self.start)
|
||||
epsilon = self.epsilon / max(length, self.min_density)
|
||||
self.add_points([
|
||||
interpolate(self.start, self.end, t)
|
||||
for t in np.arange(0, 1, self.epsilon)
|
||||
for t in np.arange(0, 1, epsilon)
|
||||
])
|
||||
|
||||
def get_length(self):
|
||||
|
@ -90,22 +96,21 @@ class Line(Mobject1D):
|
|||
return rise/run
|
||||
|
||||
class Arrow(Line):
|
||||
DEFAULT_COLOR = "white"
|
||||
DEFAULT_TIP_LENGTH = 0.25
|
||||
DEFAULT_CONFIG = {
|
||||
"color" : "white",
|
||||
"tip_length" : 0.25
|
||||
}
|
||||
def __init__(self, *args, **kwargs):
|
||||
if "tip_length" in kwargs:
|
||||
tip_length = kwargs.pop("tip_length")
|
||||
else:
|
||||
tip_length = self.DEFAULT_TIP_LENGTH
|
||||
digest_config(self, Arrow, kwargs)
|
||||
Line.__init__(self, *args, **kwargs)
|
||||
self.add_tip(tip_length)
|
||||
self.add_tip()
|
||||
|
||||
def add_tip(self, tip_length):
|
||||
def add_tip(self):
|
||||
vect = self.start-self.end
|
||||
vect = vect*tip_length/np.linalg.norm(vect)
|
||||
vect = vect*self.tip_length/np.linalg.norm(vect)
|
||||
self.add_points([
|
||||
interpolate(self.end, self.end+v, t)
|
||||
for t in np.arange(0, 1, tip_length*self.epsilon)
|
||||
for t in np.arange(0, 1, self.tip_length*self.epsilon)
|
||||
for v in [
|
||||
rotate_vector(vect, np.pi/4, axis)
|
||||
for axis in IN, OUT
|
||||
|
@ -113,7 +118,7 @@ class Arrow(Line):
|
|||
])
|
||||
|
||||
class CurvedLine(Line):
|
||||
def __init__(self, start, end, via = None, *args, **kwargs):
|
||||
def __init__(self, start, end, via = None, **kwargs):
|
||||
self.set_start_and_end(start, end)
|
||||
if via == None:
|
||||
self.via = rotate_vector(
|
||||
|
@ -124,7 +129,7 @@ class CurvedLine(Line):
|
|||
self.via = via.get_center()
|
||||
else:
|
||||
self.via = via
|
||||
Line.__init__(self, start, end, *args, **kwargs)
|
||||
Line.__init__(self, start, end, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
self.add_points([
|
||||
|
@ -137,9 +142,12 @@ class CurvedLine(Line):
|
|||
])
|
||||
|
||||
class Circle(Mobject1D):
|
||||
DEFAULT_COLOR = "red"
|
||||
def __init__(self, radius = 1.0, **kwargs):
|
||||
self.radius = radius
|
||||
DEFAULT_CONFIG = {
|
||||
"color" : "red",
|
||||
"radius" : 1.0
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, Circle, kwargs)
|
||||
Mobject1D.__init__(self, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
|
@ -149,9 +157,13 @@ class Circle(Mobject1D):
|
|||
])
|
||||
|
||||
class Rectangle(Mobject1D):
|
||||
DEFAULT_COLOR = "yellow"
|
||||
def __init__(self, height = 2.0, width = 2.0, **kwargs):
|
||||
self.height, self.width = height, width
|
||||
DEFAULT_CONFIG = {
|
||||
"color" : "yellow",
|
||||
"height" : 2.0,
|
||||
"width" : 4.0
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, Rectangle, kwargs)
|
||||
Mobject1D.__init__(self, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
|
@ -164,17 +176,27 @@ class Rectangle(Mobject1D):
|
|||
])
|
||||
|
||||
class Square(Rectangle):
|
||||
def __init__(self, side_length = 2.0, **kwargs):
|
||||
Rectangle.__init__(self, side_length, side_length, **kwargs)
|
||||
DEFAULT_CONFIG = {
|
||||
"height" : 2.0,
|
||||
"width" : 2.0,
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, Square, kwargs)
|
||||
Rectangle.__init__(self, **kwargs)
|
||||
|
||||
class Bubble(Mobject):
|
||||
def __init__(self, direction = LEFT, index_of_tip = -1, center = ORIGIN):
|
||||
self.direction = direction
|
||||
self.content = Mobject()
|
||||
self.index_of_tip = index_of_tip
|
||||
self.center_offset = center - Mobject.get_center(self)
|
||||
if direction[0] > 0:
|
||||
DEFAULT_CONFIG = {
|
||||
"direction" : LEFT,
|
||||
"index_of_tip" : -1,
|
||||
"center_point" : ORIGIN,
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, Bubble, kwargs)
|
||||
Mobject.__init__(self, **kwargs)
|
||||
self.center_offset = self.center_point - Mobject.get_center(self)
|
||||
if self.direction[0] > 0:
|
||||
self.rotate(np.pi, UP)
|
||||
self.content = Mobject()
|
||||
|
||||
def get_tip(self):
|
||||
return self.points[self.index_of_tip]
|
||||
|
@ -217,13 +239,19 @@ class Bubble(Mobject):
|
|||
return self
|
||||
|
||||
class SpeechBubble(Bubble):
|
||||
INITIAL_WIDTH = 4
|
||||
INITIAL_HEIGHT = 2
|
||||
def __init__(self, *args, **kwargs):
|
||||
Mobject.__init__(self, *args, **kwargs)
|
||||
DEFAULT_CONFIG = {
|
||||
"initial_width" : 4,
|
||||
"initial_height" : 2,
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, SpeechBubble, kwargs)
|
||||
Bubble.__init__(self, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
complex_power = 0.9
|
||||
radius = self.INITIAL_WIDTH/2
|
||||
circle = Circle(density = radius*DEFAULT_POINT_DENSITY_1D)
|
||||
radius = self.initial_width/2
|
||||
circle = Circle(radius = radius)
|
||||
circle.scale(1.0/radius)
|
||||
circle.apply_complex_function(lambda z : z**complex_power)
|
||||
circle.scale(radius)
|
||||
boundary_point_as_complex = radius*complex(-1)**complex_power
|
||||
|
@ -243,28 +271,28 @@ class SpeechBubble(Bubble):
|
|||
)
|
||||
self.highlight("white")
|
||||
self.rotate(np.pi/2)
|
||||
self.points[:,1] *= float(self.INITIAL_HEIGHT)/self.INITIAL_WIDTH
|
||||
Bubble.__init__(self, direction = LEFT)
|
||||
self.points[:,1] *= float(self.initial_height)/self.initial_width
|
||||
|
||||
class ThoughtBubble(Bubble):
|
||||
NUM_BULGES = 7
|
||||
INITIAL_INNER_RADIUS = 1.8
|
||||
INITIAL_WIDTH = 6
|
||||
def __init__(self, *args, **kwargs):
|
||||
Mobject.__init__(self, *args, **kwargs)
|
||||
DEFAULT_CONFIG = {
|
||||
"num_bulges" : 7,
|
||||
"initial_inner_radius" : 1.8,
|
||||
"initial_width" : 6
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, ThoughtBubble, kwargs)
|
||||
Bubble.__init__(self, **kwargs)
|
||||
self.index_of_tip = np.argmin(self.points[:,1])
|
||||
|
||||
def generate_points(self):
|
||||
self.add(Circle().scale(0.15).shift(2.5*DOWN+2*LEFT))
|
||||
self.add(Circle().scale(0.3).shift(2*DOWN+1.5*LEFT))
|
||||
for n in range(self.NUM_BULGES):
|
||||
theta = 2*np.pi*n/self.NUM_BULGES
|
||||
for n in range(self.num_bulges):
|
||||
theta = 2*np.pi*n/self.num_bulges
|
||||
self.add(Circle().shift((np.cos(theta), np.sin(theta), 0)))
|
||||
self.filter_out(lambda p : np.linalg.norm(p) < self.INITIAL_INNER_RADIUS)
|
||||
self.stretch_to_fit_width(self.INITIAL_WIDTH)
|
||||
self.filter_out(lambda p : np.linalg.norm(p) < self.initial_inner_radius)
|
||||
self.stretch_to_fit_width(self.initial_width)
|
||||
self.highlight("white")
|
||||
Bubble.__init__(
|
||||
self,
|
||||
index_of_tip = np.argmin(self.points[:,1]),
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -7,15 +7,14 @@ from constants import *
|
|||
from helpers import *
|
||||
|
||||
class Stars(Mobject):
|
||||
DEFAULT_COLOR = "white"
|
||||
SHOULD_BUFF_POINTS = False
|
||||
def __init__(self,
|
||||
radius = SPACE_WIDTH,
|
||||
num_points = DEFAULT_NUM_STARS,
|
||||
*args, **kwargs):
|
||||
self.num_points = num_points
|
||||
self.radius = radius
|
||||
Mobject.__init__(self, *args, **kwargs)
|
||||
DEFAULT_CONFIG = {
|
||||
"should_buffer_points" : False,
|
||||
"radius" : SPACE_WIDTH,
|
||||
"num_points" : 1000,
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, Stars, kwargs)
|
||||
Mobject.__init__(self, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
self.add_points([
|
||||
|
@ -42,12 +41,12 @@ class CubeWithFaces(Mobject2D):
|
|||
for sgn in [-1, 1]
|
||||
])
|
||||
self.pose_at_angle()
|
||||
self.set_color("skyblue")
|
||||
|
||||
def unit_normal(self, coords):
|
||||
return np.array(map(lambda x : 1 if abs(x) == 1 else 0, coords))
|
||||
|
||||
class Cube(Mobject1D):
|
||||
DEFAULT_COLOR = "yellow"
|
||||
def generate_points(self):
|
||||
self.add_points([
|
||||
([a, b, c][p[0]], [a, b, c][p[1]], [a, b, c][p[2]])
|
||||
|
@ -55,9 +54,9 @@ class Cube(Mobject1D):
|
|||
for a, b, c in it.product([-1, 1], [-1, 1], np.arange(-1, 1, self.epsilon))
|
||||
])
|
||||
self.pose_at_angle()
|
||||
self.set_color("yellow")
|
||||
|
||||
class Octohedron(Mobject1D):
|
||||
DEFAULT_COLOR = "pink"
|
||||
def generate_points(self):
|
||||
x = np.array([1, 0, 0])
|
||||
y = np.array([0, 1, 0])
|
||||
|
@ -72,9 +71,9 @@ class Octohedron(Mobject1D):
|
|||
Line(pair[0], pair[1], density = 1/self.epsilon).points
|
||||
)
|
||||
self.pose_at_angle()
|
||||
self.set_color("pink")
|
||||
|
||||
class Dodecahedron(Mobject1D):
|
||||
DEFAULT_COLOR = "limegreen"
|
||||
def generate_points(self):
|
||||
phi = (1 + np.sqrt(5)) / 2
|
||||
x = np.array([1, 0, 0])
|
||||
|
@ -99,6 +98,7 @@ class Dodecahedron(Mobject1D):
|
|||
matrix = b*np.array([x[perm], y[perm], z[perm]])
|
||||
self.add_points(np.dot(five_lines_points, matrix))
|
||||
self.pose_at_angle()
|
||||
self.set_color("limegreen")
|
||||
|
||||
class Sphere(Mobject2D):
|
||||
def generate_points(self):
|
||||
|
@ -111,6 +111,9 @@ class Sphere(Mobject2D):
|
|||
for phi in np.arange(self.epsilon, np.pi, self.epsilon)
|
||||
for theta in np.arange(0, 2 * np.pi, 2 * self.epsilon / np.sin(phi))
|
||||
])
|
||||
self.set_color("blue")
|
||||
|
||||
def unit_normal(self, coords):
|
||||
return np.array(coords) / np.linalg.norm(coords)
|
||||
return np.array(coords) / np.linalg.norm(coords)
|
||||
|
||||
|
|
@ -16,7 +16,10 @@ from script_wrapper import command_line_create_scene
|
|||
|
||||
class SampleScene(Scene):
|
||||
def construct(self):
|
||||
pass
|
||||
square = Square()
|
||||
self.add(square)
|
||||
self.dither()
|
||||
self.play(Flash(square))
|
||||
|
||||
if __name__ == "__main__":
|
||||
command_line_create_scene()
|
|
@ -15,16 +15,18 @@ import displayer as disp
|
|||
from tk_scene import TkSceneRoot
|
||||
|
||||
class Scene(object):
|
||||
def __init__(self,
|
||||
display_config = PRODUCTION_QUALITY_DISPLAY_CONFIG,
|
||||
construct_args = [],
|
||||
background = None,
|
||||
start_dither_time = DEFAULT_DITHER_TIME):
|
||||
self.display_config = display_config
|
||||
self.frame_duration = display_config["frame_duration"]
|
||||
DEFAULT_CONFIG = {
|
||||
"display_config" : PRODUCTION_QUALITY_DISPLAY_CONFIG,
|
||||
"construct_args" : [],
|
||||
"background" : None,
|
||||
"start_dither_time" : DEFAULT_DITHER_TIME
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, Scene, kwargs)
|
||||
self.frame_duration = self.display_config["frame_duration"]
|
||||
self.frames = []
|
||||
self.mobjects = []
|
||||
if background:
|
||||
if self.background:
|
||||
self.original_background = np.array(background)
|
||||
#TODO, Error checking?
|
||||
else:
|
||||
|
@ -35,7 +37,7 @@ class Scene(object):
|
|||
self.background = self.original_background
|
||||
self.shape = self.background.shape[:2]
|
||||
#TODO, space shape
|
||||
self.construct(*construct_args)
|
||||
self.construct(*self.construct_args)
|
||||
|
||||
def construct(self):
|
||||
pass #To be implemented in subclasses
|
||||
|
|
|
@ -65,21 +65,27 @@ def zero_to_one_interval():
|
|||
return interval
|
||||
|
||||
class OpenInterval(Mobject):
|
||||
def __init__(self, center = ORIGIN, width = 2, **kwargs):
|
||||
def __init__(self, center_point = ORIGIN, width = 2, **kwargs):
|
||||
digest_config(self, OpenInterval, kwargs, locals())
|
||||
Mobject.__init__(self, **kwargs)
|
||||
self.add(tex_mobject("(").shift(LEFT))
|
||||
self.add(tex_mobject(")").shift(RIGHT))
|
||||
scale_factor = width / 2.0
|
||||
self.stretch(scale_factor, 0)
|
||||
self.stretch(0.5+0.5*scale_factor, 1)
|
||||
self.shift(center)
|
||||
self.shift(center_point)
|
||||
|
||||
class Piano(ImageMobject):
|
||||
SHOULD_BUFF_POINTS = False
|
||||
DEFAULT_CONFIG = {
|
||||
"should_buffer_points" : False,
|
||||
"invert" : False,
|
||||
"scale_value" : 0.5
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
ImageMobject.__init__(self, "piano_keyboard", invert = False)
|
||||
digest_config(self, Piano, kwargs)
|
||||
ImageMobject.__init__(self, "piano_keyboard")
|
||||
jump = self.get_width()/24
|
||||
self.scale(0.5).center()
|
||||
self.center()
|
||||
self.half_note_jump = self.get_width()/24
|
||||
self.ivory_jump = self.get_width()/14
|
||||
|
||||
|
@ -100,28 +106,26 @@ class Piano(ImageMobject):
|
|||
|
||||
|
||||
class VibratingString(Animation):
|
||||
def __init__(self,
|
||||
num_periods = 1,
|
||||
overtones = 4,
|
||||
amplitude = 0.5,
|
||||
radius = INTERVAL_RADIUS,
|
||||
center = ORIGIN,
|
||||
color = "white",
|
||||
run_time = 3.0,
|
||||
alpha_func = None,
|
||||
**kwargs):
|
||||
self.radius = radius
|
||||
self.center = center
|
||||
DEFAULT_CONFIG = {
|
||||
"num_periods" : 1,
|
||||
"overtones" : 4,
|
||||
"amplitude" : 0.5,
|
||||
"radius" : INTERVAL_RADIUS,
|
||||
"center" : ORIGIN,
|
||||
"color" : "white",
|
||||
"run_time" : 3.0,
|
||||
"alpha_func" : None
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, VibratingString, kwargs)
|
||||
def func(x, t):
|
||||
return sum([
|
||||
(amplitude/((k+1)**2.5))*np.sin(2*mult*t)*np.sin(k*mult*x)
|
||||
for k in range(overtones)
|
||||
for mult in [(num_periods+k)*np.pi]
|
||||
(self.amplitude/((k+1)**2.5))*np.sin(2*mult*t)*np.sin(k*mult*x)
|
||||
for k in range(self.overtones)
|
||||
for mult in [(self.num_periods+k)*np.pi]
|
||||
])
|
||||
self.func = func
|
||||
kwargs["run_time"] = run_time
|
||||
kwargs["alpha_func"] = alpha_func
|
||||
Animation.__init__(self, Mobject1D(color = color), **kwargs)
|
||||
Animation.__init__(self, Mobject1D(color = self.color), **kwargs)
|
||||
|
||||
def update_mobject(self, alpha):
|
||||
self.mobject.init_points()
|
||||
|
|
Loading…
Add table
Reference in a new issue