mirror of
https://github.com/3b1b/manim.git
synced 2025-07-30 13:34:19 +08:00
Finished Tau Poem
This commit is contained in:
@ -68,9 +68,9 @@ class Animation(object):
|
||||
|
||||
def update(self, alpha):
|
||||
if alpha < 0:
|
||||
alpha = 0
|
||||
alpha = 0.0
|
||||
if alpha > 1:
|
||||
alpha = 1
|
||||
alpha = 1.0
|
||||
self.update_mobject(self.alpha_func(alpha))
|
||||
|
||||
def filter_out(self, *filter_functions):
|
||||
@ -111,5 +111,40 @@ class Animation(object):
|
||||
self.update(1)
|
||||
|
||||
|
||||
class Succession(Animation):
|
||||
def __init__(self, *animations, **kwargs):
|
||||
if "run_time" in kwargs:
|
||||
run_time = kwargs.pop("run_time")
|
||||
else:
|
||||
run_time = sum([anim.run_time for anim in animations])
|
||||
self.num_anims = len(animations)
|
||||
self.anims = animations
|
||||
mobject = animations[0].mobject
|
||||
Animation.__init__(self, mobject, run_time = run_time, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return self.__class__.__name__ + \
|
||||
"".join(map(str, self.anims))
|
||||
|
||||
def update(self, alpha):
|
||||
scaled_alpha = alpha*self.num_anims
|
||||
self.mobject = self.anims
|
||||
for index in range(len(self.anims)):
|
||||
self.anims[index].update(scaled_alpha - index)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -158,12 +158,11 @@ class WalkPiCreature(Animation):
|
||||
|
||||
|
||||
class BlinkPiCreature(Transform):
|
||||
def __init__(self, pi_creature, run_time = 0.2, *args, **kwargs):
|
||||
def __init__(self, pi_creature, *args, **kwargs):
|
||||
blinked = deepcopy(pi_creature).blink()
|
||||
Transform.__init__(
|
||||
self, pi_creature, blinked,
|
||||
alpha_func = there_and_back,
|
||||
run_time = run_time,
|
||||
alpha_func = squish_alpha_func(there_and_back),
|
||||
*args, **kwargs
|
||||
)
|
||||
|
||||
|
@ -150,11 +150,14 @@ class ComplexFunction(ApplyFunction):
|
||||
#Todo, abstract away function naming'
|
||||
|
||||
|
||||
#Fuck this is cool!
|
||||
class TransformAnimations(Transform):
|
||||
def __init__(self, start_anim, end_anim,
|
||||
alpha_func = squish_alpha_func(high_inflection_0_to_1),
|
||||
**kwargs):
|
||||
if "run_time" in kwargs:
|
||||
run_time = kwargs.pop("run_time")
|
||||
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,
|
||||
|
@ -29,7 +29,7 @@ SPACE_WIDTH = SPACE_HEIGHT * DEFAULT_WIDTH / DEFAULT_HEIGHT
|
||||
|
||||
#All in seconds
|
||||
DEFAULT_FRAME_DURATION = 0.04
|
||||
DEFAULT_ANIMATION_RUN_TIME = 3.0
|
||||
DEFAULT_ANIMATION_RUN_TIME = 1.0
|
||||
DEFAULT_TRANSFORM_RUN_TIME = 1.0
|
||||
DEFAULT_DITHER_TIME = 1.0
|
||||
|
||||
|
14
helpers.py
14
helpers.py
@ -106,15 +106,21 @@ def make_even_by_cycling(iterable_1, iterable_2):
|
||||
[cycle2.next() for x in range(length)]
|
||||
)
|
||||
|
||||
### Alpha Functions ###
|
||||
|
||||
def sigmoid(x):
|
||||
return 1.0/(1 + np.exp(-x))
|
||||
|
||||
### Alpha Functions ###
|
||||
|
||||
def high_inflection_0_to_1(t, inflection = 10.0):
|
||||
error = sigmoid(-inflection / 2)
|
||||
return (sigmoid(inflection*(t - 0.5)) - error) / (1 - 2*error)
|
||||
|
||||
def rush_into(t):
|
||||
return 2*high_inflection_0_to_1(t/2)
|
||||
|
||||
def rush_from(t):
|
||||
return 2*high_inflection_0_to_1(t/2+0.5) - 1
|
||||
|
||||
def there_and_back(t, inflection = 10.0):
|
||||
new_t = 2*t if t < 0.5 else 2*(1 - t)
|
||||
return high_inflection_0_to_1(new_t, inflection)
|
||||
@ -128,9 +134,9 @@ def wiggle(t, wiggles = 2):
|
||||
def squish_alpha_func(func, a = 0.4, b = 0.6):
|
||||
def result(t):
|
||||
if t < a:
|
||||
return 0
|
||||
return func(0)
|
||||
elif t > b:
|
||||
return 1
|
||||
return func(1)
|
||||
else:
|
||||
return func((t-a)/(b-a))
|
||||
return result
|
||||
|
@ -9,8 +9,9 @@ from simple_mobjects import *
|
||||
|
||||
class PiCreature(Mobject):
|
||||
DEFAULT_COLOR = "blue"
|
||||
def __init__(self, color = DEFAULT_COLOR, **kwargs):
|
||||
Mobject.__init__(self, color = color, **kwargs)
|
||||
def __init__(self, **kwargs):
|
||||
Mobject.__init__(self, **kwargs)
|
||||
color = self.DEFAULT_COLOR
|
||||
scale_val = 0.5
|
||||
mouth_to_eyes_distance = 0.25
|
||||
part_names = [
|
||||
@ -43,7 +44,8 @@ class PiCreature(Mobject):
|
||||
self.right_eye.get_center()/2 -
|
||||
(0, mouth_to_eyes_distance, 0)
|
||||
)
|
||||
|
||||
self.eyes = [self.left_eye, self.right_eye]
|
||||
self.legs = [self.left_leg, self.right_leg]
|
||||
for part in parts:
|
||||
self.add(part)
|
||||
self.parts = parts
|
||||
@ -64,11 +66,16 @@ class PiCreature(Mobject):
|
||||
part.rgbs = self.rgbs[curr:curr+n_points,:]
|
||||
curr += n_points
|
||||
|
||||
def reload_from_parts(self):
|
||||
self.rewire_part_attributes(self_from_parts = True)
|
||||
|
||||
def highlight(self, color):
|
||||
def highlight(self, color, condition = None):
|
||||
if condition is not None:
|
||||
Mobject.highlight(self, color, condition)
|
||||
return self
|
||||
for part in set(self.parts).difference(self.white_parts):
|
||||
part.highlight(color)
|
||||
self.rewire_part_attributes(self_from_parts = True)
|
||||
self.reload_from_parts()
|
||||
return self
|
||||
|
||||
def move_to(self, destination):
|
||||
@ -78,6 +85,7 @@ class PiCreature(Mobject):
|
||||
0
|
||||
))
|
||||
self.shift(destination-bottom)
|
||||
self.rewire_part_attributes()
|
||||
return self
|
||||
|
||||
def give_frown(self):
|
||||
@ -85,7 +93,7 @@ class PiCreature(Mobject):
|
||||
self.mouth.center()
|
||||
self.mouth.apply_function(lambda (x, y, z) : (x, -y, z))
|
||||
self.mouth.shift(center)
|
||||
self.rewire_part_attributes(self_from_parts = True)
|
||||
self.reload_from_parts()
|
||||
return self
|
||||
|
||||
def give_straight_face(self):
|
||||
@ -97,7 +105,33 @@ class PiCreature(Mobject):
|
||||
self.parts[self.parts.index(self.mouth)] = new_mouth
|
||||
self.white_parts[self.white_parts.index(self.mouth)] = new_mouth
|
||||
self.mouth = new_mouth
|
||||
self.rewire_part_attributes(self_from_parts = True)
|
||||
self.reload_from_parts()
|
||||
return self
|
||||
|
||||
def get_eye_center(self):
|
||||
return center_of_mass([
|
||||
self.left_eye.get_center(),
|
||||
self.right_eye.get_center()
|
||||
]) + 0.04*RIGHT + 0.02*UP
|
||||
|
||||
def make_mean(self):
|
||||
eye_x, eye_y = self.get_eye_center()[:2]
|
||||
def should_delete((x, y, z)):
|
||||
return y - eye_y > 0.3*abs(x - eye_x)
|
||||
for eye in self.left_eye, self.right_eye:
|
||||
eye.highlight("black", should_delete)
|
||||
self.give_straight_face()
|
||||
return self
|
||||
|
||||
def make_sad(self):
|
||||
eye_x, eye_y = self.get_eye_center()[:2]
|
||||
eye_y += 0.15
|
||||
def should_delete((x, y, z)):
|
||||
return y - eye_y > -0.3*abs(x - eye_x)
|
||||
for eye in self.left_eye, self.right_eye:
|
||||
eye.highlight("black", should_delete)
|
||||
self.give_frown()
|
||||
self.reload_from_parts()
|
||||
return self
|
||||
|
||||
def get_step_intermediate(self, pi_creature):
|
||||
@ -122,22 +156,48 @@ class PiCreature(Mobject):
|
||||
eye.apply_function(
|
||||
lambda (x, y, z) : (x, bottom, z)
|
||||
)
|
||||
self.rewire_part_attributes(self_from_parts = True)
|
||||
self.reload_from_parts()
|
||||
return self
|
||||
|
||||
def shift_eyes(self):
|
||||
for eye in self.left_eye, self.right_eye:
|
||||
center = eye.get_center()
|
||||
eye.center()
|
||||
eye.rotate(np.pi, UP)
|
||||
eye.shift(center)
|
||||
self.reload_from_parts()
|
||||
return self
|
||||
|
||||
def to_symbol(self):
|
||||
for white_part in self.white_parts:
|
||||
self.parts.remove(white_part)
|
||||
self.reload_from_parts()
|
||||
return self
|
||||
|
||||
|
||||
class Randolph(PiCreature):
|
||||
pass #Nothing more than an alternative name
|
||||
|
||||
class Mortimer(PiCreature):
|
||||
DEFAULT_COLOR = DARK_BROWN
|
||||
def __init__(self, *args, **kwargs):
|
||||
PiCreature.__init__(self, *args, **kwargs)
|
||||
self.highlight(DARK_BROWN)
|
||||
# self.highlight(DARK_BROWN)
|
||||
self.give_straight_face()
|
||||
self.rotate(np.pi, UP)
|
||||
self.rewire_part_attributes()
|
||||
|
||||
class TauCreature(PiCreature):
|
||||
def __init__(self, **kwargs):
|
||||
leg_shift_val = 0.25
|
||||
leg_wag_val = 0.2
|
||||
PiCreature.__init__(self, **kwargs)
|
||||
self.parts.remove(self.right_leg)
|
||||
self.left_leg.shift(leg_shift_val*RIGHT)
|
||||
self.left_leg.wag(leg_wag_val*RIGHT, DOWN)
|
||||
self.leg = self.left_leg
|
||||
self.reload_from_parts()
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -114,5 +114,18 @@ class NumberLine(Mobject1D):
|
||||
for y in np.arange(-self.tick_size, self.tick_size, self.epsilon)
|
||||
])
|
||||
|
||||
class Axes(CompoundMobject):
|
||||
def __init__(self, *args, **kwargs):
|
||||
x_axis = NumberLine(*args, **kwargs)
|
||||
y_axis = NumberLine(*args, **kwargs).rotate(np.pi/2, OUT)
|
||||
CompoundMobject.__init__(self, x_axis, y_axis)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -90,10 +90,19 @@ class SpeechBubble(ImageMobject):
|
||||
|
||||
def write(self, text):
|
||||
smidgeon = 0.1*UP + 0.2*self.direction
|
||||
self.clear()
|
||||
self.text = text_mobject(text)
|
||||
self.text.scale(0.75*self.get_width() / self.text.get_width())
|
||||
self.text.shift(self.get_center() + smidgeon)
|
||||
self.add(self.text)
|
||||
|
||||
def clear(self):
|
||||
if not hasattr(self, "text"):
|
||||
return
|
||||
num_text_points = self.text.points.shape[0]
|
||||
self.points = self.points[:num_text_points]
|
||||
self.rgbs = self.rgbs[:num_text_points]
|
||||
self.text = Mobject()
|
||||
|
||||
class ThoughtBubble(ImageMobject):
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -117,19 +126,15 @@ class VideoIcon(ImageMobject):
|
||||
self.center()
|
||||
|
||||
def text_mobject(text, size = "\\Large"):
|
||||
return tex_mobjects(text, size, TEMPLATE_TEXT_FILE)
|
||||
return tex_mobject(text, size, TEMPLATE_TEXT_FILE)
|
||||
|
||||
#Purely redundant function to make singulars and plurals sensible
|
||||
def tex_mobject(expression, size = "\\Huge"):
|
||||
return tex_mobjects(expression, size)
|
||||
|
||||
def tex_mobjects(expression,
|
||||
def tex_mobject(expression,
|
||||
size = "\\Huge",
|
||||
template_tex_file = TEMPLATE_TEX_FILE):
|
||||
images = tex_to_image(expression, size, template_tex_file)
|
||||
if isinstance(images, list):
|
||||
#TODO, is checking listiness really the best here?
|
||||
return CompoundMobject(*map(ImageMobject, images)).center().split()
|
||||
return CompoundMobject(*map(ImageMobject, images)).center()
|
||||
else:
|
||||
return ImageMobject(images).center()
|
||||
|
||||
|
@ -171,14 +171,16 @@ class Mobject(object):
|
||||
return (result.real, result.imag, 0)
|
||||
return self.apply_function(point_map)
|
||||
|
||||
def highlight(self, color = "red", condition = lambda x : True):
|
||||
def highlight(self, color = "red", condition = None):
|
||||
"""
|
||||
Condition is function which takes in one arguments, (x, y, z).
|
||||
"""
|
||||
#TODO, Should self.color change?
|
||||
rgb = Color(color).get_rgb()
|
||||
if condition:
|
||||
to_change = np.apply_along_axis(condition, 1, self.points)
|
||||
self.rgbs[to_change, :] *= 0
|
||||
self.rgbs[to_change, :] += Color(color).get_rgb()
|
||||
self.rgbs[to_change, :] = rgb
|
||||
else:
|
||||
self.rgbs[:,:] = rgb
|
||||
return self
|
||||
|
||||
def fade(self, brightness = 0.5):
|
||||
@ -191,6 +193,15 @@ class Mobject(object):
|
||||
self.rgbs = self.rgbs[to_eliminate]
|
||||
return self
|
||||
|
||||
def sort_points(self, function = lambda p : p[0]):
|
||||
"""
|
||||
function is any map from R^3 to R
|
||||
"""
|
||||
self.points = np.array(sorted(
|
||||
self.points,
|
||||
lambda *points : cmp(*map(function, points))
|
||||
))
|
||||
|
||||
### Getters ###
|
||||
|
||||
def get_num_points(self):
|
||||
|
@ -16,13 +16,8 @@ from script_wrapper import command_line_create_scene
|
||||
|
||||
class SampleScene(Scene):
|
||||
def construct(self):
|
||||
randy = Randolph()
|
||||
self.add(randy)
|
||||
self.dither()
|
||||
self.animate(BlinkPiCreature(randy))
|
||||
self.dither()
|
||||
self.animate(WaveArm(randy))
|
||||
self.dither()
|
||||
tauy = TauCreature()
|
||||
self.animate(ApplyMethod(tauy.make_sad))
|
||||
|
||||
|
||||
|
||||
|
@ -115,6 +115,29 @@ class Scene(object):
|
||||
progress_bar.finish()
|
||||
return self
|
||||
|
||||
def animate_over_time_range(self, t0, t1, animation):
|
||||
needed_scene_time = max(abs(t0), abs(t1))
|
||||
existing_scene_time = len(self.frames)*self.frame_duration
|
||||
if existing_scene_time < needed_scene_time:
|
||||
self.dither(needed_scene_time - existing_scene_time)
|
||||
existing_scene_time = needed_scene_time
|
||||
#So negative values may be used
|
||||
if t0 < 0:
|
||||
t0 = float(t0)%existing_scene_time
|
||||
if t1 < 0:
|
||||
t1 = float(t1)%existing_scene_time
|
||||
t0, t1 = min(t0, t1), max(t0, t1)
|
||||
|
||||
for t in np.arange(t0, t1, self.frame_duration):
|
||||
animation.update((t-t0)/(t1 - t0))
|
||||
index = int(t/self.frame_duration)
|
||||
self.frames[index] = disp.paint_mobject(
|
||||
animation.mobject, self.frames[index]
|
||||
)
|
||||
animation.clean_up()
|
||||
return self
|
||||
|
||||
|
||||
def count(self, items, item_type = "mobject", *args, **kwargs):
|
||||
if item_type == "mobject":
|
||||
self.count_mobjects(items, *args, **kwargs)
|
||||
@ -205,9 +228,7 @@ class Scene(object):
|
||||
self.dither(end_dither_time)
|
||||
disp.write_to_gif(self, name or str(self))
|
||||
|
||||
def write_to_movie(self, name = None,
|
||||
end_dither_time = DEFAULT_DITHER_TIME):
|
||||
self.dither(end_dither_time)
|
||||
def write_to_movie(self, name = None):
|
||||
disp.write_to_movie(self, name or str(self))
|
||||
|
||||
def show_frame(self):
|
||||
|
@ -107,9 +107,9 @@ class IntroduceGraph(GraphScene):
|
||||
for pair in [(4, 5), (0, 5), (1, 5), (7, 1), (8, 3)]
|
||||
]
|
||||
|
||||
connected, planar, graph = CompoundMobject(*text_mobject([
|
||||
connected, planar, graph = text_mobject([
|
||||
"Connected ", "Planar ", "Graph"
|
||||
])).to_edge(UP).split()
|
||||
]).to_edge(UP).split()
|
||||
not_okay = text_mobject("Not Okay").highlight("red")
|
||||
planar_explanation = text_mobject("""
|
||||
(``Planar'' just means we can draw it without
|
||||
@ -181,7 +181,7 @@ class PlanarGraphDefinition(Scene):
|
||||
Not, quote, planar, end_quote = text_mobject([
|
||||
"Not \\\\", "``", "Planar", "''",
|
||||
# "no matter how \\\\ hard you try"
|
||||
])
|
||||
]).split()
|
||||
shift_val = CompoundMobject(Not, planar).to_corner().get_center()
|
||||
Not.highlight("red").shift(shift_val)
|
||||
graphs = [
|
||||
@ -1073,7 +1073,10 @@ class MortimerCannotTraverseCycle(GraphScene):
|
||||
|
||||
class TwoPropertiesOfSpanningTree(Scene):
|
||||
def construct(self):
|
||||
spanning, tree = text_mobject(["Spanning ", "Tree"], size = "\\Huge")
|
||||
spanning, tree = text_mobject(
|
||||
["Spanning ", "Tree"],
|
||||
size = "\\Huge"
|
||||
).split()
|
||||
spanning_explanation = text_mobject("""
|
||||
Touches every vertex
|
||||
""").shift(spanning.get_center() + 2*DOWN)
|
||||
@ -1158,7 +1161,7 @@ class FinalSum(Scene):
|
||||
"(\\text{Number of Randolph's Edges}) + 1 &= V \\\\ \n",
|
||||
"(\\text{Number of Mortimer's Edges}) + 1 &= F \\\\ \n",
|
||||
" \\Downarrow \\\\", "E","+","2","&=","V","+","F",
|
||||
], size = "\\large")
|
||||
], size = "\\large").split()
|
||||
for line in lines[:2] + [CompoundMobject(*lines[2:])]:
|
||||
self.add(line)
|
||||
self.dither()
|
||||
|
@ -1,44 +1,64 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
import itertools as it
|
||||
from copy import deepcopy
|
||||
import sys
|
||||
|
||||
|
||||
from animation import *
|
||||
from mobject import *
|
||||
from constants import *
|
||||
from helpers import *
|
||||
from scene import *
|
||||
import itertools as it
|
||||
import os
|
||||
from region import *
|
||||
from scene import Scene
|
||||
from script_wrapper import command_line_create_scene
|
||||
|
||||
class LogoGeneration(Scene):
|
||||
LOGO_RADIUS = 1.5
|
||||
INNER_RADIUS_RATIO = 0.55
|
||||
CIRCLE_DENSITY = 100
|
||||
CIRCLE_BLUE = "skyblue"
|
||||
SPHERE_DENSITY = 50
|
||||
SPHERE_BLUE = DARK_BLUE
|
||||
CIRCLE_SPHERE_INTERPOLATION = 0.3
|
||||
|
||||
LOGO_RADIUS = 1.5
|
||||
|
||||
if __name__ == '__main__':
|
||||
circle = Circle(density = 100, color = 'skyblue').repeat(5).scale(LOGO_RADIUS)
|
||||
sphere = Sphere(density = 50, color = DARK_BLUE).scale(LOGO_RADIUS)
|
||||
def construct(self):
|
||||
circle = Circle(
|
||||
density = self.CIRCLE_DENSITY,
|
||||
color = self.CIRCLE_BLUE
|
||||
).repeat(5).scale(self.LOGO_RADIUS)
|
||||
sphere = Sphere(
|
||||
density = self.SPHERE_DENSITY,
|
||||
color = self.SPHERE_BLUE
|
||||
).scale(self.LOGO_RADIUS)
|
||||
sphere.rotate(-np.pi / 7, [1, 0, 0])
|
||||
sphere.rotate(-np.pi / 7)
|
||||
alpha = 0.3
|
||||
iris = Mobject()
|
||||
Mobject.interpolate(circle, sphere, iris, alpha)
|
||||
Mobject.interpolate(
|
||||
circle, sphere, iris,
|
||||
self.CIRCLE_SPHERE_INTERPOLATION
|
||||
)
|
||||
for mob, color in [(iris, LIGHT_BROWN), (circle, DARK_BROWN)]:
|
||||
mob.highlight(color, lambda (x, y, z) : x < 0 and y > 0)
|
||||
mob.highlight("black", lambda point: np.linalg.norm(point) < 0.55*LOGO_RADIUS)
|
||||
mob.highlight(
|
||||
"black",
|
||||
lambda point: np.linalg.norm(point) < \
|
||||
self.INNER_RADIUS_RATIO*self.LOGO_RADIUS
|
||||
)
|
||||
name = text_mobject("3Blue1Brown").center()
|
||||
name.highlight("grey")
|
||||
name.shift(2*DOWN)
|
||||
|
||||
name = tex_mobject(r"\text{3Blue1Brown}").center()
|
||||
name.highlight("gray")
|
||||
name.shift((0, -2, 0))
|
||||
sc = Scene()
|
||||
sc.animate(Transform(
|
||||
self.animate(Transform(
|
||||
circle, iris,
|
||||
run_time = DEFAULT_ANIMATION_RUN_TIME
|
||||
))
|
||||
sc.add(name)
|
||||
sc.dither()
|
||||
sc.frames = drag_pixels(sc.frames)
|
||||
sc.write_to_movie("LogoGeneration", end_dither_time = 0)
|
||||
self.add(name)
|
||||
self.dither()
|
||||
print "Dragging pixels..."
|
||||
self.frames = drag_pixels(self.frames)
|
||||
|
||||
|
||||
# index = int(DEFAULT_ANIMATION_RUN_TIME / DEFAULT_ANIMATION_PAUSE_TIME)
|
||||
# create_eye.frames[index].save(LOGO_PATH)
|
||||
if __name__ == "__main__":
|
||||
command_line_create_scene()
|
||||
|
@ -149,7 +149,7 @@ class MoserPattern(CircleScene):
|
||||
def __init__(self, radians, *args, **kwargs):
|
||||
CircleScene.__init__(self, radians, *args, **kwargs)
|
||||
self.remove(*self.dots + self.lines + [self.n_equals])
|
||||
n_equals, num = tex_mobjects(["n=", "10"])
|
||||
n_equals, num = tex_mobject(["n=", "10"]).split()
|
||||
for mob in n_equals, num:
|
||||
mob.shift((-SPACE_WIDTH + 1.5, SPACE_HEIGHT - 1.5, 0))
|
||||
self.add(n_equals)
|
||||
@ -183,7 +183,7 @@ class HardProblemsSimplerQuestions(Scene):
|
||||
for sym in ["n", "2", "3"]
|
||||
])
|
||||
# not_that_hard = text_mobject("(maybe not that hard...)").scale(0.5)
|
||||
fermat2, fermat2_jargon = tex_mobjects([
|
||||
fermat2, fermat2_jargon = tex_mobject([
|
||||
r"&x^2 + y^2 = z^2 \\",
|
||||
r"""
|
||||
&(3, 4, 5) \\
|
||||
@ -193,14 +193,14 @@ class HardProblemsSimplerQuestions(Scene):
|
||||
(m^2 - &n^2, 2mn, m^2 + n^2) \\
|
||||
&\quad \vdots
|
||||
"""
|
||||
])
|
||||
fermat3, fermat3_jargon = tex_mobjects([
|
||||
]).split()
|
||||
fermat3, fermat3_jargon = tex_mobject([
|
||||
r"&x^3 + y^3 = z^3\\",
|
||||
r"""
|
||||
&y^3 = (z - x)(z - \omega x)(z - \omega^2 x) \\
|
||||
&\mathds{Z}[\omega] \text{ is a UFD...}
|
||||
"""
|
||||
])
|
||||
]).split()
|
||||
for mob in [fermat2, fermat3, fermat["2"], fermat["3"],
|
||||
fermat2_jargon, fermat3_jargon]:
|
||||
mob.scale(scale_factor)
|
||||
@ -332,10 +332,10 @@ class CountIntersectionPoints(CircleScene):
|
||||
scale_factor = 0.4
|
||||
text = tex_mobject(r"\text{How Many Intersection Points?}", size = size)
|
||||
n = len(radians)
|
||||
formula, answer = tex_mobjects([
|
||||
formula, answer = tex_mobject([
|
||||
r"{%d \choose 4} = \frac{%d(%d - 1)(%d - 2)(%d-3)}{1\cdot 2\cdot 3 \cdot 4}="%(n, n, n, n, n),
|
||||
str(choose(n, 4))
|
||||
])
|
||||
]).split()
|
||||
text.scale(scale_factor).shift(text_center)
|
||||
self.add(text)
|
||||
self.count(intersection_dots, mode="show", num_offset = ORIGIN)
|
||||
@ -492,7 +492,7 @@ class IllustrateNChooseK(Scene):
|
||||
Scene.__init__(self, *args, **kwargs)
|
||||
nrange = range(1, n+1)
|
||||
tuples = list(it.combinations(nrange, k))
|
||||
nrange_mobs = tex_mobjects([str(n) + r'\;' for n in nrange])
|
||||
nrange_mobs = tex_mobject([str(n) + r'\;' for n in nrange]).split()
|
||||
tuple_mobs = tex_mobjects(
|
||||
[
|
||||
(r'\\&' if c%(20//k) == 0 else r'\;\;') + str(p)
|
||||
@ -831,10 +831,10 @@ class CannotDirectlyApplyEulerToMoser(CircleScene):
|
||||
def __init__(self, radians, *args, **kwargs):
|
||||
CircleScene.__init__(self, radians, *args, **kwargs)
|
||||
self.remove(self.n_equals)
|
||||
n_equals, intersection_count = tex_mobjects([
|
||||
n_equals, intersection_count = tex_mobject([
|
||||
r"&n = %d\\"%len(radians),
|
||||
r"&{%d \choose 4} = %d"%(len(radians), choose(len(radians), 4))
|
||||
])
|
||||
]).split()
|
||||
shift_val = self.n_equals.get_center() - n_equals.get_center()
|
||||
for mob in n_equals, intersection_count:
|
||||
mob.shift(shift_val)
|
||||
@ -877,10 +877,10 @@ class ShowMoserGraphLines(CircleScene):
|
||||
radians = list(set(map(lambda x : x%(2*np.pi), radians)))
|
||||
radians.sort()
|
||||
CircleScene.__init__(self, radians, *args, **kwargs)
|
||||
n, plus_n_choose_4 = tex_mobjects(["n", "+{n \\choose 4}"])
|
||||
n_choose_2, plus_2_n_choose_4, plus_n = tex_mobjects([
|
||||
n, plus_n_choose_4 = tex_mobject(["n", "+{n \\choose 4}"]).split()
|
||||
n_choose_2, plus_2_n_choose_4, plus_n = tex_mobject([
|
||||
r"{n \choose 2}",r"&+2{n \choose 4}\\",r"&+n"
|
||||
])
|
||||
]).split()
|
||||
for mob in n, plus_n_choose_4, n_choose_2, plus_2_n_choose_4, plus_n:
|
||||
mob.shift((SPACE_WIDTH - 2, SPACE_HEIGHT-1, 0))
|
||||
self.chop_lines_at_intersection_points()
|
||||
@ -1028,19 +1028,19 @@ class ApplyEulerToMoser(CircleScene):
|
||||
equals, two, two1, n, n1, nc2, nc4, nc41]
|
||||
|
||||
V[1], minus[1], E[1], plus[1], F[1], equals[1], two[1] = \
|
||||
tex_mobjects(["V", "-", "E", "+", "F", "=", "2"])
|
||||
tex_mobject(["V", "-", "E", "+", "F", "=", "2"]).split()
|
||||
F[2], equals[2], E[2], minus[2], V[2], plus[2], two[2] = \
|
||||
tex_mobjects(["F", "=", "E", "-", "V", "+", "2"])
|
||||
tex_mobject(["F", "=", "E", "-", "V", "+", "2"]).split()
|
||||
F[3], equals[3], E[3], minus[3], n[3], minus1[3], nc4[3], plus[3], two[3] = \
|
||||
tex_mobjects(["F", "=", "E", "-", "n", "-", r"{n \choose 4}", "+", "2"])
|
||||
tex_mobject(["F", "=", "E", "-", "n", "-", r"{n \choose 4}", "+", "2"]).split()
|
||||
F[4], equals[4], nc2[4], plus1[4], two1[4], nc41[4], plus2[4], n1[4], minus[4], n[4], minus1[4], nc4[4], plus[4], two[4] = \
|
||||
tex_mobjects(["F", "=", r"{n \choose 2}", "+", "2", r"{n \choose 4}", "+", "n","-", "n", "-", r"{n \choose 4}", "+", "2"])
|
||||
tex_mobject(["F", "=", r"{n \choose 2}", "+", "2", r"{n \choose 4}", "+", "n","-", "n", "-", r"{n \choose 4}", "+", "2"]).split()
|
||||
F[5], equals[5], nc2[5], plus1[5], two1[5], nc41[5], minus1[5], nc4[5], plus[5], two[5] = \
|
||||
tex_mobjects(["F", "=", r"{n \choose 2}", "+", "2", r"{n \choose 4}", "-", r"{n \choose 4}", "+", "2"])
|
||||
tex_mobject(["F", "=", r"{n \choose 2}", "+", "2", r"{n \choose 4}", "-", r"{n \choose 4}", "+", "2"]).split()
|
||||
F[6], equals[6], nc2[6], plus1[6], nc4[6], plus[6], two[6] = \
|
||||
tex_mobjects(["F", "=", r"{n \choose 2}", "+", r"{n \choose 4}", "+", "2"])
|
||||
tex_mobject(["F", "=", r"{n \choose 2}", "+", r"{n \choose 4}", "+", "2"]).split()
|
||||
F[7], equals[7], two[7], plus[7], nc2[7], plus1[7], nc4[7] = \
|
||||
tex_mobjects(["F", "=", "2", "+", r"{n \choose 2}", "+", r"{n \choose 4}"])
|
||||
tex_mobject(["F", "=", "2", "+", r"{n \choose 2}", "+", r"{n \choose 4}"]).split()
|
||||
shift_val = (0, 3, 0)
|
||||
for d in dicts:
|
||||
if not d:
|
||||
@ -1423,9 +1423,9 @@ class MoserSolutionInPascal(PascalsTriangleScene):
|
||||
term_color = "green"
|
||||
self.generate_n_choose_k_mobs()
|
||||
self.remove(*[self.coords_to_mobs[n0][k0] for n0, k0 in self.coords])
|
||||
terms = one, plus0, n_choose_2, plus1, n_choose_4 = tex_mobjects([
|
||||
terms = one, plus0, n_choose_2, plus1, n_choose_4 = tex_mobject([
|
||||
"1", "+", r"{%d \choose 2}"%n, "+", r"{%d \choose 4}"%n
|
||||
])
|
||||
]).split()
|
||||
target_terms = []
|
||||
for k in range(len(terms)):
|
||||
if k%2 == 0 and k <= n:
|
||||
@ -1537,9 +1537,9 @@ class ExplainNChoose2Formula(Scene):
|
||||
nums[x].shift((0, x*height, 0))
|
||||
nums_compound = CompoundMobject(*nums)
|
||||
nums_compound.shift(a_mob.get_center() - nums[0].get_center())
|
||||
n_mob, n_minus_1, over_2 = tex_mobjects([
|
||||
n_mob, n_minus_1, over_2 = tex_mobject([
|
||||
str(n), "(%d-1)"%n, r"\over{2}"
|
||||
])
|
||||
]).split()
|
||||
for part in n_mob, n_minus_1, over_2:
|
||||
part.shift((SPACE_WIDTH-1.5, SPACE_HEIGHT-1, 0))
|
||||
|
||||
@ -1603,10 +1603,10 @@ class ExplainNChoose4Formula(Scene):
|
||||
)
|
||||
quad_mobs = tuple_mobs[1::2]
|
||||
parens = CompoundMobject(*tuple_mobs[0::2])
|
||||
form_mobs = tex_mobjects([
|
||||
form_mobs = tex_mobject([
|
||||
str(n), "(%d-1)"%n, "(%d-2)"%n,"(%d-3)"%n,
|
||||
r"\over {4 \cdot 3 \cdot 2 \cdot 1}"
|
||||
])
|
||||
]).split()
|
||||
form_mobs = CompoundMobject(*form_mobs).scale(0.7).shift((4, 3, 0)).split()
|
||||
nums = [tex_mobject(str(k)) for k in range(1, n+1)]
|
||||
height = 1.5*nums[0].get_height()
|
||||
|
591
scripts/tau_poem.py
Normal file
591
scripts/tau_poem.py
Normal file
@ -0,0 +1,591 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import numpy as np
|
||||
import itertools as it
|
||||
from copy import deepcopy
|
||||
import sys
|
||||
|
||||
|
||||
from animation import *
|
||||
from mobject import *
|
||||
from constants import *
|
||||
from region import *
|
||||
from scene import Scene
|
||||
from script_wrapper import command_line_create_scene
|
||||
from generate_logo import LogoGeneration
|
||||
|
||||
POEM_LINES = """Fixed poorly in notation with that two,
|
||||
you shine so loud that you deserve a name.
|
||||
Late though we are to make a change, it's true,
|
||||
We can extol you 'til you have pi's fame.
|
||||
One might object, ``Conventions matter not!
|
||||
Great formulae cast truths transcending names.''
|
||||
I've noticed, though, how language molds my thoughts;
|
||||
the natural terms make heart and head the same.
|
||||
So lose the two inside your autograph,
|
||||
then guide our thoughts without your ``better'' half.
|
||||
Wonders math imparts become so neat
|
||||
when phrased with you, and pi remains off-screen.
|
||||
Sine and exp both cycle to your beat.
|
||||
Jive with Fourier, and forms are clean.
|
||||
``Wait! Area of circles'', pi would say,
|
||||
``sticks oddly to one half when tau's preferred.''
|
||||
More to you then! For write it in this way,
|
||||
then links to triangles can be inferred.
|
||||
Nix pi, then all within geometry
|
||||
shines clean and clear, as if by poetry.""".split("\n")
|
||||
|
||||
DIGIT_TO_WORD = {
|
||||
'0' : "Zero",
|
||||
'1' : "One",
|
||||
'2' : "Two",
|
||||
'3' : "Three",
|
||||
'4' : "Four",
|
||||
'5' : "Five",
|
||||
'6' : "Six",
|
||||
'7' : "Seven",
|
||||
'8' : "Eight",
|
||||
'9' : "Nine",
|
||||
}
|
||||
|
||||
FORMULAE = [
|
||||
"e^{x + \\tau i} = e^{x}",
|
||||
"&\\Leftrightarrow",
|
||||
"e^{x + 2\\pi i} = e^{x} \\\\",
|
||||
"A = \\frac{1}{2} \\tau r^2",
|
||||
"&\\Leftrightarrow",
|
||||
"A = \\pi r^2 \\\\",
|
||||
"n! \\sim \\sqrt{\\tau n}\\left(\\frac{n}{e}\\right)^n",
|
||||
"&\\Leftrightarrow",
|
||||
"n! \\sim \\sqrt{2\\pi n}\\left(\\frac{n}{e}\\right)^n \\\\",
|
||||
# "\\sum_{n = 0}^\\infty \\frac{(-1)^n}{2n+1} = \\frac{\\tau}{8}",
|
||||
# "&\\Leftrightarrow",
|
||||
# "\\sum_{n = 0}^\\infty \\frac{(-1)^n}{2n+1} = \\frac{\\pi}{4} \\\\",
|
||||
]
|
||||
|
||||
DIGITS = map(str, list("62831853071795864769"))
|
||||
DIGITS[1] = "." + DIGITS[1] #2->.2
|
||||
|
||||
BUFF = 1.0
|
||||
|
||||
MOVIE_PREFIX = "tau_poem/"
|
||||
|
||||
class Welcome(LogoGeneration):
|
||||
def construct(self):
|
||||
text = "Happy $\\tau$ Day, from 3Blue1Brown!"
|
||||
self.add(text_mobject(text).to_edge(UP))
|
||||
LogoGeneration.construct(self)
|
||||
|
||||
class HappyTauDayWords(Scene):
|
||||
def construct(self):
|
||||
words = text_mobject("Happy Tau Day Everybody!").scale(2)
|
||||
tau = TauCreature().move_to(2*LEFT + UP)
|
||||
pi = PiCreature().move_to(2*RIGHT + 3*DOWN)
|
||||
pi.highlight("red")
|
||||
self.add(words, tau, pi)
|
||||
self.dither()
|
||||
self.animate(BlinkPiCreature(tau))
|
||||
self.animate(BlinkPiCreature(pi))
|
||||
|
||||
class TauPoem(Scene):
|
||||
args_list = map(lambda x : (x,), range(len(POEM_LINES)))
|
||||
@staticmethod
|
||||
def args_to_string(line_num, *ignore):
|
||||
return str(line_num)
|
||||
|
||||
def __init__(self, line_num, *args, **kwargs):
|
||||
self.line_num = line_num
|
||||
self.anim_kwargs = {
|
||||
"run_time" : 4.0,
|
||||
}
|
||||
self.line_num_to_method = {
|
||||
0 : self.line0,
|
||||
1 : self.line1,
|
||||
2 : self.line2,
|
||||
3 : self.line3,
|
||||
4 : self.line4,
|
||||
5 : self.line5,
|
||||
6 : self.line6,
|
||||
7 : self.line7,
|
||||
8 : self.line8,
|
||||
9 : self.line9,
|
||||
10 : self.line10,
|
||||
11 : self.line11,
|
||||
12 : self.line12,
|
||||
13 : self.line13,
|
||||
14 : self.line14,
|
||||
15 : self.line15,
|
||||
16 : self.line16,
|
||||
17 : self.line17,
|
||||
18 : self.line18,
|
||||
19 : self.line19,
|
||||
}
|
||||
Scene.__init__(self, *args, **kwargs)
|
||||
|
||||
def construct(self):
|
||||
self.add_line_and_number()
|
||||
self.line_num_to_method[self.line_num]()
|
||||
self.first_word_to_last_digit()
|
||||
|
||||
def add_line_and_number(self):
|
||||
self.first_digits, new_digit, last_digits = tex_mobject([
|
||||
"".join(DIGITS[:self.line_num]),
|
||||
DIGITS[self.line_num],
|
||||
"".join(DIGITS[(self.line_num+1):]),
|
||||
]).to_edge(UP, buff=0.2).split()
|
||||
line_str = POEM_LINES[self.line_num]
|
||||
if self.line_num == 0:
|
||||
index = line_str.index("ed ")
|
||||
elif self.line_num == 10:
|
||||
index = line_str.index("ders")
|
||||
else:
|
||||
index = line_str.index(" ")
|
||||
first_word, rest_of_line = text_mobject(
|
||||
[line_str[:index], line_str[index:]]
|
||||
).to_edge(UP).shift(BUFF*DOWN).split()
|
||||
first_word.shift(0.15*RIGHT) #Stupid
|
||||
number_word = text_mobject(DIGIT_TO_WORD[DIGITS[self.line_num][-1]])
|
||||
number_word.shift(first_word.get_center())
|
||||
number_word.shift(BUFF * UP / 2)
|
||||
|
||||
kwargs = {
|
||||
"alpha_func" : squish_alpha_func(high_inflection_0_to_1),
|
||||
}
|
||||
self.add(first_word, rest_of_line, self.first_digits)
|
||||
self.first_word = first_word
|
||||
self.number_word = number_word
|
||||
self.new_digit = new_digit
|
||||
|
||||
def first_word_to_last_digit(self):
|
||||
if self.line_num == 19:
|
||||
shift_val = SPACE_HEIGHT*DOWN
|
||||
self.new_digit.shift(shift_val)
|
||||
self.animate(ApplyMethod(
|
||||
self.first_digits.shift, shift_val, run_time = 2.0
|
||||
))
|
||||
self.dither(2)
|
||||
self.animate_over_time_range(0, 2,
|
||||
Transform(
|
||||
deepcopy(self.first_word), self.number_word,
|
||||
alpha_func = squish_alpha_func(high_inflection_0_to_1)
|
||||
)
|
||||
)
|
||||
self.animate_over_time_range(2, 4,
|
||||
Transform(
|
||||
self.number_word, self.new_digit,
|
||||
alpha_func = squish_alpha_func(high_inflection_0_to_1)
|
||||
)
|
||||
)
|
||||
|
||||
def line0(self):
|
||||
two, pi = tex_mobject(["2", "\\pi"]).scale(2).split()
|
||||
self.add(two, pi)
|
||||
two_copy = deepcopy(two).rotate(np.pi/10).highlight("yellow")
|
||||
self.animate(Transform(
|
||||
two, two_copy,
|
||||
alpha_func = squish_alpha_func(
|
||||
lambda t : wiggle(t),
|
||||
0.4, 0.9,
|
||||
),
|
||||
**self.anim_kwargs
|
||||
))
|
||||
|
||||
def line1(self):
|
||||
two_pi = tex_mobject(["2", "\\pi"]).scale(2)
|
||||
tau = TauCreature()
|
||||
tau.to_symbol()
|
||||
sphere = Mobject()
|
||||
Mobject.interpolate(
|
||||
two_pi,
|
||||
Sphere().highlight("yellow"),
|
||||
sphere,
|
||||
0.8
|
||||
)
|
||||
self.add(two_pi)
|
||||
self.dither()
|
||||
self.animate(SemiCircleTransform(
|
||||
two_pi, sphere,
|
||||
alpha_func = lambda t : 2*high_inflection_0_to_1(t/2)
|
||||
))
|
||||
self.remove(two_pi)
|
||||
self.animate(SemiCircleTransform(
|
||||
sphere, tau,
|
||||
alpha_func = lambda t : 2*(high_inflection_0_to_1(t/2+0.5)-0.5)
|
||||
))
|
||||
self.remove(sphere)
|
||||
self.add(tau)
|
||||
self.dither()
|
||||
|
||||
def line2(self):
|
||||
tau = TauCreature()
|
||||
tau.make_sad()
|
||||
tau.mouth.points = np.array(sorted(
|
||||
tau.mouth.points,
|
||||
lambda p0, p1 : cmp(p0[0], p1[0])
|
||||
))
|
||||
blinked = deepcopy(tau).blink()
|
||||
for eye in blinked.eyes:
|
||||
eye.highlight("black")
|
||||
self.add(*set(tau.parts).difference(tau.white_parts))
|
||||
self.animate(*[
|
||||
Transform(*eyes)
|
||||
for eyes in zip(blinked.eyes, tau.eyes)
|
||||
])
|
||||
self.animate(ShowCreation(tau.mouth))
|
||||
self.dither(2)
|
||||
|
||||
def line3(self):
|
||||
tau = TauCreature().make_sad()
|
||||
pi = PiCreature()
|
||||
self.add(*tau.parts)
|
||||
self.dither()
|
||||
self.animate(
|
||||
Transform(tau.leg, pi.left_leg),
|
||||
ShowCreation(pi.right_leg),
|
||||
run_time = 1.0,
|
||||
)
|
||||
self.animate(*[
|
||||
Transform(*parts)
|
||||
for parts in zip(tau.white_parts, pi.white_parts)
|
||||
])
|
||||
self.remove(*tau.parts + pi.parts)
|
||||
self.animate(BlinkPiCreature(pi))
|
||||
|
||||
def pi_speaking(self, text):
|
||||
pi = PiCreature()
|
||||
pi.highlight("red").give_straight_face()
|
||||
pi.shift(3*DOWN + LEFT)
|
||||
bubble = SpeechBubble().speak_from(pi)
|
||||
bubble.write(text)
|
||||
return pi, bubble
|
||||
|
||||
def line4(self):
|
||||
pi, bubble = self.pi_speaking("Conventions matter \\\\ not!")
|
||||
self.add(pi)
|
||||
self.dither()
|
||||
self.animate(Transform(
|
||||
Point(bubble.tip).highlight("black"),
|
||||
bubble
|
||||
))
|
||||
|
||||
|
||||
def line5(self):
|
||||
pi, bubble = self.pi_speaking("""
|
||||
Great formulae cast \\\\
|
||||
truths transcending \\\\
|
||||
names.
|
||||
""")
|
||||
self.add(pi, bubble)
|
||||
|
||||
formulae = tex_mobject(FORMULAE, size = "\\small")
|
||||
formulae.scale(1.25)
|
||||
formulae.to_corner([1, -1, 0])
|
||||
self.animate(FadeIn(formulae))
|
||||
|
||||
def line6(self):
|
||||
bubble = ThoughtBubble()
|
||||
self.animate(ApplyFunction(
|
||||
lambda p : 2 * p / np.linalg.norm(p),
|
||||
bubble,
|
||||
alpha_func = wiggle,
|
||||
run_time = 3.0,
|
||||
))
|
||||
|
||||
def line7(self):
|
||||
bubble = ThoughtBubble()
|
||||
heart = ImageMobject("heart")
|
||||
heart.scale(0.5).shift(DOWN).highlight("red")
|
||||
for mob in bubble, heart:
|
||||
mob.sort_points(np.linalg.norm)
|
||||
|
||||
self.add(bubble)
|
||||
self.dither()
|
||||
self.remove(bubble)
|
||||
bubble_copy = deepcopy(bubble)
|
||||
self.animate(SemiCircleTransform(bubble_copy, heart))
|
||||
self.dither()
|
||||
self.remove(bubble_copy)
|
||||
self.animate(SemiCircleTransform(heart, bubble))
|
||||
self.dither()
|
||||
|
||||
|
||||
def line8(self):
|
||||
pi = PiCreature().give_straight_face()
|
||||
tau = TauCreature()
|
||||
two = ImageMobject("big2").scale(0.5).shift(1.6*LEFT + 0.1*DOWN)
|
||||
|
||||
self.add(two, *pi.parts)
|
||||
self.dither()
|
||||
self.animate(
|
||||
Transform(pi.left_leg, tau.leg),
|
||||
Transform(
|
||||
pi.right_leg,
|
||||
Point(pi.right_leg.points[0,:]).highlight("black")
|
||||
),
|
||||
Transform(pi.mouth, tau.mouth),
|
||||
SemiCircleTransform(
|
||||
two,
|
||||
Dot(two.get_center()).highlight("black")
|
||||
)
|
||||
)
|
||||
|
||||
def line9(self):
|
||||
tau = TauCreature()
|
||||
pi = PiCreature().highlight("red").give_straight_face()
|
||||
pi.scale(0.2).move_to(tau.arm.points[-1,:])
|
||||
point = Point(pi.get_center()).highlight("black")
|
||||
vanish_local = 3*(LEFT + UP)
|
||||
new_pi = deepcopy(pi)
|
||||
new_pi.scale(0.01)
|
||||
new_pi.rotate(np.pi)
|
||||
new_pi.shift(vanish_local)
|
||||
Mobject.highlight(new_pi, "black")
|
||||
|
||||
self.add(tau)
|
||||
self.dither()
|
||||
self.animate(Transform(point, pi))
|
||||
self.remove(point)
|
||||
self.add(pi)
|
||||
self.animate(WaveArm(tau),Transform(pi, new_pi))
|
||||
|
||||
def line10(self):
|
||||
formulae = tex_mobject(FORMULAE, "\\small")
|
||||
formulae.scale(1.5).to_edge(DOWN)
|
||||
self.add(formulae)
|
||||
|
||||
def line11(self):
|
||||
formulae = tex_mobject(FORMULAE, "\\small")
|
||||
formulae.scale(1.5).to_edge(DOWN)
|
||||
formulae = formulae.split()
|
||||
f_copy = deepcopy(formulae)
|
||||
for mob, count in zip(f_copy, it.count()):
|
||||
if count%3 == 0:
|
||||
mob.to_edge(LEFT).shift(RIGHT*(SPACE_WIDTH-1))
|
||||
else:
|
||||
mob.shift(2*SPACE_WIDTH*RIGHT)
|
||||
self.animate(*[
|
||||
Transform(*mobs, run_time = 2.0)
|
||||
for mobs in zip(formulae, f_copy)
|
||||
])
|
||||
|
||||
def line12(self):
|
||||
interval_size = 0.5
|
||||
axes_center = SPACE_WIDTH*LEFT/2
|
||||
grid_center = SPACE_WIDTH*RIGHT/2
|
||||
radius = SPACE_WIDTH / 2.0
|
||||
axes = Axes(
|
||||
radius = radius,
|
||||
interval_size = interval_size
|
||||
)
|
||||
axes.shift(axes_center)
|
||||
def sine_curve(t):
|
||||
t += 1
|
||||
result = np.array((-np.pi*t, np.sin(np.pi*t), 0))
|
||||
result *= interval_size
|
||||
result += axes_center
|
||||
return result
|
||||
sine = ParametricFunction(sine_curve)
|
||||
sine_period = Line(
|
||||
axes_center,
|
||||
axes_center + 2*np.pi*interval_size*RIGHT
|
||||
)
|
||||
grid = Grid(radius = radius).shift(grid_center)
|
||||
circle = Circle().scale(interval_size).shift(grid_center)
|
||||
grid.add(tex_mobject("e^{ix}").shift(grid_center+UP+RIGHT))
|
||||
circle.highlight("white")
|
||||
tau_line = Line(
|
||||
*[np.pi*interval_size*vect for vect in LEFT, RIGHT],
|
||||
density = 5*DEFAULT_POINT_DENSITY_1D
|
||||
)
|
||||
tau_line.highlight("red")
|
||||
tau = tex_mobject("\\tau")
|
||||
tau.shift(tau_line.get_center() + 0.5*UP)
|
||||
|
||||
self.add(axes, grid)
|
||||
self.animate(
|
||||
TransformAnimations(
|
||||
ShowCreation(sine),
|
||||
ShowCreation(deepcopy(sine).shift(2*np.pi*interval_size*RIGHT)),
|
||||
run_time = 2.0,
|
||||
alpha_func = high_inflection_0_to_1
|
||||
),
|
||||
ShowCreation(circle)
|
||||
)
|
||||
self.animate(
|
||||
SemiCircleTransform(sine_period, tau_line),
|
||||
SemiCircleTransform(circle, deepcopy(tau_line)),
|
||||
FadeOut(axes),
|
||||
FadeOut(grid),
|
||||
FadeOut(sine),
|
||||
FadeIn(tau),
|
||||
)
|
||||
self.dither()
|
||||
|
||||
|
||||
def line13(self):
|
||||
formula = form_start, two_pi, form_end = tex_mobject([
|
||||
"\\hat{f^{(n)}}(\\xi) = (",
|
||||
"2\\pi",
|
||||
"i\\xi)^n \\hat{f}(\\xi)"
|
||||
]).shift(DOWN).split()
|
||||
tau = TauCreature().center()
|
||||
tau.scale(two_pi.get_width()/tau.get_width())
|
||||
tau.shift(two_pi.get_center()+0.2*UP)
|
||||
tau.rewire_part_attributes()
|
||||
|
||||
self.add(*formula)
|
||||
self.dither()
|
||||
self.animate(SemiCircleTransform(two_pi, tau))
|
||||
self.remove(two_pi)
|
||||
self.animate(BlinkPiCreature(tau))
|
||||
self.dither()
|
||||
|
||||
def line14(self):
|
||||
pi, bubble = self.pi_speaking(
|
||||
"Wait! Area \\\\ of circles"
|
||||
)
|
||||
self.add(pi)
|
||||
self.animate(
|
||||
Transform(Point(bubble.tip).highlight("black"), bubble)
|
||||
)
|
||||
|
||||
def line15(self):
|
||||
pi, bubble = self.pi_speaking(
|
||||
"sticks oddly \\\\ to one half when \\\\ tau's preferred."
|
||||
)
|
||||
formula = form_start, half, form_end = tex_mobject([
|
||||
"A = ",
|
||||
"\\frac{1}{2}",
|
||||
"\\tau r^2"
|
||||
]).split()
|
||||
|
||||
self.add(pi, bubble, *formula)
|
||||
self.dither(2)
|
||||
self.animate(ApplyMethod(half.highlight, "yellow"))
|
||||
|
||||
def line16(self):
|
||||
self.add(tex_mobject(
|
||||
"\\frac{1}{2}\\tau r^2"
|
||||
).scale(2).shift(DOWN))
|
||||
|
||||
def line17(self):
|
||||
circle = Dot(
|
||||
radius = 1,
|
||||
density = 4*DEFAULT_POINT_DENSITY_1D
|
||||
)
|
||||
blue_rgb = np.array(Color("blue").get_rgb())
|
||||
white_rgb = np.ones(3)
|
||||
circle.rgbs = np.array([
|
||||
alpha * blue_rgb + (1 - alpha) * white_rgb
|
||||
for alpha in np.arange(0, 1, 1.0/len(circle.rgbs))
|
||||
])
|
||||
for index in range(circle.points.shape[0]):
|
||||
circle.rgbs
|
||||
def trianglify((x, y, z)):
|
||||
norm = np.linalg.norm((x, y, z))
|
||||
comp = complex(x, y)*complex(0, 1)
|
||||
return (
|
||||
norm * np.log(comp).imag,
|
||||
-norm,
|
||||
0
|
||||
)
|
||||
tau_r = tex_mobject("\\tau r").shift(1.3*DOWN)
|
||||
r = tex_mobject("r").shift(0.2*RIGHT + 0.7*DOWN)
|
||||
lines = [
|
||||
Line(DOWN+np.pi*LEFT, DOWN+np.pi*RIGHT),
|
||||
Line(ORIGIN, DOWN)
|
||||
]
|
||||
for line in lines:
|
||||
line.highlight("red")
|
||||
|
||||
self.animate(ApplyFunction(trianglify, circle, run_time = 2.0))
|
||||
self.add(tau_r, r)
|
||||
self.animate(*[
|
||||
ShowCreation(line, run_time = 1.0)
|
||||
for line in lines
|
||||
])
|
||||
self.dither()
|
||||
|
||||
def line18(self):
|
||||
tau = TauCreature()
|
||||
tau.shift_eyes()
|
||||
tau.move_to(DOWN)
|
||||
pi = PiCreature()
|
||||
pi.highlight("red")
|
||||
pi.move_to(DOWN + 3*LEFT)
|
||||
mad_tau = deepcopy(tau).make_mean()
|
||||
mad_tau.arm.wag(0.5*UP, LEFT, 2.0)
|
||||
sad_pi = deepcopy(pi).shift_eyes().make_sad()
|
||||
blinked_tau = deepcopy(tau).blink()
|
||||
blinked_pi = deepcopy(pi).blink()
|
||||
|
||||
self.add(*pi.parts + tau.parts)
|
||||
self.dither(0.8)
|
||||
self.animate(*[
|
||||
Transform(*eyes, run_time = 0.2, alpha_func = rush_into)
|
||||
for eyes in [
|
||||
(tau.left_eye, blinked_tau.left_eye),
|
||||
(tau.right_eye, blinked_tau.right_eye),
|
||||
]
|
||||
])
|
||||
self.remove(tau.left_eye, tau.right_eye)
|
||||
self.animate(*[
|
||||
Transform(*eyes, run_time = 0.2, alpha_func = rush_from)
|
||||
for eyes in [
|
||||
(blinked_tau.left_eye, mad_tau.left_eye),
|
||||
(blinked_tau.right_eye, mad_tau.right_eye),
|
||||
]
|
||||
])
|
||||
self.remove(blinked_tau.left_eye, blinked_tau.right_eye)
|
||||
self.add(mad_tau.left_eye, mad_tau.right_eye)
|
||||
self.animate(
|
||||
Transform(tau.arm, mad_tau.arm),
|
||||
Transform(tau.mouth, mad_tau.mouth),
|
||||
run_time = 0.5
|
||||
)
|
||||
self.remove(*tau.parts + blinked_tau.parts)
|
||||
self.add(*mad_tau.parts)
|
||||
|
||||
self.animate(*[
|
||||
Transform(*eyes, run_time = 0.2, alpha_func = rush_into)
|
||||
for eyes in [
|
||||
(pi.left_eye, blinked_pi.left_eye),
|
||||
(pi.right_eye, blinked_pi.right_eye),
|
||||
]
|
||||
])
|
||||
self.remove(pi.left_eye, pi.right_eye)
|
||||
self.animate(*[
|
||||
Transform(*eyes, run_time = 0.2, alpha_func = rush_from)
|
||||
for eyes in [
|
||||
(blinked_pi.left_eye, sad_pi.left_eye),
|
||||
(blinked_pi.right_eye, sad_pi.right_eye),
|
||||
]
|
||||
] + [
|
||||
Transform(pi.mouth, sad_pi.mouth, run_time = 0.2)
|
||||
])
|
||||
self.remove(*blinked_pi.parts + pi.parts + sad_pi.parts)
|
||||
self.animate(
|
||||
WalkPiCreature(sad_pi, DOWN+4*LEFT),
|
||||
run_time = 1.0
|
||||
)
|
||||
self.dither()
|
||||
|
||||
def line19(self):
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
command_line_create_scene(MOVIE_PREFIX)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user