Beginning transform KA article, many small fixes to the mobject refactor

This commit is contained in:
Grant Sanderson
2015-11-02 19:09:55 -08:00
parent d294d0f951
commit ac930952f1
17 changed files with 310 additions and 55 deletions

View File

@ -4,7 +4,6 @@ import numpy as np
import warnings import warnings
import time import time
import os import os
import copy
import progressbar import progressbar
import inspect import inspect
@ -21,7 +20,7 @@ class Animation(object):
mobject = instantiate(mobject) mobject = instantiate(mobject)
assert(isinstance(mobject, Mobject)) assert(isinstance(mobject, Mobject))
digest_config(self, kwargs, locals()) digest_config(self, kwargs, locals())
self.starting_mobject = copy.deepcopy(self.mobject) self.starting_mobject = self.mobject.copy()
if self.alpha_func is None: if self.alpha_func is None:
self.alpha_func = (lambda x : x) self.alpha_func = (lambda x : x)
if self.name is None: if self.name is None:

View File

@ -1,6 +1,5 @@
import numpy as np import numpy as np
import itertools as it import itertools as it
from copy import deepcopy
from helpers import * from helpers import *
from animation import Animation from animation import Animation

View File

@ -1,6 +1,5 @@
import numpy as np import numpy as np
import itertools as it import itertools as it
from copy import deepcopy
from helpers import * from helpers import *

View File

@ -46,6 +46,7 @@ class Transform(Animation):
[self.mobject, self.starting_mobject, self.ending_mobject] [self.mobject, self.starting_mobject, self.ending_mobject]
) )
for m, start, end in zip(*families): for m, start, end in zip(*families):
# print m, start, end
m.points = self.interpolation_function( m.points = self.interpolation_function(
start.points, end.points, alpha start.points, end.points, alpha
) )
@ -129,7 +130,7 @@ class ApplyFunction(Transform):
Transform.__init__( Transform.__init__(
self, self,
mobject, mobject,
function(copy.deepcopy(mobject)), function(mobject.copy()),
**kwargs **kwargs
) )
self.name = "ApplyFunctionTo"+str(mobject) self.name = "ApplyFunctionTo"+str(mobject)

View File

@ -103,7 +103,7 @@ def handle_scene(scene, **config):
scene.show_frame() scene.show_frame()
path = os.path.join(MOVIE_DIR, config["movie_prefix"], "images") path = os.path.join(MOVIE_DIR, config["movie_prefix"], "images")
if not os.path.exists(path): if not os.path.exists(path):
os.mkdir(path) os.makedirs(path)
scene.save_image(path, name) scene.save_image(path, name)
if config["write"]: if config["write"]:
scene.write_to_movie(os.path.join(config["movie_prefix"], name)) scene.write_to_movie(os.path.join(config["movie_prefix"], name))
@ -156,10 +156,10 @@ def get_scene_args(SceneClass, config):
index = preset_extensions.index(config["args_extension"]) index = preset_extensions.index(config["args_extension"])
return [args_list[index]] return [args_list[index]]
if config["args_extension"] == "" : if config["args_extension"] == "" :
if len(args_list) == 1:
return args_list
name_to_args = dict(zip(preset_extensions, args_list)) name_to_args = dict(zip(preset_extensions, args_list))
return prompt_user_for_choice(name_to_args) return prompt_user_for_choice(name_to_args)
if len(args_list) == 1:
return args_list
return [SceneClass.string_to_args(config["args_extension"])] return [SceneClass.string_to_args(config["args_extension"])]
def get_scene_classes(scene_names_to_classes, config): def get_scene_classes(scene_names_to_classes, config):

View File

