Animating Arrays#

Manim Array - MArray#

The most basic data structure this package provides is the MArray (short for Manim Array 😄). To create a MArray simply create an instance by passing a python list.

1class MyScene(Scene):
2    def construct(self):
3        arr = MArray(self, [1, 2, 3])
4        self.play(Create(arr))
5        self.wait(1)

Animating MArray#

To animate the MArray, simply invoke the animate property as shown below:

1self.play(arr.animate.shift(UP * 2 + LEFT * 5))

Moreover, you can also use the MArray.animate_elem() method to animate a single element of the MArray as well:

1self.play(arr.animate_elem(1).shift(DOWN))

Lastly, you can also animate the body, value and the index of any element using the MArray.animate_elem_square(), MArray.animate_elem_value() and MArray.animate_elem_index() respectively.

1self.play(
2    arr.animate_elem_square(1).set_fill(BLACK),
3    arr.animate_elem_value(1).set_fill(RED),
4    arr.animate_elem_index(1).rotate(PI / 2)
5)

Customizing MArray#

The MArray also allows you to alter the way your array looks. While creating your array pass arguments to Square (used to represent the element body) and Text (used to represent the element value and index) mobjects.

1arr = MArray(
2    self,
3    [1, 2, 3],
4    mob_square_args={'fill_color': RED_D},
5    mob_value_args={'color': BLACK},
6    mob_index_args={'color': GOLD_A}
7)

Growth Direction#

Furthermore, you can also create MArray that grows in different directions (e.g. up, down, right and left etc.).

To do this, simply pass your preferred direction enum from MArrayDirection as the arr_dir argument to the constructor. The code snippet below generates four different arrays in each direction.

 1class MyScene(Scene):
 2    def construct(self):
 3        arr_up = MArray(self, [1, 2], arr_dir=MArrayDirection.UP)
 4        arr_right = MArray(self, [3, 4], arr_dir=MArrayDirection.RIGHT)
 5        arr_down = MArray(self, [5, 6], arr_dir=MArrayDirection.DOWN)
 6        arr_left = MArray(self, [7, 8], arr_dir=MArrayDirection.LEFT)
 7
 8        self.play(Create(arr_up))
 9        self.play(arr_up.animate.shift(UP * 2))
10        self.play(Create(arr_right))
11        self.play(arr_right.animate.shift(RIGHT * 2))
12        self.play(Create(arr_down))
13        self.play(arr_down.animate.shift(DOWN * 2))
14        self.play(Create(arr_left))
15        self.play(arr_left.animate.shift(LEFT * 2))
16
17        self.wait(1)

Array Label#

For an MArray, you can also a label with the array via specifying the label argument.

Similar to how we specify the growth direction using MArrayDirection enum, we can dictate the position of the label.

 1class MyScene(Scene):
 2    def construct(self):
 3        arr_label_left = MArray(self, [1, 2, 3], label='Arr')
 4        arr_label_right = MArray(self, [1, 2, 3], label='Arr', arr_label_pos=MArrayDirection.RIGHT)
 5        arr_label_down = MArray(self, [1, 2, 3], label='Arr', arr_label_pos=MArrayDirection.DOWN)
 6        arr_label_up = MArray(self, [1, 2, 3], label='Arr', arr_label_pos=MArrayDirection.UP, arr_label_gap=0.75)
 7
 8        self.play(Create(arr_label_left))
 9        self.play(arr_label_left.animate.shift(UP * 2 + LEFT * 4))
10        self.play(Create(arr_label_right))
11        self.play(arr_label_right.animate.shift(DOWN * 2 + LEFT * 4))
12        self.play(Create(arr_label_down))
13        self.play(arr_label_down.animate.shift(UP * 2 + RIGHT))
14        self.play(Create(arr_label_up))
15        self.play(arr_label_up.animate.shift(DOWN * 2 + RIGHT))
16
17        self.wait(1)

Note

The arr_label_gap argument specifies the distance between the MArrayElement ‘s Square and the array label itself.

Hex Indices#

Lets say you want to show a 4-byte integer array with its addresses. You can simply achieve this by using index_hex_display and index_offset arguments of the MArray constructor.

 1class MyScene(Scene):
 2    def construct(self):
 3        arr = MArray(
 4            self,
 5            [1, 2, 3, 4],
 6            index_hex_display=True,
 7            index_offset=4
 8        )
 9        self.play(Create(arr))
10        self.wait(1)

Hide Indices#

Or if you don’t want to show the indices at all, simply pass True as the hide_index argument to the constructor

1class MyScene(Scene):
2    def construct(self):
3        arr = MArray(
4            self,
5            [1, 2, 3, 4],
6            hide_index=True
7        )
8        self.play(Create(arr))
9        self.wait(1)

