Exemplo n.º 1
0
    def __init__(self,
                 eq1,
                 eq2,
                 regex,
                 regex2=None,
                 map_list=None,
                 align_char=None):
        # align equations
        if align_char:
            difference_vec = \
                self.mob_from_char(eq1, eq1.tex_string, align_char).get_center() - \
                self.mob_from_char(eq2, eq2.tex_string, align_char).get_center()
        else:
            difference_vec = eq1.get_center() - eq2.get_center()
        eq2.shift(difference_vec)

        if regex2 is None:
            g1 = self.split_by_regex(eq1, regex)
            g2 = self.split_by_regex(eq2, regex)
            assert (len(g1.submobjects) == len(g2.submobjects))
            trans = ReplacementTransform(g1, g2)
            AnimationGroup.__init__(self, trans)
        else:
            assert (map_list)
            g1 = self.split_by_regex(eq1, regex)
            g2 = self.split_by_regex(eq2, regex2)
            self.g1 = g1
            self.g2 = g2
            G1 = VGroup()
            G2 = VGroup()
            F1 = Group()
            F2 = Group()
            g1_nodes = set(pair[0] for pair in map_list)
            g2_nodes = set(pair[1] for pair in map_list)
            while map_list:
                l1 = [g1.submobjects[map_list[0][0]]]
                l2 = [g2.submobjects[map_list[0][1]]]
                new_list = []
                for i in range(1, len(map_list)):
                    if map_list[i][0] == map_list[0][0]:
                        l2.append(g2.submobjects[map_list[i][1]])
                    elif map_list[i][1] == map_list[0][1]:
                        l1.append(g1.submobjects[map_list[i][0]])
                    else:
                        new_list.append(map_list[i])
                map_list = new_list
                G1.submobjects.append(VGroup(*l1))
                G2.submobjects.append(VGroup(*l2))
            for i in range(len(g1.submobjects)):
                if i not in g1_nodes:
                    F1.submobjects.append(g1.submobjects[i])
            for i in range(len(g2.submobjects)):
                if i not in g2_nodes:
                    F2.submobjects.append(g2.submobjects[i])
            self.g1 = g1
            self.g2 = g2
            trans = ReplacementTransform(G1, G2)
            fadeout = FadeOut(F1)
            fadein = FadeIn(F2)
            AnimationGroup.__init__(self, trans, fadeout, fadein)
Exemplo n.º 2
0
    def __init__(self, nodes, edges, attrs=None, **kwargs):
        # typechecking
        for node in nodes:
            Node.assert_primitive(node)
        for edge in edges:
            Edge.assert_primitive(edge)

        if attrs is None:
            attrs = OrderedDict()

        # mobject init
        update_without_overwrite(kwargs, self.CONFIG)
        Group.__init__(self, **kwargs)

        # create submobjects
        self.nodes = {}
        self.edges = {}

        # create nodes
        for point in nodes:
            node_attrs = attrs.get(point, OrderedDict())
            node = Node(point, **update_without_overwrite(node_attrs, kwargs))
            self.nodes[node.key] = node
            self.add(node)

        # create edges
        for pair in edges:
            edge_attrs = attrs.get(pair, OrderedDict())
            u, v = pair[0], pair[1]
            edge_attrs["curved"] = (v, u) in edges
            u = self.nodes[u]
            v = self.nodes[v]
            edge = Edge(u, v, **update_without_overwrite(edge_attrs, kwargs))
            self.edges[edge.key] = edge
            self.add(edge)
Exemplo n.º 3
0
 def __init__(self, *mobjects, **kwargs):
     start = Group(*mobjects)
     target = Group(*[
         m1.copy().move_to(m2)
         for m1, m2 in adjacent_pairs(start)
     ])
     Transform.__init__(self, start, target, **kwargs)
Exemplo n.º 4
0
 def show_random_results(self):
     group = Group(*[
         Group(*[
             MNistMobject(a) for a in network.get_activation_of_all_layers(
                 np.random.randn(784, 1))
         ]).arrange_submobjects(RIGHT) for x in range(10)
     ]).arrange_submobjects(DOWN)
     group.scale_to_fit_height(FRAME_HEIGHT - 1)
     self.add(group)