@ -104,6 +104,8 @@ class Mobject(object):
os.path.join(MOVIE_DIR, (name or str(self)) + ".png") os.path.join(MOVIE_DIR, (name or str(self)) + ".png")
) )
def copy(self):
return deepcopy(self)
#### Fundamental operations ###### #### Fundamental operations ######
@ -226,7 +228,7 @@ class Mobject(object):
""" """
target_point = np.sign(direction) * (SPACE_WIDTH, SPACE_HEIGHT, 0) target_point = np.sign(direction) * (SPACE_WIDTH, SPACE_HEIGHT, 0)
anchor_point = self.get_critical_point(direction) anchor_point = self.get_critical_point(direction)
self.shift(target - anchor_point - buff * np.array(direction)) self.shift(target_point - anchor_point - buff * np.array(direction))
return self return self
def to_corner(self, corner = LEFT+DOWN, buff = DEFAULT_MOBJECT_TO_EDGE_BUFFER): def to_corner(self, corner = LEFT+DOWN, buff = DEFAULT_MOBJECT_TO_EDGE_BUFFER):
@ -437,16 +439,7 @@ class Mobject(object):
else: else:
larger, smaller = mobject1, mobject2 larger, smaller = mobject1, mobject2
for sub_mob in larger.sub_mobjects[-diff:]: for sub_mob in larger.sub_mobjects[-diff:]:
center = sub_mob.get_center() smaller.add(Point(sub_mob.get_center()))
point_distances = np.apply_along_axis(
lambda p : np.linalg.norm(p - center),
1, larger.points
)
index = np.argmin(point_distances)
smaller.add(Point(
smaller.points[index],
color = Color(rgb = smaller.rgbs[index])
))
for m1, m2 in zip(mobject1.sub_mobjects, mobject2.sub_mobjects): for m1, m2 in zip(mobject1.sub_mobjects, mobject2.sub_mobjects):
Mobject.align_data(m1, m2) Mobject.align_data(m1, m2)

View File

@ -205,7 +205,7 @@ class DrawComplexAngleAndMagnitude(Scene):
label.shift(point - edge + buff*dot_to_label_dir) label.shift(point - edge + buff*dot_to_label_dir)
label.highlight(YELLOW) label.highlight(YELLOW)
self.add_local_mobjects() self.add_mobjects_among(locals().values())
def add_angle_label(self, number): def add_angle_label(self, number):
@ -214,7 +214,7 @@ class DrawComplexAngleAndMagnitude(Scene):
radius = 0.2 radius = 0.2
) )
self.add_local_mobjects() self.add_mobjects_among(locals().values())
def add_lines(self, tex_representation, number): def add_lines(self, tex_representation, number):
point = self.plane.number_to_point(number) point = self.plane.number_to_point(number)
@ -250,5 +250,5 @@ class DrawComplexAngleAndMagnitude(Scene):
axis = OUT if point[1] > 0 else IN axis = OUT if point[1] > 0 else IN
norm_label.next_to(brace, rotate_vector(point, np.pi/2, axis)) norm_label.next_to(brace, rotate_vector(point, np.pi/2, axis))
self.add_local_mobjects() self.add_mobjects_among(locals().values())

View File

@ -165,7 +165,7 @@ class IndicateTroublePointFromParallelLines(AddParallelLines):
vect = DOWN+RIGHT vect = DOWN+RIGHT
arrow = Arrow(circle.get_center()+2*vect, circle.get_boundary_point(vect)) arrow = Arrow(circle.get_center()+2*vect, circle.get_boundary_point(vect))
arrow.highlight(circle.get_color()) arrow.highlight(circle.get_color())
self.add_local_mobjects() self.add_mobjects_among(locals().values())
class DrawAllThreeSquaresWithMoreTriangles(DrawAllThreeSquares): class DrawAllThreeSquaresWithMoreTriangles(DrawAllThreeSquares):
@ -224,7 +224,7 @@ class IndicateBigRectangleTroublePoint(DrawAllThreeSquaresWithMoreTriangles):
circle.shift(4*RIGHT) circle.shift(4*RIGHT)
vect = DOWN+RIGHT vect = DOWN+RIGHT
arrow = Arrow(circle.get_center()+vect, circle.get_boundary_point(vect)) arrow = Arrow(circle.get_center()+vect, circle.get_boundary_point(vect))
self.add_local_mobjects() self.add_mobjects_among(locals().values())
class ShowBigRectangleDimensions(DrawAllThreeSquaresWithMoreTriangles): class ShowBigRectangleDimensions(DrawAllThreeSquaresWithMoreTriangles):
args_list = [(10, False)] args_list = [(10, False)]
@ -239,7 +239,7 @@ class ShowBigRectangleDimensions(DrawAllThreeSquaresWithMoreTriangles):
b_plus_2a = TexMobject("b+2a").scale(TEX_MOB_SCALE_VAL) b_plus_2a = TexMobject("b+2a").scale(TEX_MOB_SCALE_VAL)
a_plus_2b.next_to(u_brace, DOWN) a_plus_2b.next_to(u_brace, DOWN)
b_plus_2a.next_to(side_brace, LEFT) b_plus_2a.next_to(side_brace, LEFT)
self.add_local_mobjects() self.add_mobjects_among(locals().values())
class FillInAreaOfBigRectangle(DrawAllThreeSquaresWithMoreTriangles): class FillInAreaOfBigRectangle(DrawAllThreeSquaresWithMoreTriangles):
args_list = [(10, False)] args_list = [(10, False)]
@ -267,7 +267,7 @@ class DrawOnlyABSquares(Scene):
symobl.shift(mob.get_center()) symobl.shift(mob.get_center())
self.add(symobl) self.add(symobl)
triangle = Triangle() triangle = Triangle()
self.add_local_mobjects() self.add_mobjects_among(locals().values())
class AddTriangleCopyToABSquares(DrawOnlyABSquares): class AddTriangleCopyToABSquares(DrawOnlyABSquares):
def construct(self): def construct(self):
@ -388,7 +388,7 @@ class ZoomInOnTroublePoint(Scene):
angle1_arc.filter_out(lambda (x, y, z) : not (x > 0 and y > 0 and y < x/3)) angle1_arc.filter_out(lambda (x, y, z) : not (x > 0 and y > 0 and y < x/3))
angle2_arc.filter_out(lambda (x, y, z) : not (x < 0 and y > 0 and y < -3*x)) angle2_arc.filter_out(lambda (x, y, z) : not (x < 0 and y > 0 and y < -3*x))
self.add_local_mobjects() self.add_mobjects_among(locals().values())
self.add_elbow() self.add_elbow()
if rotate: if rotate:
for mob in self.mobjects: for mob in self.mobjects:

