diff --git a/camera.py b/camera.py index 736303ca..e5e41acb 100644 --- a/camera.py +++ b/camera.py @@ -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 = [] diff --git a/eola/chapter2.py b/eola/chapter2.py index 333b4b03..17c13492 100644 --- a/eola/chapter2.py +++ b/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() + + + + + diff --git a/eola/two_d_space.py b/eola/two_d_space.py index c0e097f8..8ff77eac 100644 --- a/eola/two_d_space.py +++ b/eola/two_d_space.py @@ -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) diff --git a/mobject/mobject.py b/mobject/mobject.py index 8d45508d..977563e9 100644 --- a/mobject/mobject.py +++ b/mobject/mobject.py @@ -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() diff --git a/mobject/point_cloud_mobject.py b/mobject/point_cloud_mobject.py index 51d70326..1d2fef54 100644 --- a/mobject/point_cloud_mobject.py +++ b/mobject/point_cloud_mobject.py @@ -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) ) diff --git a/mobject/vectorized_mobject.py b/mobject/vectorized_mobject.py index e6c9f3a1..bc71d2de 100644 --- a/mobject/vectorized_mobject.py +++ b/mobject/vectorized_mobject.py @@ -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 diff --git a/scene/scene.py b/scene/scene.py index 41c297ca..98912e3d 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -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))