Exemplo n.º 5
0
 def get_activation_images(self, digit, network, test_data, n_examples=8):
     input_vectors = [data[0] for data in test_data if data[1] == digit]
     activation_iamges = Group(*[
         Group(*[
             MNistMobject(a)
             for a in network.get_activation_of_all_layers(vect)
         ]).arrange_submobjects(RIGHT)
         for vect in input_vectors[:n_examples]
     ]).arrange_submobjects(DOWN)
     activation_iamges.scale_to_fit_height(FRAME_HEIGHT - 1)
     return activation_iamges
Exemplo n.º 6
0
    def repeat_cursor_1(self, target_depth=-1):
        cursor = self.cursors[target_depth]
        anims = [ApplyMethod(cursor.shift, self.repeat_cursor_dist * LEFT)]

        # remove child cursors
        anims.append(Uncreate(Group(*self.cursors[target_depth + 1:])))
        self.cursors = self.cursors[:target_depth + 1]
        self.cursor_indices = self.cursor_indices[:target_depth + 1]

        return anims
Exemplo n.º 7
0
 def show_all_activation_images(self, network, test_data):
     image_samples = Group(*[
         self.get_activation_images(digit, network, test_data)
         for digit in range(10)
     ])
     image_samples.arrange_submobjects_in_grid(n_rows=2, buff=LARGE_BUFF)
     image_samples.scale_to_fit_height(FRAME_HEIGHT - 1)
     self.add(image_samples)
Exemplo n.º 8
0
 def show_test_input(self, network):
     training_data, validation_data, test_data = load_data_wrapper()
     group = Group(*[
         self.get_set(network, test) for test in test_data[3:20]
         if test[1] in [4, 9]
     ])
     group.arrange_submobjects(DOWN, buff=MED_LARGE_BUFF)
     group.scale_to_fit_height(FRAME_HEIGHT - 1)
     self.play(FadeIn(group))
Exemplo n.º 9
0
    def __init__(self, pi_creature, *content, **kwargs):
        digest_config(self, kwargs)
        bubble = pi_creature.get_bubble(*content,
                                        bubble_class=self.bubble_class,
                                        **self.bubble_kwargs)
        Group(bubble, bubble.content).shift_onto_screen()

        pi_creature.generate_target()
        pi_creature.target.change_mode(self.target_mode)
        if self.look_at_arg is not None:
            pi_creature.target.look_at(self.look_at_arg)

        change_mode = MoveToTarget(pi_creature, **self.change_mode_kwargs)
        bubble_creation = self.bubble_creation_class(
            bubble, **self.bubble_creation_kwargs)
        content_introduction = self.content_introduction_class(
            bubble.content, **self.content_introduction_kwargs)
        AnimationGroup.__init__(self, change_mode, bubble_creation,
                                content_introduction, **kwargs)
Exemplo n.º 10
0
 def place_cursors(self, indices=None, block=None):
     """
     place cursors on the blocks denoted by indices if it was passed,
     otherwise the blocks denoted by self.cursor_indices
     """
     if indices is None and len(self.cursor_indices) == 0:
         raise Exception("no indices to use")
     elif indices is None:
         indices = self.cursor_indices
     if block is None:
         cur_block = self
     else:
         cur_block = block
     cursors = []
     for block_idx in indices:
         cur_block = cur_block.submobjects[block_idx]
         cursor = TexMobject("\\blacktriangleright")
         cursor.next_to(cur_block[0], LEFT)
         cursors.append(cursor)
     self.cursors.extend(cursors)
     return [ShowCreation(Group(*cursors))]