Misc Functions#

The MArray provides some auxiliary methods which this secion will discuss.

Append Element#

For an existing array, you can also append an element simply by invoking the MArray.append_elem() method.

1class MyScene(Scene):
2    def construct(self):
3        arr = MArray(self, [1, 2, 3], label='Array', arr_label_pos=MArrayDirection.DOWN)
4        self.add(arr)
5        self.wait(1)
6        arr.append_elem(4)
7        self.wait(1)

Note

You can also pass mob_*_args to this method to customize the inserted element.

Moreover, you can also specify the animation that is played for the inserted element via the append_anim argument. The code snippet below passes the GrowFromCenter animation to the MArray.append_elem() method:

1arr.append_elem(4, append_anim=GrowFromCenter)

Note

You can also specify arguments to the passed animation via the append_anim_args parameter and also set the target of the animation using the append_anim_target parameter that takes in MArrayElementComp enum.

Did you notice that in both snippets, we didn’t pass any animation to our Scene but the append animation still played? This is thanks to the self that we pass as the first argument to our MArray constructor, which is basically a reference to the current Scene.

However, if you’d like to play the animation yourself, we have got you covered! The MArrayElement method returns a list of Animation that you can pass to the Scene.play() method as follows:

1self.play(*arr.append_elem(4, play_anim=False))

Remove Element#

To remove an element simply invoke the MArray.remove_elem() method with the index of element you wish to remove.

1arr.remove_elem(1)

Similar to how you were able to pass the append animation to the MArray.append_elem() function, you can specify two animations for the MArray.remove_elem() method:

  1. Element removal animation via the removal_anim parameter.

  2. Indices update animation via the update_anim parameter.

The code snippet below provides an example:

1arr.remove_elem(1, removal_anim=ShowPassingFlash , update_anim=Write)

Note

You can also specify arguments to the passed animation via the *_anim_args parameter and also set the target of the animation using the *_anim_target parameter.

Lastly, as the MArray.append_elem() returns a list of Animation, the MArray.remove_elem() returns two objects; a removal animation and a function that udpates the indices of the remaining elements and returns their animations. Hence, you can animate this as follows:

1(remove_anim, update_indices) = arr.remove_elem(1, removal_anim=ShowPassingFlash , update_anim=Write, play_anim=False)
2self.play(remove_anim) # Play removal animation first
3self.play(*update_indices(play_anim=False)) # Then play the update_indices animation

Update Element#

You can also update the value and the index of an existing array using the MArray.update_elem_value() and MArray.update_elem_index() methods respectively.

1class MyScene(Scene):
2    def construct(self):
3        arr = MArray(self, [1, 2, 3])
4        self.add(arr)
5        self.wait(1)
6        arr.update_elem_value(1, 20)
7        arr.update_elem_index(1, -2)
8        self.wait(1)

Note

You can also pass mob_value_args and mob_index_args to respective methods to customize the updated element mobject.

Using MArrayPointer#

Thus far, if you had been hoping for a pointer to associate with your array, then your prayers have been answered. The MArrayPointer allows you to attach a pointer with your array. The following snippet demonstrates its capabilities:

 1class MyScene(Scene):
 2    def construct(self):
 3        arr = MArray(self, [1, 2, 3, 4, 5], label='Array')
 4        arr.shift(UP + LEFT * 2)
 5        self.add(arr)
 6
 7        pointer = MArrayPointer(self, arr, 2, 'P')
 8        self.play(Create(pointer))
 9        self.wait(1)
10        pointer.shift_to_elem(4)
11        self.wait(1)
12        pointer.shift_to_elem(0)
13        self.wait(1)
14        pointer.attach_to_elem(2)
15
16        self.wait(1)

Using MArraySlidingWindow#

In addition to the MArrayPointer, we also have the MArraySlidingWindow that allows you to attach a sliding window with your array. The following snippet demonstrates its capabilities:

 1class MyScene(Scene):
 2    def construct(self):
 3        arr = MArray(self, [1, 2, 3, 4, 5], label='Array')
 4        arr.shift(UP + LEFT * 2)
 5        self.add(arr)
 6
 7        window = MArraySlidingWindow(self, arr, 1, 1, 'W')
 8        self.play(Create(window))
 9        self.wait(1)
10        window.shift_to_elem(2)
11        self.wait(1)
12        window.resize_window(3)
13        self.wait(1)
14        window.shift_to_elem(0)
15        self.wait(1)
16        window.resize_window(1)
17        self.wait(1)
18        window.attach_to_elem(2)
19
20        self.wait(1)

With this we conclude this guide. We hope you found it useful! ✌️