mirror of
https://github.com/3b1b/manim.git
synced 2026-04-26 03:00:23 -04:00
Scene no longer cares about pointless mobjects
This commit is contained in:
@@ -66,7 +66,7 @@ class Camera(object):
|
||||
def capture_mobjects(self, mobjects, include_submobjects = True):
|
||||
if include_submobjects:
|
||||
mobjects = it.chain(*[
|
||||
mob.nonempty_family_members()
|
||||
mob.family_members_with_points()
|
||||
for mob in mobjects
|
||||
])
|
||||
vmobjects = []
|
||||
|
||||
101
eola/chapter2.py
101
eola/chapter2.py
@@ -54,16 +54,17 @@ class CoordinatesWereFamiliar(TeacherStudentsScene):
|
||||
|
||||
class CoordinatesAsScalars(VectorScene):
|
||||
def construct(self):
|
||||
self.add_axes()
|
||||
self.axes = self.add_axes()
|
||||
vector = self.add_vector([3, -2])
|
||||
array, x_line, y_line = self.vector_to_coords(vector)
|
||||
self.add(array)
|
||||
self.dither()
|
||||
self.general_idea_of_scalars(array)
|
||||
new_array = self.general_idea_of_scalars(array)
|
||||
self.scale_basis_vectors(new_array)
|
||||
self.show_symbolic_sum(new_array, vector)
|
||||
|
||||
def general_idea_of_scalars(self, array):
|
||||
starting_mobjects = self.get_mobjects()
|
||||
starting_mobjects.remove(array)
|
||||
|
||||
title = TextMobject("Think of each coordinate as a scalar")
|
||||
title.to_edge(UP)
|
||||
@@ -74,12 +75,104 @@ class CoordinatesAsScalars(VectorScene):
|
||||
new_y = y.copy().scale(2).highlight(Y_COLOR)
|
||||
new_y.move_to(3*RIGHT+2*UP)
|
||||
|
||||
i_hat, j_hat = self.get_basis_vectors()
|
||||
new_i_hat = Vector(3*i_hat.get_end(), color = X_COLOR)
|
||||
new_j_hat = Vector(-2*j_hat.get_end(), color = Y_COLOR)
|
||||
VMobject(i_hat, new_i_hat).shift(3*LEFT)
|
||||
VMobject(j_hat, new_j_hat).shift(3*RIGHT)
|
||||
|
||||
new_array = Matrix([new_x.copy(), new_y.copy()])
|
||||
new_array.replace(array)
|
||||
new_array.shift(0.5*DOWN)
|
||||
|
||||
self.remove(*starting_mobjects)
|
||||
self.play(
|
||||
FadeOut(*starting_mobjects)
|
||||
Transform(x, new_x),
|
||||
Transform(y, new_y),
|
||||
Write(title),
|
||||
)
|
||||
self.play(FadeIn(i_hat), FadeIn(j_hat))
|
||||
self.dither()
|
||||
self.play(
|
||||
Transform(i_hat, new_i_hat),
|
||||
Transform(j_hat, new_j_hat)
|
||||
)
|
||||
self.dither()
|
||||
starting_mobjects.remove(array)
|
||||
self.play(
|
||||
Transform(
|
||||
VMobject(x, y),
|
||||
VMobject(*new_array.get_mob_matrix().flatten())
|
||||
),
|
||||
FadeOut(i_hat),
|
||||
FadeOut(j_hat),
|
||||
Write(new_array.get_brackets()),
|
||||
FadeIn(VMobject(*starting_mobjects)),
|
||||
FadeOut(title)
|
||||
)
|
||||
self.remove(x, y)
|
||||
self.add(new_array)
|
||||
return new_array
|
||||
|
||||
def scale_basis_vectors(self, new_array):
|
||||
self.play(ApplyMethod(self.axes.highlight, GREY))
|
||||
i_hat, j_hat = self.get_basis_vectors()
|
||||
for mob in i_hat, j_hat:
|
||||
mob.set_stroke(width = 6)
|
||||
self.add_vector(i_hat)
|
||||
i_hat_label = self.label_vector(
|
||||
i_hat, "\\hat{\\imath}",
|
||||
color = X_COLOR,
|
||||
label_scale_val = 1
|
||||
)
|
||||
self.add_vector(j_hat)
|
||||
j_hat_label = self.label_vector(
|
||||
j_hat, "\\hat{\\jmath}",
|
||||
color = Y_COLOR,
|
||||
label_scale_val = 1
|
||||
)
|
||||
|
||||
x, y = new_array.get_mob_matrix().flatten()
|
||||
for coord, v, label, factor, shift_right in [
|
||||
(x, i_hat, i_hat_label, 3, False),
|
||||
(y, j_hat, j_hat_label, -2, True)
|
||||
]:
|
||||
faded_v = v.copy().fade(0.5)
|
||||
scaled_v = Vector(factor*v.get_end(), color = v.get_color())
|
||||
|
||||
scaled_label = VMobject(coord.copy(), label.copy())
|
||||
scaled_label.arrange_submobjects(RIGHT, buff = 0.1)
|
||||
scaled_label.move_to(label, DOWN+RIGHT)
|
||||
scaled_label.shift(scaled_v.get_center()-v.get_center())
|
||||
coord_copy = coord.copy()
|
||||
self.play(
|
||||
Transform(v.copy(), faded_v),
|
||||
Transform(v, scaled_v),
|
||||
Transform(VMobject(coord_copy, label), scaled_label),
|
||||
)
|
||||
self.dither()
|
||||
if shift_right:
|
||||
group = VMobject(v, coord_copy, label)
|
||||
self.play(ApplyMethod(group.shift, 3*RIGHT))
|
||||
self.dither()
|
||||
|
||||
|
||||
def show_symbolic_sum(self, new_array, vector):
|
||||
new_mob = TexMobject([
|
||||
"3\\hat{\\imath}", "+", "(-2)\\hat{\\jmath}"
|
||||
])
|
||||
new_mob.shift(vector.get_end()-new_mob.get_corner(UP+LEFT))
|
||||
i_hat, plus, j_hat = new_mob.split()
|
||||
i_hat.highlight(X_COLOR)
|
||||
j_hat.highlight(Y_COLOR)
|
||||
|
||||
self.play(Transform(new_array, new_mob))
|
||||
self.dither()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -118,18 +118,19 @@ class VectorScene(Scene):
|
||||
return plane
|
||||
|
||||
def add_axes(self, animate = False, color = WHITE, **kwargs):
|
||||
axes = Axes(color = color)
|
||||
axes = Axes(color = color, tick_frequency = 1)
|
||||
if animate:
|
||||
self.play(ShowCreation(axes, submobject_mode = "one_at_a_time"))
|
||||
self.add(axes)
|
||||
return axes
|
||||
|
||||
def add_vector(self, vector, animate = True, color = YELLOW):
|
||||
arrow = Vector(vector, color = color)
|
||||
if not isinstance(vector, Arrow):
|
||||
vector = Vector(vector, color = color)
|
||||
if animate:
|
||||
self.play(ShowCreation(arrow, submobject_mode = "one_at_a_time"))
|
||||
self.add(arrow)
|
||||
return arrow
|
||||
self.play(ShowCreation(vector, submobject_mode = "one_at_a_time"))
|
||||
self.add(vector)
|
||||
return vector
|
||||
|
||||
def get_basis_vectors(self):
|
||||
i_hat = Vector([1, 0], color = X_COLOR)
|
||||
@@ -139,12 +140,13 @@ class VectorScene(Scene):
|
||||
def label_vector(self, vector, label, animate = True,
|
||||
direction = "left", rotate = False,
|
||||
color = WHITE, add_to_vector = True,
|
||||
buff_factor = 1.5):
|
||||
buff_factor = 2,
|
||||
label_scale_val = VECTOR_LABEL_SCALE_VAL):
|
||||
if len(label) == 1:
|
||||
label = "\\vec{\\textbf{%s}}"%label
|
||||
label = TexMobject(label)
|
||||
label.highlight(color)
|
||||
label.scale(VECTOR_LABEL_SCALE_VAL)
|
||||
label.scale(label_scale_val)
|
||||
if rotate:
|
||||
label.rotate(vector.get_angle())
|
||||
|
||||
@@ -153,10 +155,10 @@ class VectorScene(Scene):
|
||||
rot_angle = -np.pi/2
|
||||
else:
|
||||
rot_angle = np.pi/2
|
||||
label.shift(-buff_factor*label.get_boundary_point(
|
||||
label.shift(-buff_factor*label.get_critical_point(
|
||||
rotate_vector(vector_vect, rot_angle)
|
||||
))
|
||||
label.shift(vector.get_center())
|
||||
label.shift(vector_vect/2)
|
||||
|
||||
if add_to_vector:
|
||||
vector.add(label)
|
||||
|
||||
@@ -96,18 +96,18 @@ class Mobject(object):
|
||||
#### Transforming operations ######
|
||||
|
||||
def apply_to_family(self, func):
|
||||
for mob in self.nonempty_family_members():
|
||||
for mob in self.family_members_with_points():
|
||||
func(mob)
|
||||
|
||||
def shift(self, *vectors):
|
||||
total_vector = reduce(op.add, vectors)
|
||||
for mob in self.nonempty_family_members():
|
||||
for mob in self.family_members_with_points():
|
||||
mob.points += total_vector
|
||||
return self
|
||||
|
||||
|
||||
def scale(self, scale_factor):
|
||||
for mob in self.nonempty_family_members():
|
||||
for mob in self.family_members_with_points():
|
||||
mob.points *= scale_factor
|
||||
return self
|
||||
|
||||
@@ -118,22 +118,22 @@ class Mobject(object):
|
||||
for axis in axes:
|
||||
rot_matrix = np.dot(rot_matrix, rotation_matrix(angle, axis))
|
||||
t_rot_matrix = np.transpose(rot_matrix)
|
||||
for mob in self.nonempty_family_members():
|
||||
for mob in self.family_members_with_points():
|
||||
mob.points = np.dot(mob.points, t_rot_matrix)
|
||||
return self
|
||||
|
||||
def stretch(self, factor, dim):
|
||||
for mob in self.nonempty_family_members():
|
||||
for mob in self.family_members_with_points():
|
||||
mob.points[:,dim] *= factor
|
||||
return self
|
||||
|
||||
def apply_function(self, function):
|
||||
for mob in self.nonempty_family_members():
|
||||
for mob in self.family_members_with_points():
|
||||
mob.points = np.apply_along_axis(function, 1, mob.points)
|
||||
return self
|
||||
|
||||
def wag(self, direction = RIGHT, axis = DOWN, wag_factor = 1.0):
|
||||
for mob in self.nonempty_family_members():
|
||||
for mob in self.family_members_with_points():
|
||||
alphas = np.dot(mob.points, np.transpose(axis))
|
||||
alphas -= min(alphas)
|
||||
alphas /= max(alphas)
|
||||
@@ -145,7 +145,7 @@ class Mobject(object):
|
||||
return self
|
||||
|
||||
def reverse_points(self):
|
||||
for mob in self.nonempty_family_members():
|
||||
for mob in self.family_members_with_points():
|
||||
mob.apply_over_attr_arrays(
|
||||
lambda arr : np.array(list(reversed(arr)))
|
||||
)
|
||||
@@ -160,7 +160,7 @@ class Mobject(object):
|
||||
lambda a1, a2 : np.append(a1, a2, axis = 0),
|
||||
[array]*count
|
||||
)
|
||||
for mob in self.nonempty_family_members():
|
||||
for mob in self.family_members_with_points():
|
||||
mob.apply_over_attr_arrays(repeat_array)
|
||||
return self
|
||||
|
||||
@@ -299,7 +299,7 @@ class Mobject(object):
|
||||
start = color_to_rgb(self.get_color())
|
||||
end = color_to_rgb(color)
|
||||
new_rgb = interpolate(start, end, alpha)
|
||||
for mob in self.nonempty_family_members():
|
||||
for mob in self.family_members_with_points():
|
||||
mob.highlight(Color(rgb = new_rgb))
|
||||
return self
|
||||
|
||||
@@ -333,7 +333,7 @@ class Mobject(object):
|
||||
|
||||
def get_merged_array(self, array_attr):
|
||||
result = np.zeros((0, self.dim))
|
||||
for mob in self.nonempty_family_members():
|
||||
for mob in self.family_members_with_points():
|
||||
result = np.append(result, getattr(mob, array_attr), 0)
|
||||
return result
|
||||
|
||||
@@ -417,7 +417,7 @@ class Mobject(object):
|
||||
all_mobjects = [self] + reduce(op.add, sub_families, [])
|
||||
return remove_list_redundancies(all_mobjects)
|
||||
|
||||
def nonempty_family_members(self):
|
||||
def family_members_with_points(self):
|
||||
return filter(
|
||||
lambda m : m.get_num_points() > 0,
|
||||
self.submobject_family()
|
||||
|
||||
@@ -28,7 +28,7 @@ class PMobject(Mobject):
|
||||
|
||||
def highlight(self, color = YELLOW_C, condition = None):
|
||||
rgb = Color(color).get_rgb()
|
||||
for mob in self.nonempty_family_members():
|
||||
for mob in self.family_members_with_points():
|
||||
if condition:
|
||||
to_change = np.apply_along_axis(condition, 1, mob.points)
|
||||
mob.rgbs[to_change, :] = rgb
|
||||
@@ -41,7 +41,7 @@ class PMobject(Mobject):
|
||||
np.array(Color(color).get_rgb())
|
||||
for color in start_color, end_color
|
||||
]
|
||||
for mob in self.nonempty_family_members():
|
||||
for mob in self.family_members_with_points():
|
||||
num_points = mob.get_num_points()
|
||||
mob.rgbs = np.array([
|
||||
interpolate(start_rgb, end_rgb, alpha)
|
||||
@@ -56,7 +56,7 @@ class PMobject(Mobject):
|
||||
return self
|
||||
|
||||
def filter_out(self, condition):
|
||||
for mob in self.nonempty_family_members():
|
||||
for mob in self.family_members_with_points():
|
||||
to_eliminate = ~np.apply_along_axis(condition, 1, mob.points)
|
||||
mob.points = mob.points[to_eliminate]
|
||||
mob.rgbs = mob.rgbs[to_eliminate]
|
||||
@@ -66,7 +66,7 @@ class PMobject(Mobject):
|
||||
"""
|
||||
Removes all but every nth point for n = factor
|
||||
"""
|
||||
for mob in self.nonempty_family_members():
|
||||
for mob in self.family_members_with_points():
|
||||
num_points = self.get_num_points()
|
||||
mob.apply_over_attr_arrays(
|
||||
lambda arr : arr[
|
||||
@@ -79,7 +79,7 @@ class PMobject(Mobject):
|
||||
"""
|
||||
function is any map from R^3 to R
|
||||
"""
|
||||
for mob in self.nonempty_family_members():
|
||||
for mob in self.family_members_with_points():
|
||||
indices = np.argsort(
|
||||
np.apply_along_axis(function, 1, mob.points)
|
||||
)
|
||||
|
||||
@@ -77,7 +77,6 @@ class VMobject(Mobject):
|
||||
return self
|
||||
|
||||
def fade(self, darkness = 0.5):
|
||||
self.set_fill(opacity = 1-darkness)
|
||||
Mobject.fade(self, darkness)
|
||||
return self
|
||||
|
||||
|
||||
@@ -67,13 +67,24 @@ class Scene(object):
|
||||
self.clear()
|
||||
###
|
||||
|
||||
def extract_mobjects_with_points(self, *mobjects):
|
||||
return list(it.chain(*[
|
||||
m.family_members_with_points()
|
||||
for m in mobjects
|
||||
]))
|
||||
|
||||
|
||||
def add(self, *mobjects):
|
||||
"""
|
||||
Mobjects will be displayed, from background to foreground,
|
||||
in the order with which they are entered.
|
||||
|
||||
Scene class only keeps track of pointful monjects, all
|
||||
grouping structure is forgotten as far as Scene is concerned
|
||||
"""
|
||||
if not all_elements_are_instances(mobjects, Mobject):
|
||||
raise Exception("Adding something which is not a mobject")
|
||||
mobjects = self.extract_mobjects_with_points(*mobjects)
|
||||
old_mobjects = filter(lambda m : m not in mobjects, self.mobjects)
|
||||
self.mobjects = old_mobjects + list(mobjects)
|
||||
return self
|
||||
@@ -84,14 +95,15 @@ class Scene(object):
|
||||
by calling add_mobjects_among(locals().values())
|
||||
"""
|
||||
mobjects = filter(lambda x : isinstance(x, Mobject), values)
|
||||
mobjects = self.extract_mobjects_with_points(*mobjects)
|
||||
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 = it.chain(*[m.submobject_family() for m in mobjects])
|
||||
mobjects = filter(lambda m : m in self.mobjects, mobjects)
|
||||
mobjects = self.extract_mobjects_with_points(*mobjects)
|
||||
# mobjects = filter(lambda m : m in self.mobjects, mobjects)
|
||||
if len(mobjects) == 0:
|
||||
return
|
||||
self.mobjects = filter(lambda m : m not in mobjects, self.mobjects)
|
||||
@@ -126,14 +138,12 @@ class Scene(object):
|
||||
return animations
|
||||
|
||||
def separate_moving_and_static_mobjects(self, *animations):
|
||||
moving_mobjects = list(it.chain(*[
|
||||
anim.mobject.submobject_family()
|
||||
for anim in animations
|
||||
]))
|
||||
bundle = Mobject(*self.mobjects)
|
||||
moving_mobjects = self.extract_mobjects_with_points(
|
||||
*[anim.mobject for anim in animations]
|
||||
)
|
||||
static_mobjects = filter(
|
||||
lambda m : m not in moving_mobjects,
|
||||
bundle.submobject_family()
|
||||
self.mobjects
|
||||
)
|
||||
return moving_mobjects, static_mobjects
|
||||
|
||||
@@ -186,7 +196,8 @@ class Scene(object):
|
||||
t1 = float(t1)%existing_scene_time
|
||||
t0, t1 = min(t0, t1), max(t0, t1)
|
||||
|
||||
moving_mobjects = [anim.mobject for anim in animations]
|
||||
moving_mobjects, static_mobjects = \
|
||||
self.separate_moving_and_static_mobjects(*animations)
|
||||
for t in np.arange(t0, t1, self.frame_duration):
|
||||
for animation in animations:
|
||||
animation.update((t-t0)/(t1 - t0))
|
||||
|
||||
Reference in New Issue
Block a user