Exemplo n.º 11
0
class Succession(Animation):
    CONFIG = {
        "rate_func": None,
    }

    def __init__(self, *args, **kwargs):
        """
        Each arg will either be an animation, or an animation class
        followed by its arguments (and potentially a dict for
        configuration).
        For example,
        Succession(
            ShowCreation(circle),
            Transform, circle, square,
            Transform, circle, triangle,
            ApplyMethod, circle.shift, 2*UP, {"run_time" : 2},
        )
        """
        animations = []
        state = {
            "animations": animations,
            "curr_class": None,
            "curr_class_args": [],
            "curr_class_config": {},
        }

        def invoke_curr_class(state):
            if state["curr_class"] is None:
                return
            anim = state["curr_class"](*state["curr_class_args"],
                                       **state["curr_class_config"])
            state["animations"].append(anim)
            anim.update(1)
            state["curr_class"] = None
            state["curr_class_args"] = []
            state["curr_class_config"] = {}

        for arg in args:
            if isinstance(arg, Animation):
                animations.append(arg)
                arg.update(1)
                invoke_curr_class(state)
            elif isinstance(arg, type) and issubclass(arg, Animation):
                invoke_curr_class(state)
                state["curr_class"] = arg
            elif isinstance(arg, dict):
                state["curr_class_config"] = arg
            else:
                state["curr_class_args"].append(arg)
        invoke_curr_class(state)
        for anim in animations:
            anim.update(0)

        animations = [x for x in animations if not (x.empty)]

        self.run_times = [anim.run_time for anim in animations]
        if "run_time" in kwargs:
            run_time = kwargs.pop("run_time")
            warnings.warn(
                "Succession doesn't currently support explicit run_time.")
        run_time = sum(self.run_times)
        self.num_anims = len(animations)
        if self.num_anims == 0:
            self.empty = True
        self.animations = animations
        # Have to keep track of this run_time, because Scene.play
        # might very well mess with it.
        self.original_run_time = run_time

        # critical_alphas[i] is the start alpha of self.animations[i]
        # critical_alphas[i + 1] is the end alpha of self.animations[i]
        critical_times = np.concatenate(([0], np.cumsum(self.run_times)))
        self.critical_alphas = [
            np.true_divide(x, run_time) for x in critical_times
        ] if self.num_anims > 0 else [0.0]

        # self.scene_mobjects_at_time[i] is the scene's mobjects at start of self.animations[i]
        # self.scene_mobjects_at_time[i + 1] is the scene mobjects at end of self.animations[i]
        self.scene_mobjects_at_time = [None for i in range(self.num_anims + 1)]
        self.scene_mobjects_at_time[0] = Group()
        for i in range(self.num_anims):
            self.scene_mobjects_at_time[
                i + 1] = self.scene_mobjects_at_time[i].copy()
            self.animations[i].clean_up(self.scene_mobjects_at_time[i + 1])

        self.current_alpha = 0
        # If self.num_anims == 0, this is an invalid index, but so it goes
        self.current_anim_index = 0
        if self.num_anims > 0:
            self.mobject = self.scene_mobjects_at_time[0]
            self.mobject.add(self.animations[0].mobject)
        else:
            self.mobject = Group()

        Animation.__init__(self, self.mobject, run_time=run_time, **kwargs)

    # Beware: This does NOT take care of calling update(0) on the subanimation.
    # This was important to avoid a pernicious possibility in which subanimations were called
    # with update twice, which could in turn call a sub-Succession with update four times,
    # continuing exponentially.
    def jump_to_start_of_anim(self, index):
        if index != self.current_anim_index:
            # Should probably have a cleaner "remove_all" method...
            self.mobject.remove(*self.mobject.submobjects)
            self.mobject.add(*self.scene_mobjects_at_time[index].submobjects)
            self.mobject.add(self.animations[index].mobject)

        for i in range(index):
            self.animations[i].update(1)

        self.current_anim_index = index
        self.current_alpha = self.critical_alphas[index]

    def update_mobject(self, alpha):
        if self.num_anims == 0:
            # This probably doesn't matter for anything, but just in case,
            # we want it in the future, we set current_alpha even in this case
            self.current_alpha = alpha
            return

        gt_alpha_iter = iter(
            filter(lambda i: self.critical_alphas[i + 1] >= alpha,
                   range(self.num_anims)))
        i = next(gt_alpha_iter, None)
        if i is None:
            # In this case, we assume what is happening is that alpha is 1.0,
            # but that rounding error is causing us to overshoot the end of
            # self.critical_alphas (which is also 1.0)
            if not abs(alpha - 1) < 0.001:
                warnings.warn(
                    "Rounding error not near alpha=1 in Succession.update_mobject,"
                    + "instead alpha = %f" % alpha)
                print(self.critical_alphas, alpha)
            i = self.num_anims - 1

        # At this point, we should have self.critical_alphas[i] <= alpha <= self.critical_alphas[i +1]

        self.jump_to_start_of_anim(i)
        sub_alpha = inverse_interpolate(self.critical_alphas[i],
                                        self.critical_alphas[i + 1], alpha)
        self.animations[i].update(sub_alpha)
        self.current_alpha = alpha

    def clean_up(self, *args, **kwargs):
        # We clean up as though we've played ALL animations, even if
        # clean_up is called in middle of things
        for anim in self.animations:
            anim.clean_up(*args, **kwargs)
