mirror of
https://github.com/3b1b/manim.git
synced 2025-07-28 20:43:56 +08:00
Beginning transform KA article, many small fixes to the mobject refactor
This commit is contained in:
@ -4,7 +4,6 @@ import numpy as np
|
||||
import warnings
|
||||
import time
|
||||
import os
|
||||
import copy
|
||||
import progressbar
|
||||
import inspect
|
||||
|
||||
@ -21,7 +20,7 @@ class Animation(object):
|
||||
mobject = instantiate(mobject)
|
||||
assert(isinstance(mobject, Mobject))
|
||||
digest_config(self, kwargs, locals())
|
||||
self.starting_mobject = copy.deepcopy(self.mobject)
|
||||
self.starting_mobject = self.mobject.copy()
|
||||
if self.alpha_func is None:
|
||||
self.alpha_func = (lambda x : x)
|
||||
if self.name is None:
|
||||
|
@ -1,6 +1,5 @@
|
||||
import numpy as np
|
||||
import itertools as it
|
||||
from copy import deepcopy
|
||||
|
||||
from helpers import *
|
||||
from animation import Animation
|
||||
|
@ -1,6 +1,5 @@
|
||||
import numpy as np
|
||||
import itertools as it
|
||||
from copy import deepcopy
|
||||
|
||||
from helpers import *
|
||||
|
||||
|
@ -46,6 +46,7 @@ class Transform(Animation):
|
||||
[self.mobject, self.starting_mobject, self.ending_mobject]
|
||||
)
|
||||
for m, start, end in zip(*families):
|
||||
# print m, start, end
|
||||
m.points = self.interpolation_function(
|
||||
start.points, end.points, alpha
|
||||
)
|
||||
@ -129,7 +130,7 @@ class ApplyFunction(Transform):
|
||||
Transform.__init__(
|
||||
self,
|
||||
mobject,
|
||||
function(copy.deepcopy(mobject)),
|
||||
function(mobject.copy()),
|
||||
**kwargs
|
||||
)
|
||||
self.name = "ApplyFunctionTo"+str(mobject)
|
||||
|
@ -103,7 +103,7 @@ def handle_scene(scene, **config):
|
||||
scene.show_frame()
|
||||
path = os.path.join(MOVIE_DIR, config["movie_prefix"], "images")
|
||||
if not os.path.exists(path):
|
||||
os.mkdir(path)
|
||||
os.makedirs(path)
|
||||
scene.save_image(path, name)
|
||||
if config["write"]:
|
||||
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"])
|
||||
return [args_list[index]]
|
||||
if config["args_extension"] == "" :
|
||||
name_to_args = dict(zip(preset_extensions, args_list))
|
||||
return prompt_user_for_choice(name_to_args)
|
||||
if len(args_list) == 1:
|
||||
return args_list
|
||||
name_to_args = dict(zip(preset_extensions, args_list))
|
||||
return prompt_user_for_choice(name_to_args)
|
||||
return [SceneClass.string_to_args(config["args_extension"])]
|
||||
|
||||
def get_scene_classes(scene_names_to_classes, config):
|
||||
|
@ -104,6 +104,8 @@ class Mobject(object):
|
||||
os.path.join(MOVIE_DIR, (name or str(self)) + ".png")
|
||||
)
|
||||
|
||||
def copy(self):
|
||||
return deepcopy(self)
|
||||
|
||||
#### Fundamental operations ######
|
||||
|
||||
@ -226,7 +228,7 @@ class Mobject(object):
|
||||
"""
|
||||
target_point = np.sign(direction) * (SPACE_WIDTH, SPACE_HEIGHT, 0)
|
||||
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
|
||||
|
||||
def to_corner(self, corner = LEFT+DOWN, buff = DEFAULT_MOBJECT_TO_EDGE_BUFFER):
|
||||
@ -437,16 +439,7 @@ class Mobject(object):
|
||||
else:
|
||||
larger, smaller = mobject1, mobject2
|
||||
for sub_mob in larger.sub_mobjects[-diff:]:
|
||||
center = 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])
|
||||
))
|
||||
smaller.add(Point(sub_mob.get_center()))
|
||||
for m1, m2 in zip(mobject1.sub_mobjects, mobject2.sub_mobjects):
|
||||
Mobject.align_data(m1, m2)
|
||||
|
||||
|
@ -205,7 +205,7 @@ class DrawComplexAngleAndMagnitude(Scene):
|
||||
label.shift(point - edge + buff*dot_to_label_dir)
|
||||
label.highlight(YELLOW)
|
||||
|
||||
self.add_local_mobjects()
|
||||
self.add_mobjects_among(locals().values())
|
||||
|
||||
|
||||
def add_angle_label(self, number):
|
||||
@ -214,7 +214,7 @@ class DrawComplexAngleAndMagnitude(Scene):
|
||||
radius = 0.2
|
||||
)
|
||||
|
||||
self.add_local_mobjects()
|
||||
self.add_mobjects_among(locals().values())
|
||||
|
||||
def add_lines(self, tex_representation, number):
|
||||
point = self.plane.number_to_point(number)
|
||||
@ -250,5 +250,5 @@ class DrawComplexAngleAndMagnitude(Scene):
|
||||
axis = OUT if point[1] > 0 else IN
|
||||
norm_label.next_to(brace, rotate_vector(point, np.pi/2, axis))
|
||||
|
||||
self.add_local_mobjects()
|
||||
self.add_mobjects_among(locals().values())
|
||||
|
||||
|
@ -165,7 +165,7 @@ class IndicateTroublePointFromParallelLines(AddParallelLines):
|
||||
vect = DOWN+RIGHT
|
||||
arrow = Arrow(circle.get_center()+2*vect, circle.get_boundary_point(vect))
|
||||
arrow.highlight(circle.get_color())
|
||||
self.add_local_mobjects()
|
||||
self.add_mobjects_among(locals().values())
|
||||
|
||||
|
||||
class DrawAllThreeSquaresWithMoreTriangles(DrawAllThreeSquares):
|
||||
@ -224,7 +224,7 @@ class IndicateBigRectangleTroublePoint(DrawAllThreeSquaresWithMoreTriangles):
|
||||
circle.shift(4*RIGHT)
|
||||
vect = DOWN+RIGHT
|
||||
arrow = Arrow(circle.get_center()+vect, circle.get_boundary_point(vect))
|
||||
self.add_local_mobjects()
|
||||
self.add_mobjects_among(locals().values())
|
||||
|
||||
class ShowBigRectangleDimensions(DrawAllThreeSquaresWithMoreTriangles):
|
||||
args_list = [(10, False)]
|
||||
@ -239,7 +239,7 @@ class ShowBigRectangleDimensions(DrawAllThreeSquaresWithMoreTriangles):
|
||||
b_plus_2a = TexMobject("b+2a").scale(TEX_MOB_SCALE_VAL)
|
||||
a_plus_2b.next_to(u_brace, DOWN)
|
||||
b_plus_2a.next_to(side_brace, LEFT)
|
||||
self.add_local_mobjects()
|
||||
self.add_mobjects_among(locals().values())
|
||||
|
||||
class FillInAreaOfBigRectangle(DrawAllThreeSquaresWithMoreTriangles):
|
||||
args_list = [(10, False)]
|
||||
@ -267,7 +267,7 @@ class DrawOnlyABSquares(Scene):
|
||||
symobl.shift(mob.get_center())
|
||||
self.add(symobl)
|
||||
triangle = Triangle()
|
||||
self.add_local_mobjects()
|
||||
self.add_mobjects_among(locals().values())
|
||||
|
||||
class AddTriangleCopyToABSquares(DrawOnlyABSquares):
|
||||
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))
|
||||
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()
|
||||
if rotate:
|
||||
for mob in self.mobjects:
|
||||
|
@ -76,19 +76,20 @@ class Scene(object):
|
||||
self.mobjects = old_mobjects + list(mobjects)
|
||||
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
|
||||
by calling add_mobjects_among(locals().values())
|
||||
"""
|
||||
name_to_mob = get_caller_locals(lambda x : isinstance(x, Mobject))
|
||||
self.add(*name_to_mob.values())
|
||||
mobjects = filter(lambda x : isinstance(x, Mobject), values)
|
||||
self.add(*mobjects)
|
||||
return self
|
||||
|
||||
def remove(self, *mobjects):
|
||||
if not all_elements_are_instances(mobjects, Mobject):
|
||||
raise Exception("Removing something which is not a mobject")
|
||||
mobjects = filter(lambda m : m in self.mobjects, mobjects)
|
||||
if len(mobjects):
|
||||
if len(mobjects) == 0:
|
||||
return
|
||||
self.mobjects = filter(lambda m : m not in mobjects, self.mobjects)
|
||||
self.repaint_mojects()
|
||||
@ -158,7 +159,11 @@ class Scene(object):
|
||||
run_time = animations[0].run_time
|
||||
for animation in animations:
|
||||
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)
|
||||
static_mobjects = filter(
|
||||
|
@ -77,7 +77,7 @@ class RearrangeEquation(Scene):
|
||||
if sum(matches) > 1:
|
||||
base_mob = all_mobs[list(all_terms).index(term)]
|
||||
all_mobs[matches] = [
|
||||
deepcopy(base_mob).replace(target_mob)
|
||||
base_mob.copy().replace(target_mob)
|
||||
for target_mob in all_mobs[matches]
|
||||
]
|
||||
return all_mobs[:num_start_terms], all_mobs[num_start_terms:]
|
||||
|
@ -1,5 +1,3 @@
|
||||
from copy import deepcopy
|
||||
|
||||
from helpers import *
|
||||
|
||||
from mobject import Mobject, Mobject, ImageMobject, TexMobject
|
||||
@ -43,7 +41,7 @@ class PiCreature(Mobject):
|
||||
mouth_center = self.get_mouth_center()
|
||||
self.mouth.center()
|
||||
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)
|
||||
for mouth in self.smile, self.frown, self.straight_mouth:
|
||||
mouth.sort_points(lambda p : p[0])
|
||||
@ -118,7 +116,7 @@ class PiCreature(Mobject):
|
||||
|
||||
def get_step_intermediate(self, pi_creature):
|
||||
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
|
||||
if self.right_leg.get_center()[0] < self.left_leg.get_center()[0]:
|
||||
#For Mortimer's case
|
||||
|
@ -113,7 +113,7 @@ class PascalsTriangleScene(Scene):
|
||||
center = self.coords_to_center(n, k)
|
||||
if num not in num_to_num_mob:
|
||||
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(
|
||||
1,
|
||||
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 k in (n + a + 1, -a -1):
|
||||
self.coords.append((n, k))
|
||||
mob = deepcopy(zero)
|
||||
mob = zero.copy()
|
||||
mob.shift(self.coords_to_center(n, k))
|
||||
self.coords_to_mobs[n][k] = mob
|
||||
self.add(mob)
|
||||
|
@ -21,7 +21,7 @@ class ComplexPlane(NumberPlane):
|
||||
digest_config(self, kwargs)
|
||||
kwargs.update({
|
||||
"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_faded_line_frequency" : self.faded_line_frequency,
|
||||
"y_line_frequency" : self.line_frequency,
|
||||
|
@ -53,9 +53,11 @@ class Line(Mobject1D):
|
||||
for arg in start, end
|
||||
]
|
||||
start_to_end = preliminary_end - preliminary_start
|
||||
vect = np.zeros(len(start_to_end))
|
||||
longer_dim = np.argmax(map(abs, start_to_end))
|
||||
vect[longer_dim] = start_to_end[longer_dim]
|
||||
self.start, self.end = [
|
||||
arg.get_edge_center(unit*start_to_end)
|
||||
arg.get_edge_center(unit*vect)
|
||||
if isinstance(arg, Mobject)
|
||||
else np.array(arg)
|
||||
for arg, unit in zip([start, end], [1, -1])
|
||||
|
@ -246,7 +246,7 @@ class GraphScene(Scene):
|
||||
self.play(*[
|
||||
CounterclockwiseTransform(
|
||||
vertex,
|
||||
deepcopy(mobject).shift(vertex.get_center())
|
||||
mobject.copy().shift(vertex.get_center())
|
||||
)
|
||||
for vertex in self.vertices
|
||||
] + [
|
||||
@ -260,7 +260,7 @@ class GraphScene(Scene):
|
||||
def annotate_edges(self, mobject, fade_in = True, **kwargs):
|
||||
angles = map(np.arctan, map(Line.get_slope, self.edges))
|
||||
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)
|
||||
]
|
||||
if fade_in:
|
||||
|
@ -18,7 +18,7 @@ class NumberLine(Mobject1D):
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, kwargs)
|
||||
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.right_num = self.number_at_center + self.numerical_radius
|
||||
Mobject1D.__init__(self, **kwargs)
|
||||
@ -66,7 +66,8 @@ class NumberLine(Mobject1D):
|
||||
)
|
||||
|
||||
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):
|
||||
return self.get_tick_numbers()[::2]
|
||||
@ -115,7 +116,7 @@ class NumberPlane(Mobject1D):
|
||||
"x_radius" : SPACE_WIDTH,
|
||||
"y_radius" : SPACE_HEIGHT,
|
||||
"x_unit_to_spatial_width" : 1,
|
||||
"y_uint_to_spatial_height" : 1,
|
||||
"y_unit_to_spatial_height" : 1,
|
||||
"x_line_frequency" : 1,
|
||||
"x_faded_line_frequency" : 0.5,
|
||||
"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(
|
||||
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(
|
||||
0, self.x_radius,
|
||||
@ -153,7 +154,7 @@ class NumberPlane(Mobject1D):
|
||||
)
|
||||
y_cont_vals = np.arange(
|
||||
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]):
|
||||
self.add_points(
|
||||
@ -170,11 +171,17 @@ class NumberPlane(Mobject1D):
|
||||
|
||||
def num_pair_to_point(self, pair):
|
||||
pair = pair + self.num_pair_at_center
|
||||
return np.array([
|
||||
pair[0]*self.x_unit_to_spatial_width,
|
||||
pair[1]*self.y_uint_to_spatial_height,
|
||||
0
|
||||
])
|
||||
result = self.get_center()
|
||||
result[0] += pair[0]*self.x_unit_to_spatial_width
|
||||
result[1] += pair[1]*self.y_unit_to_spatial_height
|
||||
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):
|
||||
result = []
|
||||
@ -207,6 +214,41 @@ class NumberPlane(Mobject1D):
|
||||
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):
|
||||
def construct(self, **number_line_config):
|
||||
self.number_line = NumberLine(**number_line_config)
|
||||
@ -231,7 +273,7 @@ class NumberLineScene(Scene):
|
||||
|
||||
transforms = []
|
||||
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.shift(self.number_line.number_to_point(number))
|
||||
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))
|
||||
for mob in self.mobjects:
|
||||
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.stretch(zoom_factor, 0)
|
||||
transforms.append(Transform(mob, new_mob))
|
||||
|
217
transform_article.py
Normal file
217
transform_article.py
Normal 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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user