View File

@ -76,19 +76,20 @@ class Scene(object):
self.mobjects = old_mobjects + list(mobjects) self.mobjects = old_mobjects + list(mobjects)
return self return self
def add_local_mobjects(self): def add_mobjects_among(self, values):
""" """
So a scene can just add all mobjects it's defined up to that point So a scene can just add all mobjects it's defined up to that point
by calling add_mobjects_among(locals().values())
""" """
name_to_mob = get_caller_locals(lambda x : isinstance(x, Mobject)) mobjects = filter(lambda x : isinstance(x, Mobject), values)
self.add(*name_to_mob.values()) self.add(*mobjects)
return self return self
def remove(self, *mobjects): def remove(self, *mobjects):
if not all_elements_are_instances(mobjects, Mobject): if not all_elements_are_instances(mobjects, Mobject):
raise Exception("Removing something which is not a mobject") raise Exception("Removing something which is not a mobject")
mobjects = filter(lambda m : m in self.mobjects, mobjects) mobjects = filter(lambda m : m in self.mobjects, mobjects)
if len(mobjects): if len(mobjects) == 0:
return return
self.mobjects = filter(lambda m : m not in mobjects, self.mobjects) self.mobjects = filter(lambda m : m not in mobjects, self.mobjects)
self.repaint_mojects() self.repaint_mojects()
@ -158,7 +159,11 @@ class Scene(object):
run_time = animations[0].run_time run_time = animations[0].run_time
for animation in animations: for animation in animations:
animation.set_run_time(run_time) animation.set_run_time(run_time)
moving_mobjects = [anim.mobject for anim in animations] moving_mobjects = [
mobject
for anim in animations
for mobject in anim.mobject.get_full_submobject_family()
]
bundle = Mobject(*self.mobjects) bundle = Mobject(*self.mobjects)
static_mobjects = filter( static_mobjects = filter(

View File

@ -77,7 +77,7 @@ class RearrangeEquation(Scene):
if sum(matches) > 1: if sum(matches) > 1:
base_mob = all_mobs[list(all_terms).index(term)] base_mob = all_mobs[list(all_terms).index(term)]
all_mobs[matches] = [ all_mobs[matches] = [
deepcopy(base_mob).replace(target_mob) base_mob.copy().replace(target_mob)
for target_mob in all_mobs[matches] for target_mob in all_mobs[matches]
] ]
return all_mobs[:num_start_terms], all_mobs[num_start_terms:] return all_mobs[:num_start_terms], all_mobs[num_start_terms:]

View File

@ -1,5 +1,3 @@
from copy import deepcopy
from helpers import * from helpers import *
from mobject import Mobject, Mobject, ImageMobject, TexMobject from mobject import Mobject, Mobject, ImageMobject, TexMobject
@ -43,7 +41,7 @@ class PiCreature(Mobject):
mouth_center = self.get_mouth_center() mouth_center = self.get_mouth_center()
self.mouth.center() self.mouth.center()
self.smile = self.mouth self.smile = self.mouth
self.frown = deepcopy(self.mouth).rotate(np.pi, RIGHT) self.frown = self.mouth.copy().rotate(np.pi, RIGHT)
self.straight_mouth = TexMobject("-").scale(0.5) self.straight_mouth = TexMobject("-").scale(0.5)
for mouth in self.smile, self.frown, self.straight_mouth: for mouth in self.smile, self.frown, self.straight_mouth:
mouth.sort_points(lambda p : p[0]) mouth.sort_points(lambda p : p[0])
@ -118,7 +116,7 @@ class PiCreature(Mobject):
def get_step_intermediate(self, pi_creature): def get_step_intermediate(self, pi_creature):
vect = pi_creature.get_center() - self.get_center() vect = pi_creature.get_center() - self.get_center()
result = deepcopy(self).shift(vect / 2.0) result = self.copy().shift(vect / 2.0)
left_forward = vect[0] > 0 left_forward = vect[0] > 0
if self.right_leg.get_center()[0] < self.left_leg.get_center()[0]: if self.right_leg.get_center()[0] < self.left_leg.get_center()[0]:
#For Mortimer's case #For Mortimer's case

View File

@ -113,7 +113,7 @@ class PascalsTriangleScene(Scene):
center = self.coords_to_center(n, k) center = self.coords_to_center(n, k)
if num not in num_to_num_mob: if num not in num_to_num_mob:
num_to_num_mob[num] = TexMobject(str(num)) num_to_num_mob[num] = TexMobject(str(num))
num_mob = deepcopy(num_to_num_mob[num]) num_mob = num_to_num_mob[num].copy()
scale_factor = min( scale_factor = min(
1, 1,
self.portion_to_fill * self.cell_height / num_mob.get_height(), self.portion_to_fill * self.cell_height / num_mob.get_height(),
@ -154,7 +154,7 @@ class PascalsTriangleScene(Scene):
for a in range((self.nrows - n)/2 + 1): for a in range((self.nrows - n)/2 + 1):
for k in (n + a + 1, -a -1): for k in (n + a + 1, -a -1):
self.coords.append((n, k)) self.coords.append((n, k))
mob = deepcopy(zero) mob = zero.copy()
mob.shift(self.coords_to_center(n, k)) mob.shift(self.coords_to_center(n, k))
self.coords_to_mobs[n][k] = mob self.coords_to_mobs[n][k] = mob
self.add(mob) self.add(mob)

View File

@ -21,7 +21,7 @@ class ComplexPlane(NumberPlane):
digest_config(self, kwargs) digest_config(self, kwargs)
kwargs.update({ kwargs.update({
"x_unit_to_spatial_width" : self.unit_to_spatial_width, "x_unit_to_spatial_width" : self.unit_to_spatial_width,
"y_uint_to_spatial_height" : self.unit_to_spatial_width, "y_unit_to_spatial_height" : self.unit_to_spatial_width,
"x_line_frequency" : self.line_frequency, "x_line_frequency" : self.line_frequency,
"x_faded_line_frequency" : self.faded_line_frequency, "x_faded_line_frequency" : self.faded_line_frequency,
"y_line_frequency" : self.line_frequency, "y_line_frequency" : self.line_frequency,

View File

@ -53,9 +53,11 @@ class Line(Mobject1D):
for arg in start, end for arg in start, end
] ]
start_to_end = preliminary_end - preliminary_start start_to_end = preliminary_end - preliminary_start
vect = np.zeros(len(start_to_end))
longer_dim = np.argmax(map(abs, start_to_end)) longer_dim = np.argmax(map(abs, start_to_end))
vect[longer_dim] = start_to_end[longer_dim]
self.start, self.end = [ self.start, self.end = [
arg.get_edge_center(unit*start_to_end) arg.get_edge_center(unit*vect)
if isinstance(arg, Mobject) if isinstance(arg, Mobject)
else np.array(arg) else np.array(arg)
for arg, unit in zip([start, end], [1, -1]) for arg, unit in zip([start, end], [1, -1])

View File

@ -246,7 +246,7 @@ class GraphScene(Scene):
self.play(*[ self.play(*[
CounterclockwiseTransform( CounterclockwiseTransform(
vertex, vertex,
deepcopy(mobject).shift(vertex.get_center()) mobject.copy().shift(vertex.get_center())
) )
for vertex in self.vertices for vertex in self.vertices
] + [ ] + [
@ -260,7 +260,7 @@ class GraphScene(Scene):
def annotate_edges(self, mobject, fade_in = True, **kwargs): def annotate_edges(self, mobject, fade_in = True, **kwargs):
angles = map(np.arctan, map(Line.get_slope, self.edges)) angles = map(np.arctan, map(Line.get_slope, self.edges))
self.edge_annotations = [ self.edge_annotations = [
deepcopy(mobject).rotate(angle).shift(edge.get_center()) mobject.copy().rotate(angle).shift(edge.get_center())
for angle, edge in zip(angles, self.edges) for angle, edge in zip(angles, self.edges)
] ]
if fade_in: if fade_in:

View File

@ -18,7 +18,7 @@ class NumberLine(Mobject1D):
def __init__(self, **kwargs): def __init__(self, **kwargs):
digest_config(self, kwargs) digest_config(self, kwargs)
if self.leftmost_tick is None: if self.leftmost_tick is None:
self.leftmost_tick = -int(self.numerical_radius) self.leftmost_tick = -int(self.numerical_radius-self.number_at_center)
self.left_num = self.number_at_center - self.numerical_radius self.left_num = self.number_at_center - self.numerical_radius
self.right_num = self.number_at_center + self.numerical_radius self.right_num = self.number_at_center + self.numerical_radius
Mobject1D.__init__(self, **kwargs) Mobject1D.__init__(self, **kwargs)
@ -66,7 +66,8 @@ class NumberLine(Mobject1D):
) )
def point_to_number(self, point): def point_to_number(self, point):
return self.number_at_center + point[0]/self.unit_length_to_spatial_width new_point = point-self.get_center()
return self.number_at_center + new_point[0]/self.unit_length_to_spatial_width
def default_numbers_to_display(self): def default_numbers_to_display(self):
return self.get_tick_numbers()[::2] return self.get_tick_numbers()[::2]
@ -115,7 +116,7 @@ class NumberPlane(Mobject1D):
"x_radius" : SPACE_WIDTH, "x_radius" : SPACE_WIDTH,
"y_radius" : SPACE_HEIGHT, "y_radius" : SPACE_HEIGHT,
"x_unit_to_spatial_width" : 1, "x_unit_to_spatial_width" : 1,
"y_uint_to_spatial_height" : 1, "y_unit_to_spatial_height" : 1,
"x_line_frequency" : 1, "x_line_frequency" : 1,
"x_faded_line_frequency" : 0.5, "x_faded_line_frequency" : 0.5,
"y_line_frequency" : 1, "y_line_frequency" : 1,
@ -145,7 +146,7 @@ class NumberPlane(Mobject1D):
))) )))
y_vals = np.array(filter(lambda y : y not in y_vals, np.arange( y_vals = np.array(filter(lambda y : y not in y_vals, np.arange(
0, self.y_radius, 0, self.y_radius,
self.y_uint_to_spatial_height*y_freq self.y_unit_to_spatial_height*y_freq
))) )))
x_cont_vals = np.arange( x_cont_vals = np.arange(
0, self.x_radius, 0, self.x_radius,
@ -153,7 +154,7 @@ class NumberPlane(Mobject1D):
) )
y_cont_vals = np.arange( y_cont_vals = np.arange(
0, self.y_radius, 0, self.y_radius,
self.epsilon/self.y_uint_to_spatial_height self.epsilon/self.y_unit_to_spatial_height
) )
for x_sgn, y_sgn in it.product([-1, 1], [-1, 1]): for x_sgn, y_sgn in it.product([-1, 1], [-1, 1]):
self.add_points( self.add_points(
@ -170,11 +171,17 @@ class NumberPlane(Mobject1D):
def num_pair_to_point(self, pair): def num_pair_to_point(self, pair):
pair = pair + self.num_pair_at_center pair = pair + self.num_pair_at_center
return np.array([ result = self.get_center()
pair[0]*self.x_unit_to_spatial_width, result[0] += pair[0]*self.x_unit_to_spatial_width
pair[1]*self.y_uint_to_spatial_height, result[1] += pair[1]*self.y_unit_to_spatial_height
0 return result
])
def point_to_num_pair(self, point):
new_point = point-self.get_center()
center_x, center_y = self.num_pair_at_center
x = center_x + point[0]/self.x_unit_to_spatial_width
y = center_y + point[1]/self.y_unit_to_spatial_height
return x, y
def get_coordinate_labels(self, x_vals = None, y_vals = None): def get_coordinate_labels(self, x_vals = None, y_vals = None):
result = [] result = []
@ -207,6 +214,41 @@ class NumberPlane(Mobject1D):
return arrow return arrow
class XYZAxes(Mobject1D):
DEFAULT_CONFIG = {
"color" : TEAL,
"radius" : SPACE_HEIGHT,
"tick_frequency" : 1,
}
def generate_points(self):
self.x_axis = NumberLine(
numerical_radius = self.radius,
tick_frequency = self.tick_frequency
)
self.y_axis = self.x_axis.copy().rotate(np.pi/2, OUT)
self.z_axis = self.x_axis.copy().rotate(np.pi/2, DOWN)
self.digest_mobject_attrs()
class SpaceGrid(Mobject1D):
DEFAULT_CONFIG = {
"color" : GREEN,
"radius" : SPACE_HEIGHT,
"unit_to_spatial_length" : 1,
"line_frequency" : 2,
}
def generate_points(self):
line_range = range(-int(self.radius), int(self.radius)+1, self.line_frequency)
for i in range(3):
perm = np.arange(i, i+3) % 3
for a, b in it.product(line_range, line_range):
start = np.array([a, b, -self.radius])[perm]
end = np.array([a, b, self.radius])[perm]
self.add_line(start, end)
class NumberLineScene(Scene): class NumberLineScene(Scene):
def construct(self, **number_line_config): def construct(self, **number_line_config):
self.number_line = NumberLine(**number_line_config) self.number_line = NumberLine(**number_line_config)
@ -231,7 +273,7 @@ class NumberLineScene(Scene):
transforms = [] transforms = []
additional_mobjects = [] additional_mobjects = []
squished_new_line = deepcopy(new_number_line) squished_new_line = new_number_line.copy()
squished_new_line.scale(1.0/zoom_factor) squished_new_line.scale(1.0/zoom_factor)
squished_new_line.shift(self.number_line.number_to_point(number)) squished_new_line.shift(self.number_line.number_to_point(number))
squished_new_line.points[:,1] = self.number_line.number_to_point(0)[1] squished_new_line.points[:,1] = self.number_line.number_to_point(0)[1]
@ -242,7 +284,7 @@ class NumberLineScene(Scene):
transforms.append(Transform(point, mob)) transforms.append(Transform(point, mob))
for mob in self.mobjects: for mob in self.mobjects:
if mob == self.number_line: if mob == self.number_line:
new_mob = deepcopy(mob) new_mob = mob.copy()
new_mob.shift(-self.number_line.number_to_point(number)) new_mob.shift(-self.number_line.number_to_point(number))
new_mob.stretch(zoom_factor, 0) new_mob.stretch(zoom_factor, 0)
transforms.append(Transform(mob, new_mob)) transforms.append(Transform(mob, new_mob))

217
transform_article.py Normal file
View File

@ -0,0 +1,217 @@
from topics import *
from animation import *
def half_plane():
plane = NumberPlane(
x_radius = SPACE_WIDTH/2,
x_unit_to_spatial_width = 0.5,
y_unit_to_spatial_height = 0.5,
density = 2*DEFAULT_POINT_DENSITY_1D,
)
plane.add_coordinates(
x_vals = range(-6, 7, 2),
y_vals = range(-6, 7, 2)
)
return plane
class SingleVariableFunction(Scene):
args_list = [
(lambda x : x**2 - 3, "ShiftedSquare", True),
(lambda x : x**2 - 3, "ShiftedSquare", False),
]
@staticmethod
def args_to_string(func, name, separate_lines):
return name + ("SeparateLines" if separate_lines else "")
def construct(self, func, name, separate_lines):
base_line = NumberLine(color = "grey")
moving_line = NumberLine(
tick_frequency = 1,
density = 3*DEFAULT_POINT_DENSITY_1D
)
base_line.add_numbers()
def point_function((x, y, z)):
return (func(x), y, z)
target = moving_line.copy().apply_function(point_function)
transform_config = {
"run_time" : 3,
"interpolation_function" : path_along_arc(np.pi/4)
}
if separate_lines:
numbers = moving_line.get_number_mobjects(*range(-7, 7))
negative_numbers = []
for number in numbers:
number.highlight(GREEN_E)
number.shift(-2*moving_line.get_vertical_number_offset())
center = number.get_center()
target_num = number.copy()
target_num.shift(point_function(center) - center)
target.add(target_num)
if center[0] < -0.5:
negative_numbers.append(number)
moving_line.add(*numbers)
base_line.shift(DOWN)
target.shift(DOWN)
moving_line.shift(UP)
self.add(base_line, moving_line)
self.dither(3)
self.play(Transform(moving_line, target, **transform_config))
if separate_lines:
self.play(*[
ApplyMethod(mob.shift, 0.4*UP)
for mob in negative_numbers
])
self.dither(3)
class LineToPlaneFunction(Scene):
args_list = [
(lambda x : (np.cos(x), 0.5*x*np.sin(x)), "Swirl", {}),
(lambda x : (np.cos(x), 0.5*x*np.sin(x)), "Swirl", {
"0" : 0,
"\\frac{\\pi}{2}" : np.pi/2,
"\\pi" : np.pi
})
]
@staticmethod
def args_to_string(func, name, numbers_to_follow):
return name + ("FollowingNumbers" if numbers_to_follow else "")
def construct(self, func, name, numbers_to_follow):
line = NumberLine(
unit_length_to_spatial_width = 0.5,
tick_frequency = 1,
number_at_center = 6,
numerical_radius = 6,
numbers_with_elongated_ticks = [0, 12],
density = 3*DEFAULT_POINT_DENSITY_1D
)
line.to_edge(LEFT)
line_copy = line.copy()
line.add_numbers(*range(0, 14, 2))
divider = Line(SPACE_HEIGHT*UP, SPACE_HEIGHT*DOWN)
plane = half_plane()
plane.shift(0.5*SPACE_WIDTH*RIGHT)
self.add(line, divider, plane)
def point_function(point):
x, y = func(line.point_to_number(point))
return plane.num_pair_to_point((x, y))
target = line_copy.copy().apply_function(point_function)
target.highlight()
anim_config = {"run_time" : 3}
anims = [Transform(line_copy, target, **anim_config)]
colors = iter([BLUE_B, GREEN_D, RED_D])
for tex, number in numbers_to_follow.items():
center = line.number_to_point(number)
dot = Dot(center, color = colors.next())
anims.append(ApplyMethod(
dot.shift,
point_function(center) - center,
**anim_config
))
label = TexMobject(tex)
label.shift(center + 2*UP)
arrow = Arrow(label, dot)
self.add(label)
self.play(ShowCreation(arrow), ShowCreation(dot))
self.dither()
self.remove(arrow, label)
self.dither(2)
self.play(*anims)
self.dither()
class PlaneToPlaneFunctionSeparatePlanes(Scene):
args_list = [
(lambda (x, y) : (x**2+y**2, x**2-y**2), "Quadratic")
]
@staticmethod
def args_to_string(func, name):
return name
def construct(self, func, name):
shift_factor = 0.55
in_plane = half_plane().shift(shift_factor*SPACE_WIDTH*LEFT)
out_plane = half_plane().shift(shift_factor*SPACE_WIDTH*RIGHT)
divider = Line(SPACE_HEIGHT*UP, SPACE_HEIGHT*DOWN)
self.add(in_plane, out_plane, divider)
plane_copy = in_plane.copy()
plane_copy.sub_mobjects = []
def point_function(point):
result = np.array(func((point*2 + 2*shift_factor*SPACE_WIDTH*RIGHT)[:2]))
result = np.append(result/2, [0])
return result + shift_factor*SPACE_WIDTH*RIGHT
target = plane_copy.copy().apply_function(point_function)
target.highlight(GREEN_B)
anim_config = {"run_time" : 5}
self.dither()
self.play(Transform(plane_copy, target, **anim_config))
self.dither()
class PlaneToPlaneFunction(Scene):
args_list = [
(lambda (x, y) : (x**2+y**2, x**2-y**2), "Quadratic")
]
@staticmethod
def args_to_string(func, name):
return name
def construct(self, func, name):
plane = NumberPlane()
background = NumberPlane(color = "grey")
background.add_coordinates()
anim_config = {"run_time" : 3}
def point_function(point):
return np.append(func(point[:2]), [0])
self.add(background, plane)
self.dither(2)
self.play(ApplyPointwiseFunction(point_function, plane, **anim_config))
self.dither(3)