Exemplo n.º 12
0
class Succession(Animation):
    CONFIG = {
        "rate_func": None,
    }

    def __init__(self, *args, **kwargs):
        """
        Each arg will either be an animation, or an animation class
        followed by its arguments (and potentially a dict for
        configuration).
        For example,
        Succession(
            ShowCreation(circle),
            Transform, circle, square,
            Transform, circle, triangle,
            ApplyMethod, circle.shift, 2*UP, {"run_time" : 2},
        )
        """
        animations = []
        state = {
            "animations": animations,
            "curr_class": None,
            "curr_class_args": [],
            "curr_class_config": {},
        }

        def invoke_curr_class(state):
            if state["curr_class"] is None:
                return
            anim = state["curr_class"](
                *state["curr_class_args"],
                **state["curr_class_config"]
            )
            state["animations"].append(anim)
            anim.update(1)
            state["curr_class"] = None
            state["curr_class_args"] = []
            state["curr_class_config"] = {}

        for arg in args:
            if isinstance(arg, Animation):
                animations.append(arg)
                arg.update(1)
                invoke_curr_class(state)
            elif isinstance(arg, type) and issubclass(arg, Animation):
                invoke_curr_class(state)
                state["curr_class"] = arg
            elif isinstance(arg, dict):
                state["curr_class_config"] = arg
            else:
                state["curr_class_args"].append(arg)
        invoke_curr_class(state)
        for anim in animations:
            anim.update(0)

        animations = filter(lambda x: not(x.empty), animations)

        self.run_times = [anim.run_time for anim in animations]
        if "run_time" in kwargs:
            run_time = kwargs.pop("run_time")
            warnings.warn(
                "Succession doesn't currently support explicit run_time.")
        run_time = sum(self.run_times)
        self.num_anims = len(animations)
        if self.num_anims == 0:
            self.empty = True
        self.animations = animations
        # Have to keep track of this run_time, because Scene.play
        # might very well mess with it.
        self.original_run_time = run_time

        # critical_alphas[i] is the start alpha of self.animations[i]
        # critical_alphas[i + 1] is the end alpha of self.animations[i]
        critical_times = np.concatenate(([0], np.cumsum(self.run_times)))
        self.critical_alphas = map(lambda x: np.true_divide(
            x, run_time), critical_times) if self.num_anims > 0 else [0.0]

        # self.scene_mobjects_at_time[i] is the scene's mobjects at start of self.animations[i]
        # self.scene_mobjects_at_time[i + 1] is the scene mobjects at end of self.animations[i]
        self.scene_mobjects_at_time = [None for i in range(self.num_anims + 1)]
        self.scene_mobjects_at_time[0] = Group()
        for i in range(self.num_anims):
            self.scene_mobjects_at_time[i +
                                        1] = self.scene_mobjects_at_time[i].copy()
            self.animations[i].clean_up(self.scene_mobjects_at_time[i + 1])

        self.current_alpha = 0
        # If self.num_anims == 0, this is an invalid index, but so it goes
        self.current_anim_index = 0
        if self.num_anims > 0:
            self.mobject = self.scene_mobjects_at_time[0]
            self.mobject.add(self.animations[0].mobject)
        else:
            self.mobject = Group()

        Animation.__init__(self, self.mobject, run_time=run_time, **kwargs)

    # Beware: This does NOT take care of calling update(0) on the subanimation.
    # This was important to avoid a pernicious possibility in which subanimations were called
    # with update twice, which could in turn call a sub-Succession with update four times,
    # continuing exponentially.
    def jump_to_start_of_anim(self, index):
        if index != self.current_anim_index:
            # Should probably have a cleaner "remove_all" method...
            self.mobject.remove(*self.mobject.submobjects)
            self.mobject.add(*self.scene_mobjects_at_time[index].submobjects)
            self.mobject.add(self.animations[index].mobject)

        for i in range(index):
            self.animations[i].update(1)

        self.current_anim_index = index
        self.current_alpha = self.critical_alphas[index]

    def update_mobject(self, alpha):
        if self.num_anims == 0:
            # This probably doesn't matter for anything, but just in case,
            # we want it in the future, we set current_alpha even in this case
            self.current_alpha = alpha
            return

        gt_alpha_iter = it.ifilter(
            lambda i: self.critical_alphas[i + 1] >= alpha,
            range(self.num_anims)
        )
        i = next(gt_alpha_iter, None)
        if i is None:
            # In this case, we assume what is happening is that alpha is 1.0,
            # but that rounding error is causing us to overshoot the end of
            # self.critical_alphas (which is also 1.0)
            if not abs(alpha - 1) < 0.001:
                warnings.warn(
                    "Rounding error not near alpha=1 in Succession.update_mobject," +
                    "instead alpha = %f" % alpha
                )
                print self.critical_alphas, alpha
            i = self.num_anims - 1

        # At this point, we should have self.critical_alphas[i] <= alpha <= self.critical_alphas[i +1]

        self.jump_to_start_of_anim(i)
        sub_alpha = inverse_interpolate(
            self.critical_alphas[i],
            self.critical_alphas[i + 1],
            alpha
        )
        self.animations[i].update(sub_alpha)
        self.current_alpha = alpha

    def clean_up(self, *args, **kwargs):
        # We clean up as though we've played ALL animations, even if
        # clean_up is called in middle of things
        for anim in self.animations:
            anim.clean_up(*args, **kwargs)
