mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
Maybe 2/3 through inventing math project
This commit is contained in:
parent
9cf8e7b75e
commit
5b228ba8da
14 changed files with 1216 additions and 118 deletions
|
@ -9,11 +9,34 @@ from mobject import Mobject, Point
|
||||||
from constants import *
|
from constants import *
|
||||||
from helpers import *
|
from helpers import *
|
||||||
|
|
||||||
|
def straight_path(start_points, end_points, alpha):
|
||||||
|
return (1-alpha)*start_points + alpha*end_points
|
||||||
|
|
||||||
|
def semi_circular_path(start_points, end_points, alpha, axis):
|
||||||
|
midpoints = (start_points + end_points) / 2
|
||||||
|
angle = alpha * np.pi
|
||||||
|
rot_matrix = rotation_matrix(angle, axis)[:2, :2]
|
||||||
|
result = np.zeros(start_points.shape)
|
||||||
|
result[:,:2] = np.dot(
|
||||||
|
(start_points - midpoints)[:,:2],
|
||||||
|
np.transpose(rot_matrix)
|
||||||
|
) + midpoints[:,:2]
|
||||||
|
result[:,2] = (1-alpha)*start_points[:,2] + alpha*end_points[:,2]
|
||||||
|
return result
|
||||||
|
|
||||||
|
def clockwise_path(start_points, end_points, alpha):
|
||||||
|
return semi_circular_path(start_points, end_points, alpha, IN)
|
||||||
|
|
||||||
|
def counterclockwise_path(start_points, end_points, alpha):
|
||||||
|
return semi_circular_path(start_points, end_points, alpha, OUT)
|
||||||
|
|
||||||
class Transform(Animation):
|
class Transform(Animation):
|
||||||
def __init__(self, mobject1, mobject2,
|
def __init__(self, mobject1, mobject2,
|
||||||
run_time = DEFAULT_TRANSFORM_RUN_TIME,
|
run_time = DEFAULT_TRANSFORM_RUN_TIME,
|
||||||
|
interpolation_function = straight_path,
|
||||||
black_out_extra_points = False,
|
black_out_extra_points = False,
|
||||||
*args, **kwargs):
|
*args, **kwargs):
|
||||||
|
self.interpolation_function = interpolation_function
|
||||||
count1, count2 = mobject1.get_num_points(), mobject2.get_num_points()
|
count1, count2 = mobject1.get_num_points(), mobject2.get_num_points()
|
||||||
if count2 == 0:
|
if count2 == 0:
|
||||||
mobject2 = Point((SPACE_WIDTH, SPACE_HEIGHT, 0))
|
mobject2 = Point((SPACE_WIDTH, SPACE_HEIGHT, 0))
|
||||||
|
@ -37,10 +60,14 @@ class Transform(Animation):
|
||||||
self.non_redundant_m2_indices = indices
|
self.non_redundant_m2_indices = indices
|
||||||
|
|
||||||
def update_mobject(self, alpha):
|
def update_mobject(self, alpha):
|
||||||
Mobject.interpolate(
|
self.mobject.points = self.interpolation_function(
|
||||||
self.starting_mobject,
|
self.starting_mobject.points,
|
||||||
self.ending_mobject,
|
self.ending_mobject.points,
|
||||||
self.mobject,
|
alpha
|
||||||
|
)
|
||||||
|
self.mobject.rgbs = straight_path(
|
||||||
|
self.starting_mobject.rgbs,
|
||||||
|
self.ending_mobject.rgbs,
|
||||||
alpha
|
alpha
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -59,23 +86,19 @@ class Transform(Animation):
|
||||||
)[self.non_redundant_m2_indices]
|
)[self.non_redundant_m2_indices]
|
||||||
)
|
)
|
||||||
|
|
||||||
class SemiCircleTransform(Transform):
|
class ClockwiseTransform(Transform):
|
||||||
def __init__(self, mobject1, mobject2, counterclockwise = True,
|
def __init__(self, mobject1, mobject2, **kwargs):
|
||||||
*args, **kwargs):
|
Transform.__init__(
|
||||||
Transform.__init__(self, mobject1, mobject2, *args, **kwargs)
|
self, mobject1, mobject2,
|
||||||
self.axis = (0, 0, 1) if counterclockwise else (0, 0, -1)
|
interpolation_function = clockwise_path, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
def update_mobject(self, alpha):
|
class CounterclockwiseTransform(Transform):
|
||||||
sm, em = self.starting_mobject, self.ending_mobject
|
def __init__(self, mobject1, mobject2, **kwargs):
|
||||||
midpoints = (sm.points + em.points) / 2
|
Transform.__init__(
|
||||||
angle = alpha * np.pi
|
self, mobject1, mobject2,
|
||||||
rot_matrix = rotation_matrix(angle, self.axis)[:2, :2]
|
interpolation_function = counterclockwise_path, **kwargs
|
||||||
self.mobject.points[:,:2] = np.dot(
|
)
|
||||||
(sm.points - midpoints)[:,:2],
|
|
||||||
np.transpose(rot_matrix)
|
|
||||||
) + midpoints[:,:2]
|
|
||||||
self.mobject.points[:,2] = (1-alpha)*sm.points[:,2] + alpha*em.points[:,2]
|
|
||||||
self.mobject.rgbs = (1-alpha)*sm.rgbs + alpha*em.rgbs
|
|
||||||
|
|
||||||
class FadeToColor(Transform):
|
class FadeToColor(Transform):
|
||||||
def __init__(self, mobject, color, *args, **kwargs):
|
def __init__(self, mobject, color, *args, **kwargs):
|
||||||
|
@ -119,12 +142,25 @@ class ApplyMethod(Transform):
|
||||||
)
|
)
|
||||||
|
|
||||||
class ApplyFunction(Transform):
|
class ApplyFunction(Transform):
|
||||||
def __init__(self, mobject, function, run_time = DEFAULT_ANIMATION_RUN_TIME,
|
def __init__(self, function, mobject, **kwargs):
|
||||||
*args, **kwargs):
|
Transform.__init__(
|
||||||
|
self,
|
||||||
|
mobject,
|
||||||
|
function(copy.deepcopy(mobject)),
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
self.name = "ApplyFunctionTo"+str(mobject)
|
||||||
|
|
||||||
|
|
||||||
|
class ApplyPointwiseFunction(Transform):
|
||||||
|
def __init__(self, mobject, function,
|
||||||
|
run_time = DEFAULT_ANIMATION_RUN_TIME, **kwargs):
|
||||||
map_image = copy.deepcopy(mobject)
|
map_image = copy.deepcopy(mobject)
|
||||||
map_image.points = np.array(map(function, map_image.points))
|
map_image.points = np.array(map(function, map_image.points))
|
||||||
Transform.__init__(self, mobject, map_image, run_time = run_time,
|
Transform.__init__(
|
||||||
*args, **kwargs)
|
self, mobject, map_image,
|
||||||
|
run_time = run_time, **kwargs
|
||||||
|
)
|
||||||
self.name = "".join([
|
self.name = "".join([
|
||||||
"Apply",
|
"Apply",
|
||||||
"".join([s.capitalize() for s in function.__name__.split("_")]),
|
"".join([s.capitalize() for s in function.__name__.split("_")]),
|
||||||
|
|
|
@ -41,8 +41,8 @@ UP = np.array(( 0, 1, 0))
|
||||||
DOWN = np.array(( 0,-1, 0))
|
DOWN = np.array(( 0,-1, 0))
|
||||||
RIGHT = np.array(( 1, 0, 0))
|
RIGHT = np.array(( 1, 0, 0))
|
||||||
LEFT = np.array((-1, 0, 0))
|
LEFT = np.array((-1, 0, 0))
|
||||||
IN = np.array(( 0, 0, 1))
|
IN = np.array(( 0, 0,-1))
|
||||||
OUT = np.array(( 0, 0,-1))
|
OUT = np.array(( 0, 0, 1))
|
||||||
|
|
||||||
THIS_DIR = os.path.dirname(os.path.realpath(__file__))
|
THIS_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||||
FILE_DIR = os.path.join(THIS_DIR, "files")
|
FILE_DIR = os.path.join(THIS_DIR, "files")
|
||||||
|
@ -61,6 +61,7 @@ SIZE_TO_REPLACE = "SizeHere"
|
||||||
TEX_TEXT_TO_REPLACE = "YourTextHere"
|
TEX_TEXT_TO_REPLACE = "YourTextHere"
|
||||||
TEMPLATE_TEX_FILE = os.path.join(TEX_DIR, "template.tex")
|
TEMPLATE_TEX_FILE = os.path.join(TEX_DIR, "template.tex")
|
||||||
TEMPLATE_TEXT_FILE = os.path.join(TEX_DIR, "text_template.tex")
|
TEMPLATE_TEXT_FILE = os.path.join(TEX_DIR, "text_template.tex")
|
||||||
|
MAX_LEN_FOR_HUGE_TEX_FONT = 25
|
||||||
|
|
||||||
LOGO_PATH = os.path.join(IMAGE_DIR, "logo.png")
|
LOGO_PATH = os.path.join(IMAGE_DIR, "logo.png")
|
||||||
|
|
||||||
|
|
|
@ -81,18 +81,26 @@ class VideoIcon(ImageMobject):
|
||||||
self.scale(0.3)
|
self.scale(0.3)
|
||||||
self.center()
|
self.center()
|
||||||
|
|
||||||
def text_mobject(text, size = "\\Large"):
|
def text_mobject(text, size = None):
|
||||||
|
size = size or "\\Large" #TODO, auto-adjust?
|
||||||
return tex_mobject(text, size, TEMPLATE_TEXT_FILE)
|
return tex_mobject(text, size, TEMPLATE_TEXT_FILE)
|
||||||
|
|
||||||
def tex_mobject(expression,
|
def tex_mobject(expression,
|
||||||
size = "\\Huge",
|
size = None,
|
||||||
template_tex_file = TEMPLATE_TEX_FILE):
|
template_tex_file = TEMPLATE_TEX_FILE):
|
||||||
|
if size == None:
|
||||||
|
if len("".join(expression)) < MAX_LEN_FOR_HUGE_TEX_FONT:
|
||||||
|
size = "\\Huge"
|
||||||
|
else:
|
||||||
|
size = "\\large"
|
||||||
|
#Todo, make this more sophisticated.
|
||||||
images = tex_to_image(expression, size, template_tex_file)
|
images = tex_to_image(expression, size, template_tex_file)
|
||||||
if isinstance(images, list):
|
if isinstance(images, list):
|
||||||
#TODO, is checking listiness really the best here?
|
#TODO, is checking listiness really the best here?
|
||||||
return CompoundMobject(*map(ImageMobject, images)).center()
|
result = CompoundMobject(*map(ImageMobject, images))
|
||||||
else:
|
else:
|
||||||
return ImageMobject(images).center()
|
result = ImageMobject(images)
|
||||||
|
return result.highlight("white").center()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -146,11 +146,15 @@ class Mobject(object):
|
||||||
center = self.get_center()
|
center = self.get_center()
|
||||||
return self.center().scale(scale_factor).shift(center)
|
return self.center().scale(scale_factor).shift(center)
|
||||||
|
|
||||||
|
def stretch(self, factor, dim):
|
||||||
|
self.points[:,dim] *= factor
|
||||||
|
return self
|
||||||
|
|
||||||
def stretch_to_fit(self, length, dim):
|
def stretch_to_fit(self, length, dim):
|
||||||
center = self.get_center()
|
center = self.get_center()
|
||||||
old_length = max(self.points[:,dim]) - min(self.points[:,dim])
|
old_length = max(self.points[:,dim]) - min(self.points[:,dim])
|
||||||
self.center()
|
self.center()
|
||||||
self.points[:,dim] *= length/old_length
|
self.stretch(length/old_length, dim)
|
||||||
self.shift(center)
|
self.shift(center)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -178,6 +182,9 @@ class Mobject(object):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def replace(self, mobject, stretch = False):
|
def replace(self, mobject, stretch = False):
|
||||||
|
if mobject.get_num_points() == 0:
|
||||||
|
raise Warning("Attempting to replace mobject with no points")
|
||||||
|
return self
|
||||||
if stretch:
|
if stretch:
|
||||||
self.stretch_to_fit_width(mobject.get_width())
|
self.stretch_to_fit_width(mobject.get_width())
|
||||||
self.stretch_to_fit_height(mobject.get_height())
|
self.stretch_to_fit_height(mobject.get_height())
|
||||||
|
@ -231,11 +238,34 @@ class Mobject(object):
|
||||||
### Getters ###
|
### Getters ###
|
||||||
|
|
||||||
def get_num_points(self):
|
def get_num_points(self):
|
||||||
return self.points.shape[0]
|
return len(self.points)
|
||||||
|
|
||||||
def get_center(self):
|
def get_center(self):
|
||||||
|
return (np.max(self.points, 0) + np.min(self.points, 0))/2.0
|
||||||
|
|
||||||
|
def get_center_of_mass(self):
|
||||||
return np.apply_along_axis(np.mean, 0, self.points)
|
return np.apply_along_axis(np.mean, 0, self.points)
|
||||||
|
|
||||||
|
def get_border_point(self, direction):
|
||||||
|
return self.points[np.argmax(np.dot(self.points, direction))]
|
||||||
|
|
||||||
|
def get_edge_center(self, dim, max_or_min_func):
|
||||||
|
result = self.get_center()
|
||||||
|
result[dim] = max_or_min_func(self.points[:,dim])
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_top(self):
|
||||||
|
return self.get_edge_center(1, np.max)
|
||||||
|
|
||||||
|
def get_bottom(self):
|
||||||
|
return self.get_edge_center(1, np.min)
|
||||||
|
|
||||||
|
def get_right(self):
|
||||||
|
return self.get_edge_center(0, np.max)
|
||||||
|
|
||||||
|
def get_left(self):
|
||||||
|
return self.get_edge_center(0, np.min)
|
||||||
|
|
||||||
def get_width(self):
|
def get_width(self):
|
||||||
return np.max(self.points[:, 0]) - np.min(self.points[:, 0])
|
return np.max(self.points[:, 0]) - np.min(self.points[:, 0])
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ from constants import *
|
||||||
from helpers import *
|
from helpers import *
|
||||||
|
|
||||||
class Point(Mobject):
|
class Point(Mobject):
|
||||||
|
DEFAULT_COLOR = "black"
|
||||||
def __init__(self, point = (0, 0, 0), *args, **kwargs):
|
def __init__(self, point = (0, 0, 0), *args, **kwargs):
|
||||||
Mobject.__init__(self, *args, **kwargs)
|
Mobject.__init__(self, *args, **kwargs)
|
||||||
self.points = np.array(point).reshape(1, 3)
|
self.points = np.array(point).reshape(1, 3)
|
||||||
|
@ -21,16 +22,18 @@ class Arrow(Mobject1D):
|
||||||
length = 1,
|
length = 1,
|
||||||
tip_length = 0.25,
|
tip_length = 0.25,
|
||||||
normal = (0, 0, 1),
|
normal = (0, 0, 1),
|
||||||
|
density = DEFAULT_POINT_DENSITY_1D,
|
||||||
*args, **kwargs):
|
*args, **kwargs):
|
||||||
self.point = np.array(point)
|
self.point = np.array(point)
|
||||||
if tail is not None:
|
if tail is not None:
|
||||||
direction = self.point - tail
|
direction = self.point - tail
|
||||||
length = np.linalg.norm(direction)
|
length = np.linalg.norm(direction)
|
||||||
self.direction = np.array(direction) / np.linalg.norm(direction)
|
self.direction = np.array(direction) / np.linalg.norm(direction)
|
||||||
|
density *= max(length, 0.1)
|
||||||
self.length = length
|
self.length = length
|
||||||
self.normal = np.array(normal)
|
self.normal = np.array(normal)
|
||||||
self.tip_length = tip_length
|
self.tip_length = tip_length
|
||||||
Mobject1D.__init__(self, *args, **kwargs)
|
Mobject1D.__init__(self, density = density, **kwargs)
|
||||||
|
|
||||||
def generate_points(self):
|
def generate_points(self):
|
||||||
self.add_points([
|
self.add_points([
|
||||||
|
@ -131,12 +134,34 @@ class CurvedLine(Line):
|
||||||
|
|
||||||
class Circle(Mobject1D):
|
class Circle(Mobject1D):
|
||||||
DEFAULT_COLOR = "red"
|
DEFAULT_COLOR = "red"
|
||||||
|
def __init__(self, radius = 1.0, **kwargs):
|
||||||
|
self.radius = radius
|
||||||
|
Mobject1D.__init__(self, **kwargs)
|
||||||
|
|
||||||
def generate_points(self):
|
def generate_points(self):
|
||||||
self.add_points([
|
self.add_points([
|
||||||
(np.cos(theta), np.sin(theta), 0)
|
(self.radius*np.cos(theta), self.radius*np.sin(theta), 0)
|
||||||
for theta in np.arange(0, 2 * np.pi, self.epsilon)
|
for theta in np.arange(0, 2 * np.pi, self.epsilon/self.radius)
|
||||||
])
|
])
|
||||||
|
|
||||||
|
class Rectangle(Mobject1D):
|
||||||
|
DEFAULT_COLOR = "yellow"
|
||||||
|
def __init__(self, height = 2.0, width = 2.0, **kwargs):
|
||||||
|
self.height, self.width = height, width
|
||||||
|
Mobject1D.__init__(self, **kwargs)
|
||||||
|
|
||||||
|
def generate_points(self):
|
||||||
|
wh = [self.width/2.0, self.height/2.0]
|
||||||
|
self.add_points([
|
||||||
|
(x, u, 0) if dim==0 else (u, x, 0)
|
||||||
|
for dim in 0, 1
|
||||||
|
for u in wh[1-dim], -wh[1-dim]
|
||||||
|
for x in np.arange(-wh[dim], wh[dim], self.epsilon)
|
||||||
|
])
|
||||||
|
|
||||||
|
class Square(Rectangle):
|
||||||
|
def __init__(self, side_length = 2.0, **kwargs):
|
||||||
|
Rectangle.__init__(self, side_length, side_length, **kwargs)
|
||||||
|
|
||||||
class Bubble(Mobject):
|
class Bubble(Mobject):
|
||||||
def __init__(self, direction = LEFT, index_of_tip = -1, center = ORIGIN):
|
def __init__(self, direction = LEFT, index_of_tip = -1, center = ORIGIN):
|
||||||
|
@ -151,7 +176,7 @@ class Bubble(Mobject):
|
||||||
return self.points[self.index_of_tip]
|
return self.points[self.index_of_tip]
|
||||||
|
|
||||||
def get_bubble_center(self):
|
def get_bubble_center(self):
|
||||||
return Mobject.get_center(self)+self.center_offset
|
return self.get_center()+self.center_offset
|
||||||
|
|
||||||
def move_tip_to(self, point):
|
def move_tip_to(self, point):
|
||||||
self.shift(point - self.get_tip())
|
self.shift(point - self.get_tip())
|
||||||
|
@ -168,8 +193,7 @@ class Bubble(Mobject):
|
||||||
def add_content(self, mobject):
|
def add_content(self, mobject):
|
||||||
mobject.scale(0.75*self.get_width() / mobject.get_width())
|
mobject.scale(0.75*self.get_width() / mobject.get_width())
|
||||||
mobject.shift(self.get_bubble_center())
|
mobject.shift(self.get_bubble_center())
|
||||||
self.content = CompoundMobject(self.content, mobject)
|
self.content = mobject
|
||||||
self.add(self.content)
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def write(self, text):
|
def write(self, text):
|
||||||
|
@ -177,10 +201,7 @@ class Bubble(Mobject):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
num_content_points = self.content.points.shape[0]
|
self.content = Mobject()
|
||||||
self.points = self.points[:-num_content_points]
|
|
||||||
self.rgbs = self.rgbs[:-num_content_points]
|
|
||||||
self.contents = Mobject()
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
class SpeechBubble(Bubble):
|
class SpeechBubble(Bubble):
|
||||||
|
|
|
@ -10,14 +10,24 @@ from animation import *
|
||||||
from mobject import *
|
from mobject import *
|
||||||
from constants import *
|
from constants import *
|
||||||
from region import *
|
from region import *
|
||||||
from scene import Scene
|
from scene import Scene, RearrangeEquation
|
||||||
from script_wrapper import command_line_create_scene
|
from script_wrapper import command_line_create_scene
|
||||||
|
|
||||||
|
|
||||||
class SampleScene(Scene):
|
class SampleScene(RearrangeEquation):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
tauy = TauCreature()
|
start_terms = "a + b = c".split(" ")
|
||||||
self.animate(ApplyMethod(tauy.make_sad))
|
end_terms = "a = c - b + 0".split(" ")
|
||||||
|
index_map = {
|
||||||
|
0 : 0,
|
||||||
|
1 : 3,
|
||||||
|
2 : 4,
|
||||||
|
3 : 1,
|
||||||
|
4 : 2,
|
||||||
|
}
|
||||||
|
RearrangeEquation.construct(
|
||||||
|
self, start_terms, end_terms, index_map
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
from scene import *
|
from scene import *
|
||||||
from sub_scenes import *
|
from sub_scenes import *
|
||||||
|
from arithmetic_scenes import *
|
93
scene/arithmetic_scenes.py
Normal file
93
scene/arithmetic_scenes.py
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
import numpy as np
|
||||||
|
import itertools as it
|
||||||
|
|
||||||
|
from scene import Scene
|
||||||
|
from graphs import *
|
||||||
|
|
||||||
|
from mobject import *
|
||||||
|
from animation import *
|
||||||
|
from region import *
|
||||||
|
from constants import *
|
||||||
|
from helpers import *
|
||||||
|
|
||||||
|
class RearrangeEquation(Scene):
|
||||||
|
def construct(
|
||||||
|
self,
|
||||||
|
start_terms,
|
||||||
|
end_terms,
|
||||||
|
index_map,
|
||||||
|
size = None,
|
||||||
|
path = counterclockwise_path,
|
||||||
|
start_transform = None,
|
||||||
|
end_transform = None,
|
||||||
|
leave_start_terms = False,
|
||||||
|
transform_kwargs = {},
|
||||||
|
):
|
||||||
|
transform_kwargs["interpolation_function"] = path
|
||||||
|
start_mobs, end_mobs = self.get_mobs_from_terms(
|
||||||
|
start_terms, end_terms, size
|
||||||
|
)
|
||||||
|
if start_transform:
|
||||||
|
start_mobs = start_transform(CompoundMobject(*start_mobs)).split()
|
||||||
|
if end_transform:
|
||||||
|
end_mobs = end_transform(CompoundMobject(*end_mobs)).split()
|
||||||
|
unmatched_start_indices = set(range(len(start_mobs)))
|
||||||
|
unmatched_end_indices = set(range(len(end_mobs)))
|
||||||
|
unmatched_start_indices.difference_update(
|
||||||
|
[n%len(start_mobs) for n in index_map]
|
||||||
|
)
|
||||||
|
unmatched_end_indices.difference_update(
|
||||||
|
[n%len(end_mobs) for n in index_map.values()]
|
||||||
|
)
|
||||||
|
mobject_pairs = [
|
||||||
|
(start_mobs[a], end_mobs[b])
|
||||||
|
for a, b in index_map.iteritems()
|
||||||
|
]+ [
|
||||||
|
(Point(end_mobs[b].get_center()), end_mobs[b])
|
||||||
|
for b in unmatched_end_indices
|
||||||
|
]
|
||||||
|
if not leave_start_terms:
|
||||||
|
mobject_pairs += [
|
||||||
|
(start_mobs[a], Point(start_mobs[a].get_center()))
|
||||||
|
for a in unmatched_start_indices
|
||||||
|
]
|
||||||
|
|
||||||
|
self.add(*start_mobs)
|
||||||
|
if leave_start_terms:
|
||||||
|
self.add(CompoundMobject(*start_mobs))
|
||||||
|
self.dither()
|
||||||
|
self.animate(*[
|
||||||
|
Transform(*pair, **transform_kwargs)
|
||||||
|
for pair in mobject_pairs
|
||||||
|
])
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
|
||||||
|
def get_mobs_from_terms(self, start_terms, end_terms, size):
|
||||||
|
"""
|
||||||
|
Need to ensure that all image mobjects for a tex expression
|
||||||
|
stemming from the same string are point-for-point copies of one
|
||||||
|
and other. This makes transitions much smoother, and not look
|
||||||
|
like point-clouds.
|
||||||
|
"""
|
||||||
|
num_start_terms = len(start_terms)
|
||||||
|
all_mobs = np.array(
|
||||||
|
tex_mobject(start_terms, size = size).split() + \
|
||||||
|
tex_mobject(end_terms, size = size).split()
|
||||||
|
)
|
||||||
|
all_terms = np.array(start_terms+end_terms)
|
||||||
|
for term in set(all_terms):
|
||||||
|
matches = all_terms == term
|
||||||
|
if sum(matches) > 1:
|
||||||
|
base_mob = all_mobs[list(all_terms).index(term)]
|
||||||
|
all_mobs[matches] = [
|
||||||
|
deepcopy(base_mob).replace(target_mob)
|
||||||
|
for target_mob in all_mobs[matches]
|
||||||
|
]
|
||||||
|
return all_mobs[:num_start_terms], all_mobs[num_start_terms:]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -247,7 +247,7 @@ class GraphScene(Scene):
|
||||||
mobject.center()
|
mobject.center()
|
||||||
diameter = max(mobject.get_height(), mobject.get_width())
|
diameter = max(mobject.get_height(), mobject.get_width())
|
||||||
self.animate(*[
|
self.animate(*[
|
||||||
SemiCircleTransform(
|
CounterclockwiseTransform(
|
||||||
vertex,
|
vertex,
|
||||||
deepcopy(mobject).shift(vertex.get_center())
|
deepcopy(mobject).shift(vertex.get_center())
|
||||||
)
|
)
|
||||||
|
|
|
@ -213,7 +213,6 @@ class Scene(object):
|
||||||
self.number = num_mob
|
self.number = num_mob
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
def get_frame(self):
|
def get_frame(self):
|
||||||
frame = self.background
|
frame = self.background
|
||||||
for mob in self.mobjects:
|
for mob in self.mobjects:
|
||||||
|
@ -224,6 +223,10 @@ class Scene(object):
|
||||||
self.frames += [self.get_frame()]*int(duration / self.frame_duration)
|
self.frames += [self.get_frame()]*int(duration / self.frame_duration)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def repeat(self, num):
|
||||||
|
self.frames = self.frames*num
|
||||||
|
return self
|
||||||
|
|
||||||
def write_to_gif(self, name = None,
|
def write_to_gif(self, name = None,
|
||||||
end_dither_time = DEFAULT_DITHER_TIME):
|
end_dither_time = DEFAULT_DITHER_TIME):
|
||||||
self.dither(end_dither_time)
|
self.dither(end_dither_time)
|
||||||
|
|
|
@ -165,7 +165,7 @@ class OldIntroduceGraphs(GraphScene):
|
||||||
friends = text_mobject("Friends").scale(EDGE_ANNOTATION_SCALE_VAL)
|
friends = text_mobject("Friends").scale(EDGE_ANNOTATION_SCALE_VAL)
|
||||||
self.annotate_edges(friends.shift((0, friends.get_height()/2, 0)))
|
self.annotate_edges(friends.shift((0, friends.get_height()/2, 0)))
|
||||||
self.animate(*[
|
self.animate(*[
|
||||||
SemiCircleTransform(vertex, Dot(point))
|
CounterclockwiseTransform(vertex, Dot(point))
|
||||||
for vertex, point in zip(self.vertices, self.points)
|
for vertex, point in zip(self.vertices, self.points)
|
||||||
]+[
|
]+[
|
||||||
Transform(ann, line)
|
Transform(ann, line)
|
||||||
|
@ -558,7 +558,7 @@ class FacebookGraph(GraphScene):
|
||||||
self.annotate_edges(friends)
|
self.annotate_edges(friends)
|
||||||
self.dither()
|
self.dither()
|
||||||
self.animate(*[
|
self.animate(*[
|
||||||
SemiCircleTransform(account, vertex)
|
CounterclockwiseTransform(account, vertex)
|
||||||
for account, vertex in zip(accounts, self.vertices)
|
for account, vertex in zip(accounts, self.vertices)
|
||||||
])
|
])
|
||||||
self.dither()
|
self.dither()
|
||||||
|
@ -1176,7 +1176,7 @@ class FinalSum(Scene):
|
||||||
copy = plus if index == -2 else deepcopy(mob)
|
copy = plus if index == -2 else deepcopy(mob)
|
||||||
copy.center().shift(lines[index].get_center())
|
copy.center().shift(lines[index].get_center())
|
||||||
copy.scale_in_place(lines[index].get_width()/mob.get_width())
|
copy.scale_in_place(lines[index].get_width()/mob.get_width())
|
||||||
anims.append(SemiCircleTransform(copy, mob))
|
anims.append(CounterclockwiseTransform(copy, mob))
|
||||||
self.clear()
|
self.clear()
|
||||||
self.animate(*anims, run_time = 2.0)
|
self.animate(*anims, run_time = 2.0)
|
||||||
self.dither()
|
self.dither()
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -226,7 +226,7 @@ class HardProblemsSimplerQuestions(Scene):
|
||||||
self.remove(*self.mobjects)
|
self.remove(*self.mobjects)
|
||||||
self.add(fermat["n"])
|
self.add(fermat["n"])
|
||||||
self.animate(*[
|
self.animate(*[
|
||||||
SemiCircleTransform(mobs[0], mobs[1])
|
CounterclockwiseTransform(mobs[0], mobs[1])
|
||||||
for f_copy, sym in zip(copies, ["3", "2"])
|
for f_copy, sym in zip(copies, ["3", "2"])
|
||||||
for mobs in zip(f_copy.split(), fermat[sym].split())
|
for mobs in zip(f_copy.split(), fermat[sym].split())
|
||||||
])
|
])
|
||||||
|
@ -260,7 +260,7 @@ class HardProblemsSimplerQuestions(Scene):
|
||||||
self.animate(
|
self.animate(
|
||||||
FadeIn(fermat2_jargon),
|
FadeIn(fermat2_jargon),
|
||||||
FadeIn(fermat3_jargon),
|
FadeIn(fermat3_jargon),
|
||||||
SemiCircleTransform(start_line, end_line),
|
CounterclockwiseTransform(start_line, end_line),
|
||||||
ShowCreation(dots)
|
ShowCreation(dots)
|
||||||
)
|
)
|
||||||
self.dither()
|
self.dither()
|
||||||
|
@ -686,17 +686,17 @@ class GraphsAndEulersFormulaJoke(Scene):
|
||||||
self.add(axes)
|
self.add(axes)
|
||||||
self.animate(ShowCreation(graph), run_time = 1.0)
|
self.animate(ShowCreation(graph), run_time = 1.0)
|
||||||
eulers = tex_mobject("e^{\pi i} = -1").shift((0, 3, 0))
|
eulers = tex_mobject("e^{\pi i} = -1").shift((0, 3, 0))
|
||||||
self.animate(SemiCircleTransform(
|
self.animate(CounterclockwiseTransform(
|
||||||
deepcopy(graph), eulers
|
deepcopy(graph), eulers
|
||||||
))
|
))
|
||||||
self.dither()
|
self.dither()
|
||||||
self.remove(*self.mobjects)
|
self.remove(*self.mobjects)
|
||||||
self.add(eulers)
|
self.add(eulers)
|
||||||
self.animate(SemiCircleTransform(
|
self.animate(CounterclockwiseTransform(
|
||||||
CompoundMobject(axes, graph),
|
CompoundMobject(axes, graph),
|
||||||
Point((-SPACE_WIDTH, SPACE_HEIGHT, 0))
|
Point((-SPACE_WIDTH, SPACE_HEIGHT, 0))
|
||||||
))
|
))
|
||||||
self.animate(SemiCircleTransform(
|
self.animate(CounterclockwiseTransform(
|
||||||
eulers,
|
eulers,
|
||||||
Point((SPACE_WIDTH, SPACE_HEIGHT, 0))
|
Point((SPACE_WIDTH, SPACE_HEIGHT, 0))
|
||||||
))
|
))
|
||||||
|
@ -899,7 +899,7 @@ class ShowMoserGraphLines(CircleScene):
|
||||||
compound = CompoundMobject(*mobs)
|
compound = CompoundMobject(*mobs)
|
||||||
if mobs in (self.dots, self.intersection_dots):
|
if mobs in (self.dots, self.intersection_dots):
|
||||||
self.remove(*mobs)
|
self.remove(*mobs)
|
||||||
self.animate(SemiCircleTransform(
|
self.animate(CounterclockwiseTransform(
|
||||||
compound,
|
compound,
|
||||||
deepcopy(compound).scale(1.05),
|
deepcopy(compound).scale(1.05),
|
||||||
alpha_func = there_and_back,
|
alpha_func = there_and_back,
|
||||||
|
@ -1055,7 +1055,7 @@ class ApplyEulerToMoser(CircleScene):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.animate(*[
|
self.animate(*[
|
||||||
SemiCircleTransform(d[1], d[2], run_time = 2.0)
|
CounterclockwiseTransform(d[1], d[2], run_time = 2.0)
|
||||||
for d in [V, minus, E, plus, F, equals, two]
|
for d in [V, minus, E, plus, F, equals, two]
|
||||||
])
|
])
|
||||||
self.dither()
|
self.dither()
|
||||||
|
@ -1164,8 +1164,8 @@ class ApplyEulerToMoser(CircleScene):
|
||||||
self.dither()
|
self.dither()
|
||||||
self.remove(*self.mobjects)
|
self.remove(*self.mobjects)
|
||||||
self.animate(
|
self.animate(
|
||||||
SemiCircleTransform(two[6], two[7]),
|
CounterclockwiseTransform(two[6], two[7]),
|
||||||
SemiCircleTransform(plus[6], plus[7]),
|
CounterclockwiseTransform(plus[6], plus[7]),
|
||||||
*[
|
*[
|
||||||
Transform(d[6], d[7])
|
Transform(d[6], d[7])
|
||||||
for d in [F, equals, nc2, plus1, nc4]
|
for d in [F, equals, nc2, plus1, nc4]
|
||||||
|
@ -1184,7 +1184,7 @@ class ApplyEulerToMoser(CircleScene):
|
||||||
one = tex_mobject("1").shift(two.get_center())
|
one = tex_mobject("1").shift(two.get_center())
|
||||||
two.highlight("red")
|
two.highlight("red")
|
||||||
self.add(two)
|
self.add(two)
|
||||||
self.animate(SemiCircleTransform(two, one))
|
self.animate(CounterclockwiseTransform(two, one))
|
||||||
|
|
||||||
class FormulaRelatesToPowersOfTwo(Scene):
|
class FormulaRelatesToPowersOfTwo(Scene):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -1226,7 +1226,7 @@ class FormulaRelatesToPowersOfTwo(Scene):
|
||||||
self.remove(*self.mobjects)
|
self.remove(*self.mobjects)
|
||||||
self.add(*forms + sums + results)
|
self.add(*forms + sums + results)
|
||||||
self.animate(*[
|
self.animate(*[
|
||||||
SemiCircleTransform(result, pof2)
|
CounterclockwiseTransform(result, pof2)
|
||||||
for result, pof2 in zip(results, powers_of_two)
|
for result, pof2 in zip(results, powers_of_two)
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -1277,7 +1277,7 @@ class PascalsTriangleWithNChooseK(PascalsTriangleScene):
|
||||||
self.dither()
|
self.dither()
|
||||||
self.remove(*self.mobjects)
|
self.remove(*self.mobjects)
|
||||||
self.animate(*[
|
self.animate(*[
|
||||||
SemiCircleTransform(
|
CounterclockwiseTransform(
|
||||||
deepcopy(mob_dicts[i][n][k]),
|
deepcopy(mob_dicts[i][n][k]),
|
||||||
mob_dicts[1-i][n][k]
|
mob_dicts[1-i][n][k]
|
||||||
)
|
)
|
||||||
|
@ -1399,7 +1399,7 @@ class PascalsTriangleSumRows(PascalsTriangleScene):
|
||||||
self.remove(*to_remove)
|
self.remove(*to_remove)
|
||||||
self.add(*powers_of_two)
|
self.add(*powers_of_two)
|
||||||
for n in range(self.nrows):
|
for n in range(self.nrows):
|
||||||
self.animate(SemiCircleTransform(
|
self.animate(CounterclockwiseTransform(
|
||||||
powers_of_two[n], powers_of_two_symbols[n],
|
powers_of_two[n], powers_of_two_symbols[n],
|
||||||
run_time = run_time
|
run_time = run_time
|
||||||
))
|
))
|
||||||
|
@ -1456,14 +1456,14 @@ class MoserSolutionInPascal(PascalsTriangleScene):
|
||||||
])
|
])
|
||||||
self.animate(*
|
self.animate(*
|
||||||
[
|
[
|
||||||
SemiCircleTransform(
|
CounterclockwiseTransform(
|
||||||
self.coords_to_n_choose_k[n0][k0],
|
self.coords_to_n_choose_k[n0][k0],
|
||||||
self.coords_to_mobs[n0][k0]
|
self.coords_to_mobs[n0][k0]
|
||||||
)
|
)
|
||||||
for n0, k0 in self.coords
|
for n0, k0 in self.coords
|
||||||
if (n0, k0) not in [(n, k) for k in term_range]
|
if (n0, k0) not in [(n, k) for k in term_range]
|
||||||
]+[
|
]+[
|
||||||
SemiCircleTransform(terms[k], target_terms[k])
|
CounterclockwiseTransform(terms[k], target_terms[k])
|
||||||
for k in term_range
|
for k in term_range
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -1565,7 +1565,7 @@ class ExplainNChoose2Formula(Scene):
|
||||||
b_mob = nums.pop(b-2 if a < b else b-1)
|
b_mob = nums.pop(b-2 if a < b else b-1)
|
||||||
self.add(b_mob)
|
self.add(b_mob)
|
||||||
self.animate(*[
|
self.animate(*[
|
||||||
SemiCircleTransform(
|
CounterclockwiseTransform(
|
||||||
mob,
|
mob,
|
||||||
Point(mob.get_center()).highlight("black")
|
Point(mob.get_center()).highlight("black")
|
||||||
)
|
)
|
||||||
|
@ -1582,8 +1582,8 @@ class ExplainNChoose2Formula(Scene):
|
||||||
b_copy = deepcopy(b_mob).center().shift(a_center - (0, 2, 0))
|
b_copy = deepcopy(b_mob).center().shift(a_center - (0, 2, 0))
|
||||||
self.add(over_2, deepcopy(a_mob), deepcopy(b_mob))
|
self.add(over_2, deepcopy(a_mob), deepcopy(b_mob))
|
||||||
self.animate(
|
self.animate(
|
||||||
SemiCircleTransform(a_mob, a_copy),
|
CounterclockwiseTransform(a_mob, a_copy),
|
||||||
SemiCircleTransform(b_mob, b_copy),
|
CounterclockwiseTransform(b_mob, b_copy),
|
||||||
FadeIn(parens_copy),
|
FadeIn(parens_copy),
|
||||||
FadeIn(text_mobject("is considered the same as"))
|
FadeIn(text_mobject("is considered the same as"))
|
||||||
)
|
)
|
||||||
|
@ -1635,7 +1635,7 @@ class ExplainNChoose4Formula(Scene):
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.animate(*[
|
self.animate(*[
|
||||||
SemiCircleTransform(
|
CounterclockwiseTransform(
|
||||||
mob,
|
mob,
|
||||||
Point(mob.get_center()).highlight("black")
|
Point(mob.get_center()).highlight("black")
|
||||||
)
|
)
|
||||||
|
@ -1658,7 +1658,7 @@ class ExplainNChoose4Formula(Scene):
|
||||||
for i in range(4)
|
for i in range(4)
|
||||||
]
|
]
|
||||||
compound_quad = CompoundMobject(*quad_mobs)
|
compound_quad = CompoundMobject(*quad_mobs)
|
||||||
self.animate(SemiCircleTransform(
|
self.animate(CounterclockwiseTransform(
|
||||||
compound_quad,
|
compound_quad,
|
||||||
CompoundMobject(*new_quad_mobs)
|
CompoundMobject(*new_quad_mobs)
|
||||||
))
|
))
|
||||||
|
|
|
@ -203,12 +203,12 @@ class TauPoem(Scene):
|
||||||
)
|
)
|
||||||
self.add(two_pi)
|
self.add(two_pi)
|
||||||
self.dither()
|
self.dither()
|
||||||
self.animate(SemiCircleTransform(
|
self.animate(CounterclockwiseTransform(
|
||||||
two_pi, sphere,
|
two_pi, sphere,
|
||||||
alpha_func = lambda t : 2*high_inflection_0_to_1(t/2)
|
alpha_func = lambda t : 2*high_inflection_0_to_1(t/2)
|
||||||
))
|
))
|
||||||
self.remove(two_pi)
|
self.remove(two_pi)
|
||||||
self.animate(SemiCircleTransform(
|
self.animate(CounterclockwiseTransform(
|
||||||
sphere, tau,
|
sphere, tau,
|
||||||
alpha_func = lambda t : 2*(high_inflection_0_to_1(t/2+0.5)-0.5)
|
alpha_func = lambda t : 2*(high_inflection_0_to_1(t/2+0.5)-0.5)
|
||||||
))
|
))
|
||||||
|
@ -302,10 +302,10 @@ class TauPoem(Scene):
|
||||||
self.dither()
|
self.dither()
|
||||||
self.remove(bubble)
|
self.remove(bubble)
|
||||||
bubble_copy = deepcopy(bubble)
|
bubble_copy = deepcopy(bubble)
|
||||||
self.animate(SemiCircleTransform(bubble_copy, heart))
|
self.animate(CounterclockwiseTransform(bubble_copy, heart))
|
||||||
self.dither()
|
self.dither()
|
||||||
self.remove(bubble_copy)
|
self.remove(bubble_copy)
|
||||||
self.animate(SemiCircleTransform(heart, bubble))
|
self.animate(CounterclockwiseTransform(heart, bubble))
|
||||||
self.dither()
|
self.dither()
|
||||||
|
|
||||||
|
|
||||||
|
@ -323,7 +323,7 @@ class TauPoem(Scene):
|
||||||
Point(pi.right_leg.points[0,:]).highlight("black")
|
Point(pi.right_leg.points[0,:]).highlight("black")
|
||||||
),
|
),
|
||||||
Transform(pi.mouth, tau.mouth),
|
Transform(pi.mouth, tau.mouth),
|
||||||
SemiCircleTransform(
|
CounterclockwiseTransform(
|
||||||
two,
|
two,
|
||||||
Dot(two.get_center()).highlight("black")
|
Dot(two.get_center()).highlight("black")
|
||||||
)
|
)
|
||||||
|
@ -412,8 +412,8 @@ class TauPoem(Scene):
|
||||||
ShowCreation(circle)
|
ShowCreation(circle)
|
||||||
)
|
)
|
||||||
self.animate(
|
self.animate(
|
||||||
SemiCircleTransform(sine_period, tau_line),
|
CounterclockwiseTransform(sine_period, tau_line),
|
||||||
SemiCircleTransform(circle, deepcopy(tau_line)),
|
CounterclockwiseTransform(circle, deepcopy(tau_line)),
|
||||||
FadeOut(axes),
|
FadeOut(axes),
|
||||||
FadeOut(grid),
|
FadeOut(grid),
|
||||||
FadeOut(sine),
|
FadeOut(sine),
|
||||||
|
@ -435,7 +435,7 @@ class TauPoem(Scene):
|
||||||
|
|
||||||
self.add(*formula)
|
self.add(*formula)
|
||||||
self.dither()
|
self.dither()
|
||||||
self.animate(SemiCircleTransform(two_pi, tau))
|
self.animate(CounterclockwiseTransform(two_pi, tau))
|
||||||
self.remove(two_pi)
|
self.remove(two_pi)
|
||||||
self.animate(BlinkPiCreature(tau))
|
self.animate(BlinkPiCreature(tau))
|
||||||
self.dither()
|
self.dither()
|
||||||
|
|
Loading…
Add table
Reference in a new issue