mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
788 lines
28 KiB
Python
788 lines
28 KiB
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 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))
|
||
|
)
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|