Exemplo n.º 13
0
 def __init__(self, *continual_animations, **kwargs):
     digest_config(self, kwargs, locals())
     self.group = Group(*[ca.mobject for ca in continual_animations])
     ContinualAnimation.__init__(self, self.group, **kwargs)
Exemplo n.º 14
0
    def __init__(self, *args, **kwargs):
        """
        Each arg will either be an animation, or an animation class
        followed by its arguments (and potentially a dict for
        configuration).
        For example,
        Succession(
            ShowCreation(circle),
            Transform, circle, square,
            Transform, circle, triangle,
            ApplyMethod, circle.shift, 2*UP, {"run_time" : 2},
        )
        """
        animations = []
        state = {
            "animations": animations,
            "curr_class": None,
            "curr_class_args": [],
            "curr_class_config": {},
        }

        def invoke_curr_class(state):
            if state["curr_class"] is None:
                return
            anim = state["curr_class"](
                *state["curr_class_args"],
                **state["curr_class_config"]
            )
            state["animations"].append(anim)
            anim.update(1)
            state["curr_class"] = None
            state["curr_class_args"] = []
            state["curr_class_config"] = {}

        for arg in args:
            if isinstance(arg, Animation):
                animations.append(arg)
                arg.update(1)
                invoke_curr_class(state)
            elif isinstance(arg, type) and issubclass(arg, Animation):
                invoke_curr_class(state)
                state["curr_class"] = arg
            elif isinstance(arg, dict):
                state["curr_class_config"] = arg
            else:
                state["curr_class_args"].append(arg)
        invoke_curr_class(state)
        for anim in animations:
            anim.update(0)

        animations = filter(lambda x: not(x.empty), animations)

        self.run_times = [anim.run_time for anim in animations]
        if "run_time" in kwargs:
            run_time = kwargs.pop("run_time")
            warnings.warn(
                "Succession doesn't currently support explicit run_time.")
        run_time = sum(self.run_times)
        self.num_anims = len(animations)
        if self.num_anims == 0:
            self.empty = True
        self.animations = animations
        # Have to keep track of this run_time, because Scene.play
        # might very well mess with it.
        self.original_run_time = run_time

        # critical_alphas[i] is the start alpha of self.animations[i]
        # critical_alphas[i + 1] is the end alpha of self.animations[i]
        critical_times = np.concatenate(([0], np.cumsum(self.run_times)))
        self.critical_alphas = map(lambda x: np.true_divide(
            x, run_time), critical_times) if self.num_anims > 0 else [0.0]

        # self.scene_mobjects_at_time[i] is the scene's mobjects at start of self.animations[i]
        # self.scene_mobjects_at_time[i + 1] is the scene mobjects at end of self.animations[i]
        self.scene_mobjects_at_time = [None for i in range(self.num_anims + 1)]
        self.scene_mobjects_at_time[0] = Group()
        for i in range(self.num_anims):
            self.scene_mobjects_at_time[i +
                                        1] = self.scene_mobjects_at_time[i].copy()
            self.animations[i].clean_up(self.scene_mobjects_at_time[i + 1])

        self.current_alpha = 0
        # If self.num_anims == 0, this is an invalid index, but so it goes
        self.current_anim_index = 0
        if self.num_anims > 0:
            self.mobject = self.scene_mobjects_at_time[0]
            self.mobject.add(self.animations[0].mobject)
        else:
            self.mobject = Group()

        Animation.__init__(self, self.mobject, run_time=run_time, **kwargs)
