Whoops, forgot to commit these deletions...

This commit is contained in:
Grant Sanderson
2015-04-26 14:41:03 -07:00
parent 8d5240e070
commit 80f5c39b24
8 changed files with 0 additions and 3173 deletions

View File

@@ -1,346 +0,0 @@
from PIL import Image
from colour import Color
import numpy as np
import warnings
import time
import os
import copy
import progressbar
import inspect
from images2gif import writeGif
from helpers import *
from mobject import *
import displayer as disp
class MobjectMovement(object):
def __init__(self,
mobject,
run_time = DEFAULT_ANIMATION_RUN_TIME,
alpha_func = high_inflection_0_to_1,
name = None):
if isinstance(mobject, type) and issubclass(mobject, Mobject):
self.mobject = mobject()
elif isinstance(mobject, Mobject):
self.mobject = mobject
else:
raise Exception("Invalid mobject parameter, must be \
subclass or instance of Mobject")
self.starting_mobject = copy.deepcopy(self.mobject)
self.reference_mobjects = [self.starting_mobject]
self.alpha_func = alpha_func or (lambda x : x)
self.run_time = run_time
#TODO, Adress the idea of filtering the mobmov
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)
def __str__(self):
return self.name
def get_points_and_rgbs(self):
"""
It is the responsibility of this class to only emit points within
the space. Returns np array of points and corresponding np array
of rgbs
"""
#TODO, I don't think this should be necessary. This should happen
#under the individual mobjects.
points = self.mobject.points
rgbs = self.mobject.rgbs
#Filters out what is out of bounds.
admissibles = (abs(points[:,0]) < self.restricted_width) * \
(abs(points[:,1]) < self.restricted_height)
for filter_function in self.filter_functions:
admissibles *= ~filter_function(points)
if any(self.spacial_center):
points += self.spacial_center
#Filter out points pushed off the edge
admissibles *= (abs(points[:,0]) < SPACE_WIDTH) * \
(abs(points[:,1]) < SPACE_HEIGHT)
if rgbs.shape[0] < points.shape[0]:
#TODO, this shouldn't be necessary, find what's happening.
points = points[:rgbs.shape[0], :]
admissibles = admissibles[:rgbs.shape[0]]
return points[admissibles, :], rgbs[admissibles, :]
def update(self, alpha):
if alpha < 0:
alpha = 0
if alpha > 1:
alpha = 1
self.update_mobject(self.alpha_func(alpha))
def filter_out(self, *filter_functions):
self.filter_functions += filter_functions
return self
def restrict_height(self, height):
self.restricted_height = min(height, SPACE_HEIGHT)
return self
def restrict_width(self, width):
self.restricted_width = min(width, SPACE_WIDTH)
return self
def shift(self, vector):
self.spacial_center += vector
return self
def set_run_time(self, time):
self.run_time = time
return self.reload()
def set_alpha_func(self, alpha_func):
if alpha_func is None:
alpha_func = lambda x : x
self.alpha_func = alpha_func
return self
def set_name(self, name):
self.name = name
return self
# def drag_pixels(self):
# self.frames = drag_pixels(self.get_frames())
# return self
# def reverse(self):
# self.get_frames().reverse()
# self.name = 'Reversed' + str(self)
# return self
def update_mobject(self, alpha):
#Typically ipmlemented by subclass
pass
def clean_up(self):
pass
###### Concrete MobjectMovement ########
class Rotating(MobjectMovement):
def __init__(self,
mobject,
axis = None,
axes = [[0, 0, 1], [0, 1, 0]],
radians = 2 * np.pi,
run_time = 20.0,
alpha_func = None,
*args, **kwargs):
MobjectMovement.__init__(
self, mobject,
run_time = run_time,
alpha_func = alpha_func,
*args, **kwargs
)
self.axes = [axis] if axis else axes
self.radians = radians
def update_mobject(self, alpha):
self.mobject.points = self.starting_mobject.points
for axis in self.axes:
self.mobject.rotate(
self.radians * alpha,
axis
)
class RotationAsTransform(Rotating):
def __init__(self, mobject, radians, axis = (0, 0, 1), axes = None,
run_time = DEFAULT_ANIMATION_RUN_TIME,
alpha_func = high_inflection_0_to_1,
*args, **kwargs):
Rotating.__init__(
self,
mobject,
axis = axis,
axes = axes,
run_time = run_time,
radians = radians,
alpha_func = alpha_func,
)
class FadeOut(MobjectMovement):
def update_mobject(self, alpha):
self.mobject.rgbs = self.starting_mobject.rgbs * (1 - alpha)
class Reveal(MobjectMovement):
def update_mobject(self, alpha):
self.mobject.rgbs = self.starting_mobject.rgbs * alpha
if self.mobject.points.shape != self.starting_mobject.points.shape:
self.mobject.points = self.starting_mobject.points
#TODO, Why do you need to do this? Shouldn't points always align?
class Transform(MobjectMovement):
def __init__(self, mobject1, mobject2,
run_time = DEFAULT_TRANSFORM_RUN_TIME,
*args, **kwargs):
count1, count2 = mobject1.get_num_points(), mobject2.get_num_points()
Mobject.align_data(mobject1, mobject2)
MobjectMovement.__init__(self, mobject1, run_time = run_time, *args, **kwargs)
self.ending_mobject = mobject2
self.mobject.SHOULD_BUFF_POINTS = \
mobject1.SHOULD_BUFF_POINTS and mobject2.SHOULD_BUFF_POINTS
self.reference_mobjects.append(mobject2)
self.name += "To" + str(mobject2)
if count2 < count1:
#Ensure redundant pixels fade to black
indices = self.non_redundant_m2_indices = \
np.arange(0, count1-1, float(count1) / count2).astype('int')
temp = np.zeros(mobject2.points.shape)
temp[indices] = mobject2.rgbs[indices]
mobject2.rgbs = temp
def update_mobject(self, alpha):
Mobject.interpolate(
self.starting_mobject,
self.ending_mobject,
self.mobject,
alpha
)
def clean_up(self):
if hasattr(self, "non_redundant_m2_indices"):
#Reduce mobject (which has become identical to mobject2), as
#well as mobject2 itself
for mobject in [self.mobject, self.ending_mobject]:
for attr in ['points', 'rgbs']:
setattr(
mobject, attr,
getattr(
self.ending_mobject,
attr
)[self.non_redundant_m2_indices]
)
class ApplyMethod(Transform):
def __init__(self, method, mobject, *args, **kwargs):
"""
Method is a method of Mobject
"""
method_args = ()
if isinstance(method, tuple):
method, method_args = method[0], method[1:]
if not inspect.ismethod(method):
raise "Not a valid Mobject method"
Transform.__init__(
self,
mobject,
method(copy.deepcopy(mobject), *method_args),
*args, **kwargs
)
class ApplyFunction(Transform):
def __init__(self, function, mobject, run_time = DEFAULT_ANIMATION_RUN_TIME,
*args, **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,
*args, **kwargs)
self.name = "".join([
"Apply",
"".join([s.capitalize() for s in function.__name__.split("_")]),
"To" + str(mobject)
])
class ComplexFunction(ApplyFunction):
def __init__(self, function, *args, **kwargs):
def point_map(point):
x, y, z = point
c = np.complex(x, y)
c = function(c)
return c.real, c.imag, z
if len(args) > 0:
args = list(args)
mobject = args.pop(0)
elif "mobject" in kwargs:
mobject = kwargs.pop("mobject")
else:
mobject = Grid()
ApplyFunction.__init__(self, point_map, mobject, *args, **kwargs)
self.name = "ComplexFunction" + to_cammel_case(function.__name__)
#Todo, abstract away function naming'
class Homotopy(MobjectMovement):
def __init__(self, homotopy, *args, **kwargs):
"""
Homotopy a function from (x, y, z, t) to (x', y', z')
"""
self.homotopy = homotopy
MobjectMovement.__init__(self, *args, **kwargs)
def update_mobject(self, alpha):
self.mobject.points = np.array([
self.homotopy((x, y, z, alpha))
for x, y, z in self.starting_mobject.points
])
class ComplexHomotopy(Homotopy):
def __init__(self, complex_homotopy, *args, **kwargs):
"""
Complex Hootopy a function (z, t) to z'
"""
def homotopy((x, y, z, t)):
c = complex_homotopy((complex(x, y), t))
return (c.real, c.imag, z)
if len(args) > 0:
args = list(args)
mobject = args.pop(0)
elif "mobject" in kwargs:
mobject = kwargs["mobject"]
else:
mobject = Grid()
Homotopy.__init__(self, homotopy, mobject, *args, **kwargs)
self.name = "ComplexHomotopy" + \
to_cammel_case(complex_homotopy.__name__)
class ShowCreation(MobjectMovement):
def update_mobject(self, alpha):
#TODO, shoudl I make this more efficient?
new_num_points = int(alpha * self.starting_mobject.points.shape[0])
for attr in ["points", "rgbs"]:
setattr(
self.mobject,
attr,
getattr(self.starting_mobject, attr)[:new_num_points, :]
)
class Flash(MobjectMovement):
def __init__(self, mobject, color = "white", slow_factor = 0.01,
run_time = 0.1, alpha_func = None,
*args, **kwargs):
MobjectMovement.__init__(self, mobject, run_time = run_time,
alpha_func = alpha_func,
*args, **kwargs)
self.intermediate = Mobject(color = 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
def update_mobject(self, alpha):
#Makes alpha go from 0 to slow_factor to 0 instead of 0 to 1
alpha = self.slow_factor * (1.0 - 4 * (alpha - 0.5)**2)
Mobject.interpolate(
self.starting_mobject,
self.intermediate,
self.mobject,
alpha
)

View File

View File

@@ -1,787 +0,0 @@
from PIL import Image
from animate import *
from mobject import *
from constants import *
from helpers import *
from tex_image_utils import load_pdf_images
from displayer import *
import itertools as it
import os
import numpy as np
from copy import deepcopy
from epii_animations import MULTIPLIER_COLOR, ADDER_COLOR, ONE_COLOR
CCCD_MOVIE_DIR = "cccd"
symbol_images = load_pdf_images("cccd_symbols.pdf", regen_if_exists = False)
phrase_images = load_pdf_images("cccd_phrases.pdf", regen_if_exists = False)
name_to_image = dict(
zip([
"two",
"minus_1",
"i",
"x_squared",
"four",
"one",
"multiplication_function",
"deriv_def_base",
"deriv_def_inner_e_to_x",
"deriv_def_plus_h",
"deriv_def_e_to_h",
"deriv_def_one",
"deriv_def_outer_e_to_x",
"series_terms",
"series_exponents",
"series_exponents_minus_1",
"d_series_coefficients",
"one_plus",
"d_series_simple",
"deriv_x_squared",
"deriv_e_to_x",
"question_mark",
], symbol_images) + zip([
"complex_derivative_title",
"limit_explanation",
"velocity_vector_explanation",
"both_same_point",
"maybe_like_this",
"or_this",
"why_vectors",
"pause_and_ponder",
"think_in_pictures",
"remember_this",
], phrase_images)
)
def function_of_numbers():
kwargs = {"dither_time" : 0}
two, minus_1, i, x_squared, four, one = [
ImageMobject(name_to_image[name])
for name in ["two", "minus_1", "i", "x_squared",
"four", "one"]
]
minus_1_copy = copy.deepcopy(minus_1)
for mob1, mob2, height in [
(two, four, 2),
(minus_1, one, 0),
(i, minus_1_copy, -2)
]:
mob1.center().shift((-2, height, 0))
mob2.center().shift((2, height, 0))
x_squared.center()
point = Point()
inputs = CompoundMobject(two, minus_1, i)
outputs = CompoundMobject(four, one, minus_1_copy)
return Transform(
inputs, point, **kwargs
).then(
Transform(point, outputs, **kwargs)
).with_background(x_squared)
def real_function_graph():
graph = NumberLine()
graph.add(NumberLine().rotate(np.pi / 2))
int_size = graph.interval_size
min_x = SPACE_WIDTH / int_size
graph.add(FunctionGraph(
lambda x : x**2,
x_range = [-min_x, min_x]
))
point = graph.points[-20,:]
line = Line((2, 0, 0), (2, 1.1, 0)) #Terrible...
line.highlight("yellow")
return ShowCreation(graph).then(ShowCreation(line).with_background(graph))
def two_grids():
grid1, grid2 = Grid(), Grid()
return ShowCreation(grid1).then(
Rotating(grid1, run_time = 7.0, radians = np.pi / 3).while_also(
ShowCreation(grid2, dither_time = 2.0).while_also(
Rotating, axis = [1, 1, 0]
)
)
)
def z_squared():
return ComplexHomotopy(square_homotopy)
def z_squared_marked():
#Hard coded 2, i, -1
return ComplexHomotopy(
lambda (z, t) : z**(1 + t)
).while_also(
ComplexHomotopy(
lambda (z, t) : z + complex(2, 0)**(1 + t),
Cross(
color = random_color()
)
)
).while_also(
ComplexHomotopy(
lambda (z, t) : z + complex(-1, 0)**(1 + t),
Cross(
color = random_color()
)
)
).while_also(
ComplexHomotopy(
lambda (z, t) : z + complex(0, 1)**(1 + t),
Cross(
color = random_color()
)
)
)
def multiplier_in_the_wild(point):
func = ComplexFunction(lambda z : z*point)
one = Cross().highlight(ONE_COLOR).shift((1, 0, 0))
point = Cross().highlight(
MULTIPLIER_COLOR
).shift((point.real, point.imag, 0))
func_copy = copy.deepcopy(func)
return func.then(
func_copy.while_also(
Transform(one, point, run_time = DEFAULT_ANIMATION_RUN_TIME)
)
).then(
Animation(
Grid(), run_time = 2.0, dither_time = 0
).with_background(point)
)
def random_looking_function():
wongky_map = Transform(
Grid(), CubeShell().scale(SPACE_HEIGHT),
alpha_func = lambda t : 0.5 * high_inflection_0_to_1(t),
run_time = 3.0
)
return wongky_map.then(copy.deepcopy(wongky_map).reverse())
def zoom_in_on_map(function, is_homotopy, point, zoom_level):
center_line = ParametricFunction(lambda t : (0, t * SPACE_HEIGHT, 0))
half_width = SPACE_WIDTH / 2
left_center = (-half_width + center_line.epsilon, 0, 0)
right_center = (half_width - center_line.epsilon, 0, 0)
left_divider = copy.deepcopy(center_line).shift(right_center)
right_divider = copy.deepcopy(center_line).shift(left_center)
point = complex(point)
outer_circle = Circle().scale(SPACE_HEIGHT + SPACE_WIDTH + half_width)
inner_circle = Circle().scale(zoom_level).shift(
(point.real, point.imag, 0)
)
outer_to_inner = Transform(outer_circle, inner_circle).with_background(Grid())
big_radius = min(half_width, SPACE_HEIGHT)
big_circle = Circle().scale(big_radius)
bts_ratio = big_radius / zoom_level
half_grid = Grid().filter_out(lambda (x, y, z) : abs(x) > half_width or y > SPACE_HEIGHT)
one = Cross().shift((1, 0, 0)).highlight(ONE_COLOR)
if is_homotopy:
global_function = ComplexHomotopy(function, copy.deepcopy(half_grid))
def local_homotopoy((z, t)):
return bts_ratio * (function((z/bts_ratio + point, t)) - function((point,t)))
local_function = ComplexHomotopy(local_homotopoy, copy.deepcopy(half_grid))
one_following = ComplexHomotopy(
lambda (z, t) : z + local_homotopoy((1, t)) - 1,
one
)
circle_following = ComplexHomotopy(
lambda (z, t) : z + function((point, t)) - point,
inner_circle
)
else:
global_function = ComplexFunction(function, copy.deepcopy(half_grid))
def local_lambda(z):
return bts_ratio*(function(z/bts_ratio + point) - function(point))
local_function = ComplexFunction(local_lambda, copy.deepcopy(half_grid))
one_following = ComplexFunction(lambda z : z + local_lambda(1) - 1, one)
circle_following = ComplexFunction(
lambda z : z + function(point) - point,
inner_circle
)
zoom_region = Circle().scale(zoom_level)
zoom_region.add(Grid().filter_out(lambda p : np.linalg.norm(p) > zoom_level))
zoom_region.shift(left_center).shift((point.real, point.imag, 0))
zoom_in = ComplexFunction(
lambda z : bts_ratio * (z - point - complex(left_center[0], left_center[1])) + \
complex(right_center[0], right_center[1]),
zoom_region
).set_run_time(DEFAULT_TRANSFORM_RUN_TIME)
grow_local_grid = ShowCreation(
Grid().filter_out(lambda p : np.linalg.norm(p) > big_radius),
run_time = 1.0
).with_background(big_circle)
def out_of_circle(points):
return np.apply_along_axis(np.linalg.norm, 1, points) > big_radius
local_function.filter_out(out_of_circle)
for anim in global_function, outer_to_inner, circle_following:
anim.shift(left_center).restrict_width(half_width).with_background(left_divider)
for anim in local_function, grow_local_grid, one_following:
anim.shift(right_center).with_background(right_divider, big_circle)
for anim in outer_to_inner, zoom_in, grow_local_grid:
anim.set_dither(0)
#Kind of hacky...one day there will be a better way of doing this.
show_left_grid = Animation(Mobject().add_points(*global_function.get_points_and_rgbs()))
show_left_grid.with_background(copy.deepcopy(inner_circle).shift(left_center))
return outer_to_inner.then(
zoom_in.while_also(show_left_grid),
).then(
grow_local_grid.while_also(show_left_grid).while_also(zoom_in)
).then(
global_function.while_also(
local_function
).while_also(
circle_following
).while_also(
one_following
)
)
def z_squared_derivative_example(point, zoom_level):
point = complex(point)
point_coords = np.array((point.real, point.imag, 0))
circle = Circle().scale(zoom_level).shift(point_coords)
z = Cross(color = Circle.DEFAULT_COLOR).shift(point_coords)
two_z = Cross(color = MULTIPLIER_COLOR).shift(2*point_coords)
zero = Cross()
one = Cross(color = ONE_COLOR).shift((1, 0, 0))
plane = Grid()
return Transform(circle, z).with_background(plane, zero).then(
Transform(z, two_z).with_background(
plane, zero
).while_also(
Reveal(one).with_background(
two_z, plane, zero
)
).set_dither(0)
).then(
ComplexFunction(lambda c : c * 2 * point, plane).with_background(
two_z, zero
).while_also(
ComplexFunction(lambda c : c - 1 + 2 * point, one)
)
)
def z_squared_derivative_2z(zoom_level):
circles = Mobject()
crosses = Mobject()
mini_maps = []
mini_grids = Mobject()
example_range = range(-3, 4, 2)
for x in example_range:
for y in example_range:
circle = Circle().scale(zoom_level).shift((x, y, 0))
cross = Cross().shift((2*x, 2*y, 0))
mini_grid = Grid(
radius = zoom_level,
subinterval_size = 0.25
).filter_out(
lambda point : np.linalg.norm(point) > zoom_level
)
mini_maps.append(
ComplexFunction(
lambda z : (z + complex(x, y))**2 - complex(x, y)**2,
mini_grid
).filter_out(
lambda points : np.apply_along_axis(np.linalg.norm, 1, points) \
> zoom_level
).with_background(
Circle().scale(zoom_level)
).shift((x, y, 0)).set_dither(0)
)#generate frames so that lambda doesn't change
mini_grids.add(copy.deepcopy(mini_grid).shift((x, y, 0)))
Mobject.align_data(circle, cross)
circles.add(circle)
crosses.add(cross)
all_mini_maps = reduce(Animation.while_also, mini_maps)
crosses.highlight(MULTIPLIER_COLOR)
return FadeOut(Grid()).while_also(
Animation(CompoundMobject(circles, mini_grids))
).then(
all_mini_maps.with_background(circles)
).then(
ComplexFunction(lambda z : 2*z).while_also(
Transform(
circles, crosses,
run_time = DEFAULT_ANIMATION_RUN_TIME
)
)
)
def visualize_exp():
kwargs1 = {"run_time" : 2.0, "dither_time" : 1.0}
kwargs2 = {"run_time" : 2.0, "dither_time" : 0.0}
cylinder = Grid().apply_function(
lambda (x, y, z) : (x, np.sin(y), np.cos(y))
).rotate(np.pi/9, [0, 1, 0])
exp_plane = Grid().apply_complex_function(np.exp)
rotating_cyl = Rotating(cylinder, radians = np.pi/5, run_time = 5.0)
return Transform(Grid(), cylinder, **kwargs1).then(
Transform(cylinder, exp_plane, **kwargs2)
)
def derivative_definition():
base, inner_e_to_x, plus_h, e_to_h, one, outer_e_to_x = [
ImageMobject(name_to_image["deriv_def_" + name])
for name in [
"base",
"inner_e_to_x",
"plus_h",
"e_to_h",
"one",
"outer_e_to_x",
]
]
shift = (-0.2, -0.2, 0)
base.shift(shift)
outer_e_to_x.shift(shift)
limit_explanation = ImageMobject(name_to_image["limit_explanation"])
limit_explanation.shift((1, -2, 0))
return Transform(plus_h, e_to_h).with_background(
base, inner_e_to_x
).then(
Transform(inner_e_to_x, outer_e_to_x).while_also(
Reveal(one, dither_time = 1.0, run_time = 1.0)
).with_background(base, e_to_h)
).then(
Reveal(limit_explanation).with_background(
base, outer_e_to_x, one, e_to_h
)
)
def take_derivative_of_series():
series_terms, series_exponents, series_exponents_minus_1, \
d_series_coefficients, one_plus, d_series_simple = [
ImageMobject(name_to_image[name])
for name in [
"series_terms",
"series_exponents",
"series_exponents_minus_1",
"d_series_coefficients",
"one_plus",
"d_series_simple",
]
]
coefficients = copy.deepcopy(series_exponents)
fraction_bars = copy.deepcopy(series_exponents)
coefficients.filter_out(lambda (x, y, z) : y < 0.5)
series_exponents_minus_1.filter_out(lambda (x, y, z) : y < 0.5)
fraction_bars.filter_out(lambda (x, y, z) : y > 0.5)
exponenets_to_coefficients = Homotopy(
lambda (x, y, z, t) : (x - 0.5*t, y - 0.5*t + np.sin(np.pi * t), z),
coefficients
).with_background(fraction_bars)
d_series_non_simple = CompoundMobject(
series_terms,
copy.deepcopy(coefficients).shift((-0.5, -0.5, 0)),
series_exponents_minus_1,
series_exponents
)
d_series_simple.center().shift((1, 0, 0))
#Yeah, this is totally good programming...
fdxc = [-2, -0.4, 1.3] #first dividing x coordinates
sdxc = [-1.7, -0.8, .3] #second dividing x coordinates
broken_series_terms = [
copy.deepcopy(d_series_non_simple).filter_out(
lambda (x, y, z) : x > fdxc[0]),
copy.deepcopy(d_series_non_simple).filter_out(
lambda (x, y, z) : x > fdxc[1] or x < fdxc[0]),
copy.deepcopy(d_series_non_simple).filter_out(
lambda (x, y, z) : x > fdxc[2] or x < fdxc[1]),
copy.deepcopy(d_series_non_simple).filter_out(
lambda (x, y, z) : x < fdxc[2]),
]
broken_dseries_terms = [
copy.deepcopy(d_series_simple).filter_out(
lambda (x, y, z) : x > sdxc[0]),
copy.deepcopy(d_series_simple).filter_out(
lambda (x, y, z) : x > sdxc[1] or x < sdxc[0]),
copy.deepcopy(d_series_simple).filter_out(
lambda (x, y, z) : x > sdxc[2] or x < sdxc[1]),
copy.deepcopy(d_series_simple).filter_out(
lambda (x, y, z) : x < sdxc[2]),
]
simplify = None
for term1, term2 in zip(broken_series_terms, broken_dseries_terms):
anim = Transform(term1, term2)
if simplify:
simplify.while_also(anim)
else:
simplify = anim
series_terms.add(series_exponents)
return exponenets_to_coefficients.while_also(
Reveal(series_exponents_minus_1).with_background(
series_terms, series_exponents
)
).while_also(
Transform(one_plus, Point(one_plus.get_center()))
).set_dither(1.0, True).set_run_time(1.0, True).then(
simplify
)
def e_to_x_takes_adder_to_multiplier(point):
point = complex(point)
point_coords = (point.real, point.imag, 0)
image = np.exp(point)
image_coords = (image.real, image.imag, 0)
adder_cross = Cross().shift(point_coords).highlight(ADDER_COLOR)
multi_cross = Cross().shift(image_coords).highlight(MULTIPLIER_COLOR)
zero = Cross()
one = Cross().shift((1, 0, 0)).highlight(ONE_COLOR)
adder = ComplexFunction(
lambda z : z + point,
CompoundMobject(Grid(radius = SPACE_WIDTH + SPACE_HEIGHT), zero)
).with_background(adder_cross)
e_to_x = ComplexFunction(np.exp).while_also(
Transform(adder_cross, multi_cross, run_time = DEFAULT_ANIMATION_RUN_TIME)
)
multiplier = ComplexFunction(lambda z : image * z).while_also(
Transform(one, multi_cross, run_time = DEFAULT_ANIMATION_RUN_TIME)
).with_background(zero, multi_cross)
return adder.then(e_to_x).then(multiplier)
def e_to_x_derivative_zoom(point, zoom_level):
point = complex(point)
image_point = np.exp(point)
both_same_point = ImageMobject(
name_to_image["both_same_point"]
).shift((image_point.real, SPACE_HEIGHT - 1, 0))
left_point = (image_point.real - SPACE_WIDTH/2, image_point.imag, 0)
right_point = (image_point.real + SPACE_WIDTH/2, image_point.imag, 0)
left_arrow = Arrow(left_point, (-1, -1, 0)).highlight("white")
right_arrow = Arrow(right_point, (1, -1, 0)).highlight("white")
e_deriv_anim = zoom_in_on_map(np.exp, False, point, zoom_level)
#SUPER HACKY, YOU MUST MAKE A BETTER WAY TO DO THIS IN FUTURE
last_anim = e_deriv_anim.generate_frames().following_animations[-1]
background = Mobject()
for anim in [last_anim] + last_anim.concurrent_animations:
background.add_points(*anim.get_points_and_rgbs())
# background.display()
return e_deriv_anim.then(
Reveal(CompoundMobject(
left_arrow, right_arrow, both_same_point
)).with_background(background)
)
def other_possible_functions():
phrases = [
ImageMobject(name_to_image[name]).center().shift((0, -2, 0))
for name in ["maybe_like_this", "or_this"]
]
return ComplexFunction(np.sin).with_background(
phrases[0]
).then(
ComplexFunction(np.sinc).with_background(
phrases[1]
)
)
def setup_velocity_vector_discussion(zoom_level):
def homotopy((x, y, z, t)):
t = 3*t - 1.5
return (
x + t,
y + t**3 - t,
z
)
big_radius = SPACE_HEIGHT
def out_of_circle(points):
return np.apply_along_axis(np.linalg.norm, 1, points) > big_radius
landing_point = homotopy((0, 0, 0, 1))
cross = Cross().highlight(Circle.DEFAULT_COLOR)
small_circle = Circle().scale(2*zoom_level)
big_circle = Circle().scale(big_radius)
new_cross = copy.deepcopy(cross)
for mob in new_cross, small_circle:
mob.shift(landing_point)
wandering = Homotopy(homotopy, cross)
one = Cross().highlight(ONE_COLOR).shift((1, 0, 0))
multiply = ComplexFunction(
lambda z : z * complex(landing_point[0], landing_point[1])
).filter_out(out_of_circle)
return wandering.then(
Transform(small_circle, big_circle).with_background(
new_cross
)
).then(
multiply.with_background(big_circle).while_also(
Transform(one, new_cross, run_time = DEFAULT_ANIMATION_RUN_TIME)
).with_background(
new_cross
)
)
def traced_path():
path = ParametricFunction(
lambda t : (
np.sin(2 * np.pi * t),
(t-1)**2 + 1,
0
)
)
new_path = copy.deepcopy(path).apply_complex_function(np.exp)
return ShowCreation(path).then(
Transform(path, new_path)
)
def walking_north(start_point, vector_len):
vv_explanation = ImageMobject(name_to_image["velocity_vector_explanation"])
vv_explanation.scale(0.75).shift((0, -3, 0))
walk_kwargs = {"alpha_func" : None, "run_time" : 5.0}
start_point = complex(start_point)
end_point = start_point + complex(0, 4)
start_coords = (start_point.real, start_point.imag, 0)
end_coords = (end_point.real, end_point.imag, 0)
vector = Vector((0, vector_len, 0)).shift(start_coords)
vector.add(vv_explanation)
start_cross = Cross().shift(start_coords)
end_cross = Cross().shift(end_coords)
path = Line(start_coords, end_coords).highlight(start_cross.DEFAULT_COLOR)
return Transform(
start_cross, end_cross, **walk_kwargs
).with_background(Grid()).while_also(
ShowCreation(path, **walk_kwargs)
).then(
Animation(vector), True
)
def map_north_vector(start_point, vector_len, zoom_level):
question_mark = ImageMobject(name_to_image["question_mark"]).center()
kwargs = {
"run_time" : DEFAULT_ANIMATION_RUN_TIME,
"dither_time" : DEFAULT_DITHER_TIME
}
start_point = complex(start_point)
image_point = np.exp(start_point)
start_coords = np.array((start_point.real, start_point.imag, 0))
image_coords = np.array((image_point.real, image_point.imag, 0))
vector = Vector((0, vector_len, 0)).add(Circle().scale(zoom_level))
vimage = copy.deepcopy(vector)
image_len = np.linalg.norm(image_coords)
image_arg = np.log(image_point).imag
stretched = copy.deepcopy(vimage).scale(image_len)
vector.shift(start_coords)
for vect in vimage, stretched:
vect.shift(image_coords)
question_mark.shift(image_coords + (0.3, 0, 0))
line_to_image = Line((0, 0, 0), image_coords)
line_along_horiz = Line((0, 0, 0), (image_len, 0, 0)).highlight(Grid.DEFAULT_COLOR)
return Transform(vector, vimage).then(
Reveal(question_mark, dither_time = 0).with_background(vimage)
).then(
Transform(vimage, stretched, **kwargs).while_also(
ShowCreation(line_to_image, **kwargs)
)
).with_background(Grid()).then(
RotationAsTransform(
copy.deepcopy(stretched).shift(-image_coords),
radians = image_arg, **kwargs
).shift(image_coords).while_also(
RotationAsTransform(
line_along_horiz, radians = image_arg, **kwargs
).with_background(Grid(), line_to_image)
)
)
def all_possible_vectors(vector_len):
tvkwargs = {"run_time" : 2.0, "dither_time" : 0}
turn_vectors = Animation(Grid(), **tvkwargs)
prototype = Vector((0, vector_len, 0))
start_vectors = Mobject()
final_vectors = Mobject()
radii = []
example_range = range(-3, 4)
for x, y in it.product(*[example_range]*2):
length = np.linalg.norm((x, y))
arg = np.log(complex(x, y)).imag
new = copy.deepcopy(prototype)
turn_vectors.while_also(
ComplexFunction(
lambda z : z * complex(x, y),
new, **tvkwargs
).shift((x, y, 0))
)
if length > 0:
radii.append(length)
start_vectors.add(copy.deepcopy(new).shift((x, y, 0)))
final_vectors.add(copy.deepcopy(new).scale(length).rotate(arg).shift((x, y, 0)))
to_vectors = Transform(prototype, start_vectors, **tvkwargs).with_background(Grid())
turn_vectors.set_name("TurnVectors")
radii = sorted(set(radii))
show_all_circles = Reveal(CompoundMobject(*[
Circle().scale(radius) for radius in radii
]), dither_time = 2.0)
show_all_circles.with_background(Grid(), final_vectors)
show_all_circles.set_name("ShowAllCircles")
return to_vectors.then(turn_vectors).then(show_all_circles)
class VelocityVectorsOfPath(Animation):
def __init__(self, path, vector_len = 1, alpha_func = None, *args, **kwargs):
self.path = path.points
diffs = path.points[1:,:] - path.points[:-1, :]
self.unit_distance = np.mean(np.apply_along_axis(np.linalg.norm, 1, diffs))
Animation.__init__(
self, Vector(point = (vector_len, 0, 0)), alpha_func = alpha_func
)
self.with_background(path)
def update_mobject(self, alpha):
index = int(alpha * self.path.shape[0])
if index >= self.path.shape[0] - 1:
return
point1, point2 = self.path[index, :], self.path[index + 1, :]
diff = (point2 - point1)
distance = np.linalg.norm(diff)
arg = np.log(complex(diff[0], diff[1])).imag
self.mobject.points = self.starting_mobject.points * (distance / self.unit_distance)
self.mobject.rotate(arg).shift(point1)
def map_trajectories(vector_len, path_func):
half_width = SPACE_WIDTH / 2
left_center = np.array((-half_width, 0, 0))
right_center = np.array((half_width, 0, 0))
dividing_line = Line((half_width, SPACE_HEIGHT, 0), (half_width, -SPACE_HEIGHT, 0))
left_grid = Grid(radius = SPACE_HEIGHT)
left_path = ParametricFunction(path_func).highlight("white")
right_path = copy.deepcopy(left_path).apply_complex_function(np.exp)
right_grid = copy.deepcopy(left_grid).apply_complex_function(np.exp)
for grid in left_grid, right_grid:
grid.filter_out(
lambda (x, y, z) : abs(x) > half_width
)
left_start = Cross().shift(left_path.points[0, :])
right_start = Cross().shift(right_path.points[0, :])
apply_function = ComplexFunction(
np.exp, copy.deepcopy(left_grid)
).restrict_width(half_width + 0.1) #STUPID HACK
move_right_grid = ComplexFunction(
lambda z : z + SPACE_WIDTH,
copy.deepcopy(right_grid).shift(left_center),
run_time = DEFAULT_TRANSFORM_RUN_TIME
).with_background(copy.deepcopy(left_grid).shift(left_center))
draw_left_path = ShowCreation(left_path)
draw_right_path = ShowCreation(right_path)
show_left_start = Reveal(left_start)
show_right_start = Reveal(right_start)
left_vectors = VelocityVectorsOfPath(left_path, vector_len)
right_vectors = VelocityVectorsOfPath(right_path, vector_len)
for anim in apply_function, draw_left_path, show_left_start, left_vectors:
anim.shift(left_center).with_background(
left_grid, dividing_line
)
for anim in draw_right_path, show_right_start, right_vectors:
anim.shift(right_center).with_background(right_grid)
for anim in draw_left_path, draw_right_path, show_left_start, show_right_start:
anim.set_dither(0)
for anim, bg in (show_left_start, left_path), (show_right_start, right_path):
anim.set_alpha_func(there_and_back).with_background(bg)
left_vectors.with_background(left_path)
right_vectors.with_background(right_path)
return apply_function.then(move_right_grid).then(
draw_left_path.while_also(draw_right_path)
).then(
show_left_start.while_also(show_right_start)
).then(
left_vectors.while_also(right_vectors)
)
if __name__ == '__main__':
example_complex = complex(1, 1)
example_complex2 = complex(2, -1)
zoom_level = 0.5
strong_zoom_level = 0.1
vector_len = 0.5
def square_homotopy((z, t)):
return z**(1 + t)
def example_walk(t):
return ((t + 1)/2, ((t + 1)/2)**2, 0)
def walk_imaginary_axis(t):
return (0, np.pi * (t + 1), 0)
functions = [
# function_of_numbers,
# real_function_graph,
# two_grids,
# z_squared,
# z_squared_marked,
# (multiplier_in_the_wild, [example_complex2]),
# random_looking_function,
# (zoom_in_on_map, [square_homotopy, True, example_complex, zoom_level]),
# (zoom_in_on_map, [square_homotopy, True, example_complex, strong_zoom_level]),
# (z_squared_derivative_example, [example_complex, zoom_level]),
# (z_squared_derivative_2z, [zoom_level]),
# visualize_exp,
# take_derivative_of_series,
# derivative_definition,
# (e_to_x_takes_adder_to_multiplier, [example_complex]),
# (e_to_x_derivative_zoom, [example_complex, strong_zoom_level]),
# other_possible_functions,
# (setup_velocity_vector_discussion, [strong_zoom_level]),
# traced_path,
# (walking_north, [example_complex, vector_len]),
# (map_north_vector, [example_complex, vector_len, strong_zoom_level]),
# (all_possible_vectors, [vector_len]),
# (map_trajectories, [vector_len, example_walk]),
# (map_trajectories, [vector_len, walk_imaginary_axis])
]
full_path = os.path.join(MOVIE_DIR, CCCD_MOVIE_DIR)
if not os.path.exists(full_path):
os.mkdir(full_path)
for func in functions:
args = []
if isinstance(func, tuple):
func, args = func
name = os.path.join(
CCCD_MOVIE_DIR,
to_cammel_case(func.__name__) + hash_args(args)
)
func(*args).write_to_movie(name)
for anim in [
# ComplexFunction(lambda z : 0.1*(z**3 - z**2 + 3)),
# ComplexFunction(np.exp, Grid(radius = SPACE_HEIGHT)),
]:
anim.write_to_movie(os.path.join(CCCD_MOVIE_DIR, str(anim)))
for name in [
# "complex_derivative_title",
# "why_vectors",
# "pause_and_ponder",
# "think_in_pictures",
# "remember_this",
# "deriv_x_squared",
# "deriv_e_to_x",
# "multiplication_function",
]:
ImageMobject(name_to_image[name]).center().save_image(
os.path.join(CCCD_MOVIE_DIR, to_cammel_case(name))
)

View File

@@ -1,16 +0,0 @@
from PIL import Image
import itertools as it
from constants import *
from helpers import invert_image
from tex_image_utils import load_pdf_images
if __name__ == "__main__":
folder = os.path.join(MOVIE_DIR, "dexp")
if not os.path.exists(folder):
os.makedirs(folder)
images = load_pdf_images("discover_exp.pdf", regen_if_exists = False)
for image, count in zip(images, it.count()):
filepath = os.path.join(folder, "dexp-%d.png"%count)
invert_image(image).save(filepath)

File diff suppressed because it is too large Load Diff

View File

@@ -1,298 +0,0 @@
#!/usr/bin/env python
from PIL import Image
from animate import *
from mobject import *
from constants import *
from helpers import *
from tex_image_utils import load_pdf_images
from displayer import *
import itertools as it
import os
import numpy as np
from copy import deepcopy
from epii_animations import name_to_image
PI_COLOR = "red"
E_COLOR = "skyblue"
I_COLOR = "green"
ADDER_COLOR = "limegreen"
MULTIPLIER_COLOR = "yellow"
ONE_COLOR = "skyblue"
POEM_MOVIE_DIR = "poem"
symbol_images = load_pdf_images("epii_poem.pdf", regen_if_exists = False)
RUN_TIMES = [
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
]
DITHER_TIMES = [
0,
0.1,
0,
0.1,
0,
0.05,
0.0,
0.1,
]
LAST_FRAME_REST_KWARGS = {"run_time" : 1.0, "dither_time" : 0}
LINE_KWARGS = [
{"run_time" : run_time, "dither_time" : dither_time}
for run_time, dither_time in zip(RUN_TIMES, DITHER_TIMES)
]
LINES_PER_VERSE = 8
LINES_PER_LAST_VERSE = 4
VERSES = 10
def get_text_transitions(verse):
num_lines = LINES_PER_LAST_VERSE if (verse == VERSES - 1) else LINES_PER_VERSE
lines = [
ImageMobject(symbol_images[LINES_PER_VERSE * verse + x])
for x in range(num_lines)
]
lines[2].shift((-1, 0, 0))
transitions = []
for x in range(num_lines):
if x == 0:
transition = Animation(lines[x], **LINE_KWARGS[x])
elif x == 1:
transition = Reveal(lines[x], **LINE_KWARGS[x])
elif x in range(2, num_lines-1):
transition = Transform(lines[x-2], lines[x], **LINE_KWARGS[x])
else:
transition = Transform(
CompoundMobject(lines[x-2], lines[x-1]), lines[x],
**LINE_KWARGS[x]
)
if x in range(1, num_lines-1):
transition.with_background(lines[x - 1])
transitions.append(transition)
return transitions
def augment_verse_0(transitions):
mobs = [e, pi, i, equals_neg1] = [
ImageMobject(name_to_image[name])
for name in ["e", "pi", "i", "equals_neg1"]
]
center = CompoundMobject(*mobs).get_center()
for mob in mobs:
mob.shift(-center)
for x, mob in zip([1, 2, 3, 7], mobs):
transitions[x].while_also(ShowCreation(mob, **LINE_KWARGS[x]))
for y in range(x + 1, LINES_PER_VERSE):
transitions[y].with_background(mob)
def augment_verse_1(transitions):
e, pi, i, e_by_e_pi_i_times = [
ImageMobject(name_to_image[name])
for name in ["e", "pi", "i", "e_by_e_pi_i_times"]
]
epii = CompoundMobject(e, pi, i).center()
for x in range(4):
transitions[x].with_background(epii)
transitions[4].while_also(Transform(epii, e_by_e_pi_i_times, **LINE_KWARGS[4]))
for x in range(5, LINES_PER_VERSE):
transitions[x].with_background(e_by_e_pi_i_times)
def augment_verse_2(transitions):
e, pi, i, pi_question, i_question = [
ImageMobject(name_to_image[name])
for name in ["e", "pi", "i", "pi_question", "i_question"]
]
center = CompoundMobject(e, pi, i).get_center()
for mob in e, pi, i:
mob.shift(-center)
pi.highlight(PI_COLOR)
pi_question.highlight(PI_COLOR).shift((-1, -1, 0))
i.highlight(I_COLOR)
i_question.highlight(I_COLOR).shift((1, 1, 0))
for x in [2, 3]:
transitions[x].with_background(pi_question, i_question)
transitions[4].while_also(
Transform(pi_question, pi, **LINE_KWARGS[4])
).with_background(i_question)
transitions[5].while_also(
Transform(i_question, i, **LINE_KWARGS[5])
).with_background(pi)
for x in [6, 7]:
transitions[x].with_background(pi, i)
transitions[7].while_also(Reveal(e, **LINE_KWARGS[7]))
def augment_verse_3(transitions):
one, i, minus, two, three_point_five = [
ImageMobject(name_to_image[name])
for name in ["one", "i", "minus", "two", "three_point_five"]
]
minus.shift((-0.8, 0.25, 0))
minus_two = CompoundMobject(minus, two)
nums = [one, i, minus_two, three_point_five]
for num in nums:
num.center().shift((0.5, 0, 0)).highlight(ADDER_COLOR)
i.scale(2)
plane = Grid(radius = SPACE_WIDTH + SPACE_HEIGHT)
transitions[3].while_also(ShowCreation(plane, **LINE_KWARGS[3]))
for x, c, num in zip([4, 5, 6, 7], [1, complex(0, 1), -2, 3.5], nums):
transitions[x].while_also(
ComplexFunction(lambda z : z + c, plane, **LINE_KWARGS[x])
).with_background(num)
def augment_verse_4(transitions):
i, two = [
ImageMobject(name_to_image[name])
for name in ["i", "two"]
]
for num in i, two:
num.center().shift((0.5, 0, 0)).highlight(MULTIPLIER_COLOR)
plane = Grid(radius = SPACE_WIDTH + SPACE_HEIGHT)
for x in [0, 1, 2, 3, 6, 7]:
transitions[x].with_background(plane)
transitions[4].while_also(
RotationAsTransform(plane, np.pi/2, **LINE_KWARGS[4])
).with_background(i)
transitions[5].while_also(
ComplexFunction(lambda z : 2*z, plane, **LINE_KWARGS[5])
).with_background(two)
def augment_verse_5(transitions):
e_to_x = ImageMobject(name_to_image["e_to_x"]).center()
for transition in transitions:
transition.with_background(e_to_x)
def augment_verse_6(transitions):
e_to_x, e_by_e_x_times, not_what_is_happening, two, e_to_2 = [
ImageMobject(name_to_image[name])
for name in ["e_to_x", "e_by_e_x_times", "not_what_is_happening",
"two", "e_to_2_value"]
]
two.center().shift((-2, 0, 0)).highlight(ADDER_COLOR)
e_to_2.center().shift((2, 0, 0)).highlight(MULTIPLIER_COLOR)
e_to_x.center()
point = Point()
not_what_is_happening.rotate(np.pi/7).highlight("red")
transitions[0].while_also(Transform(e_to_x, e_by_e_x_times, **LINE_KWARGS[0]))
transitions[1].while_also(
Reveal(not_what_is_happening, **LINE_KWARGS[1])
).with_background(e_by_e_x_times)
for x in [2, 3]:
transitions[x].with_background(e_by_e_x_times, not_what_is_happening)
transitions[4].with_background(two, e_to_x)
transitions[5].while_also(
Transform(two, point, **LINE_KWARGS[5])
).with_background(e_to_x)
transitions[6].while_also(
Transform(point, e_to_2, **LINE_KWARGS[6])
).with_background(e_to_x)
transitions[7].with_background(e_to_2, e_to_x)
def augment_verse_7(transitions):
plane = Grid(SPACE_HEIGHT + SPACE_WIDTH)
for x, c in zip([0, 1, 4, 5], [1, -1, complex(0, 1), complex(0, -1)]):
transitions[x].while_also(
ComplexFunction(lambda z : z + c, plane, **LINE_KWARGS[x])
)
big_plane = copy.deepcopy(plane).scale(2)
for x, c, mob in zip([2, 3], [2, 0.5], [plane, big_plane]):
transitions[x].while_also(
ComplexFunction(lambda z : z*c, mob, **LINE_KWARGS[x])
)
rotated_plane = copy.deepcopy(plane).rotate(np.pi / 4)
for x, r, mob in zip([6, 7], [np.pi/4, -np.pi/4], [plane, rotated_plane]):
transitions[x].while_also(
RotationAsTransform(mob, r, **LINE_KWARGS[x])
)
def augment_verse_8(transitions):
pi, i, neg_1 = [
ImageMobject(name_to_image[name])
for name in ["pi", "i", "neg_1"]
]
pi_i = CompoundMobject(pi, i).center()
pi_i.shift((-0.3, np.pi, 0))
neg_1.center().shift((-1.2, 0, 0))
imaginaries = ParametricFunction(
lambda t : (0, np.pi * t, 0),
color = ADDER_COLOR
)
plane = Grid()
circle = Circle(color = MULTIPLIER_COLOR)
transitions[0].with_background(plane)
transitions[1].while_also(
Reveal(pi_i, **LINE_KWARGS[1])
).with_background(plane)
transitions[2].while_also(
ComplexFunction(lambda z : z + np.pi * complex(0, 1), **LINE_KWARGS[2])
).with_background(pi_i)
transitions[3].with_background(plane, pi_i)
transitions[4].while_also(
Transform(imaginaries, circle, **LINE_KWARGS[4])
).while_also(
Transform(pi, neg_1, **LINE_KWARGS[4])
).with_background(plane)
transitions[5].with_background(plane, neg_1, circle)
transitions[6].while_also(
RotationAsTransform(plane, np.pi, **LINE_KWARGS[6])
).with_background(circle, neg_1)
transitions[7].with_background(plane, circle, neg_1)
def augment_verse_9(transitions):
mobs = [e, pi, i, equals_neg1] = [
ImageMobject(name_to_image[name])
for name in ["e", "pi", "i", "equals_neg1"]
]
epii_neg1 = CompoundMobject(*mobs).center()
for transition in transitions:
transition.with_background(epii_neg1)
if __name__ == '__main__':
augment_verse = [
augment_verse_0,
augment_verse_1,
augment_verse_2,
augment_verse_3,
augment_verse_4,
augment_verse_5,
augment_verse_6,
augment_verse_7,
augment_verse_8,
augment_verse_9,
]
for verse in range(VERSES):
transitions = get_text_transitions(verse)
augment_verse[verse](transitions)
name = os.path.join(POEM_MOVIE_DIR, "Verse%d"%verse)
reduce(Animation.then, transitions).write_to_movie(name)

View File

@@ -1,515 +0,0 @@
import numpy as np
import itertools as it
import os
from PIL import Image
from random import random
from copy import deepcopy
from colour import Color
from constants import *
from helpers import *
import displayer as disp
class Mobject(object):
"""
Mathematical Object
"""
#Number of numbers used to describe a point (3 for pos, 3 for normal vector)
DIM = 3
DEFAULT_COLOR = Color("skyblue")
SHOULD_BUFF_POINTS = GENERALLY_BUFF_POINTS
def __init__(self,
color = None,
name = None,
center = None,
):
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.points = np.zeros((0, 3))
self.rgbs = np.zeros((0, 3))
if self.has_normals:
self.unit_normals = np.zeros((0, 3))
self.generate_points()
if center:
self.center().shift(center)
def __str__(self):
return self.name
def show(self):
Image.fromarray(disp.paint_mobject(self)).show()
def save_image(self, name = None):
Image.fromarray(disp.paint_mobject(self)).save(
os.path.join(MOVIE_DIR, (name or str(self)) + ".png")
)
def add_points(self, points, rgbs = None, color = None):
"""
points must be a Nx3 numpy array, as must rgbs if it is not None
"""
points = np.array(points)
num_new_points = points.shape[0]
self.points = np.append(self.points, points)
self.points = self.points.reshape((self.points.size / 3, 3))
if rgbs is None:
color = Color(color) if color else self.color
rgbs = np.array([color.get_rgb()] * num_new_points)
else:
if rgbs.shape != points.shape:
raise Exception("points and rgbs must have same shape")
self.rgbs = np.append(self.rgbs, rgbs).reshape(self.points.shape)
if self.has_normals:
self.unit_normals = np.append(
self.unit_normals,
np.array([self.unit_normal(point) for point in points])
).reshape(self.points.shape)
return self
def rotate(self, angle, axis = [0, 0, 1]):
t_rotation_matrix = np.transpose(rotation_matrix(angle, axis))
self.points = np.dot(self.points, t_rotation_matrix)
if self.has_normals:
self.unit_normals = np.dot(self.unit_normals, t_rotation_matrix)
return self
def shift(self, vector):
cycle = it.cycle(vector)
v = np.array([cycle.next() for x in range(self.points.size)]).reshape(self.points.shape)
self.points += v
return self
def center(self):
self.shift(-self.get_center())
return self
def scale(self, scale_factor):
self.points *= scale_factor
return self
def scale_in_place(self, scale_factor):
center = self.get_center()
return self.center().scale(scale_factor).shift(center)
def add(self, *mobjects):
for mobject in mobjects:
self.add_points(mobject.points, mobject.rgbs)
return self
def repeat(self, count):
#Can make transition animations nicer
points, rgbs = deepcopy(self.points), deepcopy(self.rgbs)
for x in range(count - 1):
self.add_points(points, rgbs)
return self
def get_num_points(self):
return self.points.shape[0]
def pose_at_angle(self):
self.rotate(np.pi / 7)
self.rotate(np.pi / 7, [1, 0, 0])
return self
def apply_function(self, function):
self.points = np.apply_along_axis(function, 1, self.points)
return self
def apply_complex_function(self, function):
def point_map((x, y, z)):
result = function(complex(x, y))
return (result.real, result.imag, 0)
return self.apply_function(point_map)
def highlight(self, color = "red", condition = lambda x : True):
"""
Condition is function which takes in one arguments, (x, y, z).
"""
#TODO, Should self.color change?
to_change = np.apply_along_axis(condition, 1, self.points)
self.rgbs[to_change, :] *= 0
self.rgbs[to_change, :] += Color(color).get_rgb()
return self
def fade(self, amount = 0.5):
self.rgbs += amount
return self
def filter_out(self, condition):
to_eliminate = ~np.apply_along_axis(condition, 1, self.points)
self.points = self.points[to_eliminate]
self.rgbs = self.rgbs[to_eliminate]
return self
### Getters ###
def get_center(self):
return np.apply_along_axis(np.mean, 0, self.points)
def get_width(self):
return np.max(self.points[:, 0]) - np.min(self.points[:, 0])
def get_height(self):
return np.max(self.points[:, 1]) - np.min(self.points[:, 1])
### Stuff subclasses should deal with ###
def should_buffer_points(self):
# potentially changed in subclasses
return GENERALLY_BUFF_POINTS
def generate_points(self):
#Typically implemented in subclass, unless purposefully left blank
pass
### Static Methods ###
def align_data(mobject1, mobject2):
count1, count2 = mobject1.get_num_points(), mobject2.get_num_points()
if count1 == 0:
mobject1.add_points([(0, 0, 0)])
if count2 == 0:
mobject2.add_points([(0, 0, 0)])
if count1 == count2:
return
for attr in ['points', 'rgbs']:
new_arrays = make_even(getattr(mobject1, attr), getattr(mobject2, attr))
for array, mobject in zip(new_arrays, [mobject1, mobject2]):
setattr(mobject, attr, np.array(array))
def interpolate(mobject1, mobject2, target_mobject, alpha):
"""
Turns target_mobject into an interpolation between mobject1
and mobject2.
"""
Mobject.align_data(mobject1, mobject2)
for attr in ['points', 'rgbs']:
new_array = (1 - alpha) * getattr(mobject1, attr) + \
alpha * getattr(mobject2, attr)
setattr(target_mobject, attr, new_array)
class Mobject1D(Mobject):
def __init__(self, density = DEFAULT_POINT_DENSITY_1D, *args, **kwargs):
self.epsilon = 1.0 / density
Mobject.__init__(self, *args, **kwargs)
class Mobject2D(Mobject):
def __init__(self, density = DEFAULT_POINT_DENSITY_2D, *args, **kwargs):
self.epsilon = 1.0 / density
Mobject.__init__(self, *args, **kwargs)
class CompoundMobject(Mobject):
def __init__(self, *mobjects):
Mobject.__init__(self)
for mobject in mobjects:
self.add_points(mobject.points, mobject.rgbs)
###### Concrete Mobjects ########
class Stars(Mobject):
DEFAULT_COLOR = "white"
SHOULD_BUFF_POINTS = False
def __init__(self, num_points = DEFAULT_NUM_STARS,
*args, **kwargs):
self.num_points = num_points
Mobject.__init__(self, *args, **kwargs)
def generate_points(self):
self.add_points([
(
r * np.sin(phi)*np.cos(theta),
r * np.sin(phi)*np.sin(theta),
r * np.cos(phi)
)
for x in range(self.num_points)
for r, phi, theta in [[
max(SPACE_HEIGHT, SPACE_WIDTH) * random(),
np.pi * random(),
2 * np.pi * random(),
]]
])
class Point(Mobject):
def __init__(self, point = (0, 0, 0), *args, **kwargs):
Mobject.__init__(self, *args, **kwargs)
self.points = np.array(point).reshape(1, 3)
self.rgbs = np.array(self.color.get_rgb()).reshape(1, 3)
class Arrow(Mobject1D):
DEFAULT_COLOR = "white"
NUNGE_DISTANCE = 0.1
def __init__(self, point = (0, 0, 0), direction = (-1, 1, 0),
length = 1, tip_length = 0.25,
normal = (0, 0, 1), *args, **kwargs):
self.point = np.array(point)
self.direction = np.array(direction) / np.linalg.norm(direction)
self.normal = np.array(normal)
self.length = length
self.tip_length = tip_length
Mobject1D.__init__(self, *args, **kwargs)
def generate_points(self):
self.add_points([
[x, x, x] * self.direction + self.point
for x in np.arange(-self.length, 0, self.epsilon)
])
tips_dir = np.array(-self.direction), np.array(-self.direction)
for i, sgn in zip([0, 1], [-1, 1]):
tips_dir[i] = rotate_vector(tips_dir[i], sgn * np.pi / 4, self.normal)
self.add_points([
[x, x, x] * tips_dir[i] + self.point
for x in np.arange(0, self.tip_length, self.epsilon)
for i in [0, 1]
])
def nudge(self):
return self.shift(-self.direction * self.NUNGE_DISTANCE)
class Vector(Arrow):
def __init__(self, point = (1, 0, 0), *args, **kwargs):
length = np.linalg.norm(point)
Arrow.__init__(self, point = point, direction = point,
length = length, tip_length = 0.2 * length,
*args, **kwargs)
class Dot(Mobject1D): #Use 1D density, even though 2D
DEFAULT_COLOR = "white"
def __init__(self, center = (0, 0, 0), radius = 0.05, *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)
def generate_points(self):
self.add_points([
np.array((t*np.cos(theta), t*np.sin(theta), 0)) + self.center_point
for t in np.arange(0, self.radius, self.epsilon)
for theta in np.arange(0, 2 * np.pi, self.epsilon)
])
class Cross(Mobject1D):
RADIUS = 0.3
DEFAULT_COLOR = "white"
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 sgn in [-1, 1]
])
class Line(Mobject1D):
def __init__(self, start, end, density = DEFAULT_POINT_DENSITY_1D, *args, **kwargs):
self.start = np.array(start)
self.end = np.array(end)
density *= np.linalg.norm(self.start - self.end)
Mobject1D.__init__(self, density = density, *args, **kwargs)
def generate_points(self):
self.add_points([
t * self.end + (1 - t) * self.start
for t in np.arange(0, 1, self.epsilon)
])
class CurvedLine(Line):
def generate_points(self):
equidistant_point = rotate_vector(
self.end - self.start,
np.pi/3, [0,0,1]
) + self.start
self.add_points([
(1 - t*(1-t))*(t*self.end + (1-t)*self.start) \
+ t*(1-t)*equidistant_point
for t in np.arange(0, 1, self.epsilon)
])
self.ep = equidistant_point
class CubeWithFaces(Mobject2D):
def generate_points(self):
self.add_points([
sgn * np.array(coords)
for x in np.arange(-1, 1, self.epsilon)
for y in np.arange(x, 1, self.epsilon)
for coords in it.permutations([x, y, 1])
for sgn in [-1, 1]
])
self.pose_at_angle()
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]])
for p in [(0, 1, 2), (2, 0, 1), (1, 2, 0)]
for a, b, c in it.product([-1, 1], [-1, 1], np.arange(-1, 1, self.epsilon))
])
self.pose_at_angle()
class Sphere(Mobject2D):
def generate_points(self):
self.add_points([
(
np.sin(phi) * np.cos(theta),
np.sin(phi) * np.sin(theta),
np.cos(phi)
)
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))
])
def unit_normal(self, coords):
return np.array(coords) / np.linalg.norm(coords)
class Circle(Mobject1D):
DEFAULT_COLOR = "red"
def generate_points(self):
self.add_points([
(np.cos(theta), np.sin(theta), 0)
for theta in np.arange(0, 2 * np.pi, self.epsilon)
])
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)
def generate_points(self):
scale_factor = 2.0 * SPACE_WIDTH / (self.x_max - self.x_min)
self.epsilon /= scale_factor
self.add_points([
np.array([x, 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 = "lightblue"
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
elif self.dim == 1:
self.epsilon = 1.0 / expected_measure / DEFAULT_POINT_DENSITY_1D
else:
self.epsilon = 1.0 / np.sqrt(expected_measure) / DEFAULT_POINT_DENSITY_2D
Mobject.__init__(self, *args, **kwargs)
def generate_points(self):
if self.dim == 1:
self.add_points([
self.function(t)
for t in np.arange(-1, 1, self.epsilon)
])
if self.dim == 2:
self.add_points([
self.function(s, t)
for t in np.arange(-1, 1, self.epsilon)
for s in np.arange(-1, 1, self.epsilon)
])
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)
def generate_points(self):
self.add_points([
(sgns[0] * x, sgns[1] * y, 0)
for beta in np.arange(0, self.radius, self.interval_size)
for alpha in np.arange(0, self.radius, self.epsilon)
for sgns in it.product((-1, 1), (-1, 1))
for x, y in [(alpha, beta), (beta, alpha)]
])
if self.subinterval_size:
si = self.subinterval_size
color = Color(self.color)
color.set_rgb([x/2 for x in color.get_rgb()])
self.add_points([
(sgns[0] * x, sgns[1] * y, 0)
for beta in np.arange(0, self.radius, si)
if abs(beta % self.interval_size) > self.epsilon
for alpha in np.arange(0, self.radius, self.epsilon)
for sgns in it.product((-1, 1), (-1, 1))
for x, y in [(alpha, beta), (beta, alpha)]
], color = color)
class NumberLine(Mobject1D):
def __init__(self,
radius = SPACE_WIDTH,
interval_size = 0.5, tick_size = 0.1,
with_numbers = False, *args, **kwargs):
self.radius = int(radius) + 1
self.interval_size = interval_size
self.tick_size = tick_size
self.with_numbers = with_numbers
Mobject1D.__init__(self, *args, **kwargs)
def generate_points(self):
self.add_points([
(x, 0, 0)
for x in np.arange(-self.radius, self.radius, self.epsilon)
])
self.add_points([
(0, y, 0)
for y in np.arange(-2*self.tick_size, 2*self.tick_size, self.epsilon)
])
self.add_points([
(x, y, 0)
for x in np.arange(-self.radius, self.radius, self.interval_size)
for y in np.arange(-self.tick_size, self.tick_size, self.epsilon)
])
if self.with_numbers:
#TODO, test
vertical_displacement = -0.3
nums = range(-self.radius, self.radius)
nums = map(lambda x : x / self.interval_size, nums)
mobs = tex_mobjects(*[str(num) for num in nums])
for num, mob in zip(nums, mobs):
mob.center().shift([num, vertical_displacement, 0])
self.add(*mobs)
# class ComplexPlane(Grid):
# def __init__(self, *args, **kwargs):
# Grid.__init__(self, *args, **kwargs)
# self.add(Dot())

View File

@@ -1,70 +0,0 @@
import os
from PIL import Image
from constants import PDF_DIR, IMAGE_DIR, WIDTH, HEIGHT, PDF_DENSITY
def load_pdf_images(filename, regen_if_exists = False):
"""
Converts a pdf, which potentially has multiple slides, into a
directory full of enumerated pngs corresponding with these slides.
Returns a list of PIL Image objects for these images sorted as they
where in the pdf
"""
#TODO, Handle case where there is one page in the pdf!
possible_paths = [
filename,
os.path.join(PDF_DIR, filename),
os.path.join(PDF_DIR, filename + ".pdf"),
]
for path in possible_paths:
if os.path.exists(path):
directory, filename = os.path.split(path)
name = filename.split(".")[0]
images_dir = os.path.join(IMAGE_DIR, name)
already_exists = os.path.exists(images_dir)
if not already_exists:
os.mkdir(images_dir)
if not already_exists or regen_if_exists:
commands = [
"convert",
"-density",
str(PDF_DENSITY),
path,
"-size",
str(WIDTH) + "x" + str(HEIGHT),
os.path.join(images_dir, name + ".png")
]
os.system(" ".join(commands))
image_paths = [
os.path.join(images_dir, name)
for name in os.listdir(images_dir)
if name.endswith(".png")
]
image_paths.sort(cmp_enumerated_files)
return [Image.open(path).convert('RGB') for path in image_paths]
raise IOError("File not Found")
def cmp_enumerated_files(name1, name2):
num1, num2 = [
int(name.split(".")[0].split("-")[-1])
for name in (name1, name2)
]
return num1 - num2
SYMBOL_IMAGES = load_pdf_images("symbols.pdf", regen_if_exists = False)
NAME_TO_IMAGE_FILE = dict(
zip([
"-3",
"-2",
"-1",
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"cdots",
"3Blue1Brown",
], SYMBOL_IMAGES)
)