Scene no longer cares about pointless mobjects

This commit is contained in:
Grant Sanderson
2016-07-18 14:03:25 -07:00
parent a3a066f5a3
commit 889c31f590
7 changed files with 146 additions and 41 deletions

View File

@@ -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 = []

View File

@@ -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()

View File

@@ -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)

View File

@@ -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()

View File

@@ -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)
)

View File

@@ -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

View File

@@ -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))