Exemplo n.º 15
0
 def get_set(self, network, test):
     test_in, test_out = test
     activations = network.get_activation_of_all_layers(test_in)
     group = Group(*map(MNistMobject, activations))
     group.arrange_submobjects(RIGHT, buff=LARGE_BUFF)
     return group
Exemplo n.º 16
0
 def __init__(self, *args, **kwargs):
     return Animation.__init__(self, Group(), *args, **kwargs)
Exemplo n.º 17
0
 def show_maximizing_inputs(self, network):
     training_data, validation_data, test_data = load_data_wrapper()
     layer = 1
     n_neurons = DEFAULT_LAYER_SIZES[layer]
     groups = Group()
     for k in range(n_neurons):
         out = np.zeros(n_neurons)
         out[k] = 1
         in_vect = maximizing_input(network, layer, out)
         new_out = network.get_activation_of_all_layers(in_vect)[layer]
         group = Group(*map(MNistMobject, [in_vect, new_out]))
         group.arrange_submobjects(DOWN + RIGHT, SMALL_BUFF)
         groups.add(group)
     groups.arrange_submobjects_in_grid()
     groups.scale_to_fit_height(FRAME_HEIGHT - 1)
     self.add(groups)
Exemplo n.º 18
0
    def __init__(self, *args, **kwargs):
        """
        Each arg will either be an animation, or an animation class
        followed by its arguments (and potentially a dict for
        configuration).
        For example,
        Succession(
            ShowCreation(circle),
            Transform, circle, square,
            Transform, circle, triangle,
            ApplyMethod, circle.shift, 2*UP, {"run_time" : 2},
        )
        """
        animations = []
        state = {
            "animations": animations,
            "curr_class": None,
            "curr_class_args": [],
            "curr_class_config": {},
        }

        def invoke_curr_class(state):
            if state["curr_class"] is None:
                return
            anim = state["curr_class"](*state["curr_class_args"],
                                       **state["curr_class_config"])
            state["animations"].append(anim)
            anim.update(1)
            state["curr_class"] = None
            state["curr_class_args"] = []
            state["curr_class_config"] = {}

        for arg in args:
            if isinstance(arg, Animation):
                animations.append(arg)
                arg.update(1)
                invoke_curr_class(state)
            elif isinstance(arg, type) and issubclass(arg, Animation):
                invoke_curr_class(state)
                state["curr_class"] = arg
            elif isinstance(arg, dict):
                state["curr_class_config"] = arg
            else:
                state["curr_class_args"].append(arg)
        invoke_curr_class(state)
        for anim in animations:
            anim.update(0)

        animations = [x for x in animations if not (x.empty)]

        self.run_times = [anim.run_time for anim in animations]
        if "run_time" in kwargs:
            run_time = kwargs.pop("run_time")
            warnings.warn(
                "Succession doesn't currently support explicit run_time.")
        run_time = sum(self.run_times)
        self.num_anims = len(animations)
        if self.num_anims == 0:
            self.empty = True
        self.animations = animations
        # Have to keep track of this run_time, because Scene.play
        # might very well mess with it.
        self.original_run_time = run_time

        # critical_alphas[i] is the start alpha of self.animations[i]
        # critical_alphas[i + 1] is the end alpha of self.animations[i]
        critical_times = np.concatenate(([0], np.cumsum(self.run_times)))
        self.critical_alphas = [
            np.true_divide(x, run_time) for x in critical_times
        ] if self.num_anims > 0 else [0.0]

        # self.scene_mobjects_at_time[i] is the scene's mobjects at start of self.animations[i]
        # self.scene_mobjects_at_time[i + 1] is the scene mobjects at end of self.animations[i]
        self.scene_mobjects_at_time = [None for i in range(self.num_anims + 1)]
        self.scene_mobjects_at_time[0] = Group()
        for i in range(self.num_anims):
            self.scene_mobjects_at_time[
                i + 1] = self.scene_mobjects_at_time[i].copy()
            self.animations[i].clean_up(self.scene_mobjects_at_time[i + 1])

        self.current_alpha = 0
        # If self.num_anims == 0, this is an invalid index, but so it goes
        self.current_anim_index = 0
        if self.num_anims > 0:
            self.mobject = self.scene_mobjects_at_time[0]
            self.mobject.add(self.animations[0].mobject)
        else:
            self.mobject = Group()

        Animation.__init__(self, self.mobject, run_time=run_time, **kwargs)