Search code examples
arraysmanim

is there a better approach to visualize an array?


I want to make sure the approach I'm using to display an array isn't an awkward work-around for a better approach I'm unaware of. A lot of what I have so far feels pretty awkward.

For spacing elements I set each element of a TextMobject array .next_to the previous element to the right with a buffer. Is there a better approach?

For boxes around elements I was going to draw the top and bottom lines the full width of the array separately, and then draw vertical lines between elements. I don't envision caring about animating the drawing of these boxes in different ways, though I do plan to highlight elements using the existing tools for that. Is there a better approach?

Here's my "hello world" code so far. Any suggestions/improvements more than welcomed!

class ExampleArray(Scene):
    def construct(self):
        n = 6
        labels = TextMobject(* ["[" + str(index) + "]" for index in range(n)] )
        text = TextMobject( *[str(random.randint(0,10)) for i in range(n) ] )

        # space it out
        for i in range(1,len(text)):
            text[i].next_to(text[i-1], RIGHT, buff = 0.8)

        text.move_to(ORIGIN)


        for i in range(len(labels)):
            labels[i].scale(0.5)
            labels[i].next_to( text[i], DOWN, buff = 0.5)

        # can I just make a copy of top_line?
        top_line = Line(text.get_left() + LEFT, text.get_right() + RIGHT)
        bottom_line = Line(text.get_left() + LEFT, text.get_right() + RIGHT)
        top_line.next_to(text, UP)
        bottom_line.next_to(text, DOWN)

        self.add(labels)
        self.add(top_line, bottom_line)

        for i in range(len(text)):
            self.play(Write(text[i]))
            self.wait(0.1)

Solution

  • This is the way I would do it. I recommend you consider the following:

    1. Always modularize your code when you don't know if you are going to have to reuse it.
    2. It is always better to read vertically than to read horizontally.
    3. Avoid using indexes (use them when really necessary)
    4. Use the CONFIG dictionary to generalize variables that you will use throughout the scene.
    5. A shorter code is not synonymous with a better code, there must always be a balance between:
      • Easy to read
      • Easy to maintain
      • Easy to scale
    class ExampleArray2(Scene):
        CONFIG = {
            "array_len": 6,
            "random_seed": 1, # with this you force to manim use other numbers
            # Change the random_seed to other number every time you want other 
            # random numbers
        }
        def construct(self):
            labels = TextMobject(*[
                f"[{index}]" 
                for index in range(self.array_len)
            ])
            text = TextMobject(*[
                str(random.randint(0,10))
                for i in range(self.array_len)
            ])
            # space it out
            text.arrange(RIGHT,buff=0.8)
            # See: https://github.com/3b1b/manim/blob/master/manimlib/mobject/mobject.py#L936
    
            for label,t in zip(labels,text): # I like to avoid using indexes
                label.scale(0.5)
                label.next_to(t,DOWN,buff=0.5)
    
            up_and_down_line = self.get_up_and_down_line(
                VGroup(text,labels),
                buff=0.7, # Distance between numbers and lines
                scale=1.4 # Scale of the line
            )
    
            self.play(
                *list(map(lambda x: Write(x,run_time=2),[text,labels])),
                *list(map(GrowFromCenter,up_and_down_line))
            )
            self.wait()
    
        def get_long_line(self,mob,y_direction,buff=0.5,scale=1):
            return Line(
                mob.get_corner(y_direction + LEFT), 
                mob.get_corner(y_direction + RIGHT)
            ).shift(y_direction*buff).scale(scale)
    
        def get_up_and_down_line(self,mob,**kwargs):
            return VGroup(
                self.get_long_line(mob,UP,**kwargs),
                self.get_long_line(mob,DOWN,**kwargs)
            )
    

    enter image description here