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 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:
|
||||||
|
@ -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
|
||||||
|
@ -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 *
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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):
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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())
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -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(
|
||||||
|
@ -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:]
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
|
@ -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])
|
||||||
|
@ -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:
|
||||||
|
@ -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
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