본문 바로가기

Programming/Manim Project

python의 for 루프 설명하기

반응형

파이썬의 for 루프를 설명하기 위한 애니메이션입니다.

 

설명하기 위한 for 루프를 사용한 파이썬 코드는 1에서 100까지 합을 구한는 코드

 

sum = 0
for i in range(1,101,1):
    sum = sum + i

 

애니메이션의 핵심은, 변수 i와 sum의 값이 for 루프를 돌면서 어떻게 변하는 지를 잘 보여주는 것.

 

이를 위해서 i값과 sum값이 차례로 위 쪽으로 이동하면서 변하는 객체를 만들어서 처리해야겠습니다. 사다리 모양이어서 객체 이름은 VarLadder로.  `변수를 처리하는 사다리` 정도의 의미 .^^

 

 

작성된 VarLadder 클래스에 대한 코드는 아래와 같습니다. 

 

class VarLadder(VGroup):
    CONFIG = {
        "rect_cnt": 3,
        "decimal_scale": 0.7,
        "rect_width": 0.5,
        "rect_height": 0.4
    }

    def __init__(self, **kwargs):
        digest_config(self, kwargs, locals())
        super().__init__(**kwargs)
        self.gen_objects()

    def gen_objects(self):
        def get_rect(idx):
            stroke_width = 0
            if idx == self.rect_cnt - 1:
                stroke_width = 3
            r = Rectangle(fill_opacity=0, stroke_width=stroke_width, stroke_opacity=0.8, stroke_color=GREY, )
            r.set_width(self.rect_width, stretch=True)
            r.set_height(self.rect_height, stretch=True)
            return r

        rects = VGroup(*[get_rect(i) for i in range(self.rect_cnt + 2)])
        rects.arrange(DOWN, buff=0)

        decimals=[]
        for i in range(1,4):
            decimal = self.get_decimals(0, rects[i], color=BLACK)
            decimals.append(decimal)

        self.add(rects, *decimals)
        self.rects = rects
        self.decimals = decimals
        self.cur_seq=0

    def get_decimals(self, num=0, rect=None, color=WHITE):
        t = DecimalNumber(num, num_decimal_places=0, color=color)
        t.scale(self.decimal_scale)
        if rect is not None:
            t.move_to(rect)
        return t

    def set_decimal(self, d0=None, d1=None, d2=None):
        d = self.decimals
        if d0 is not None:
            d[0].set_value(d0)
            d[0].set_color(WHITE)
        else:
            d[0].set_color(BLACK)

        if d1 is not None:
            d[1].set_value(d1)
            d[1].set_color(WHITE)
        else:
            d[1].set_color(BLACK)

        if d2 is not None:
            d[2].set_value(d2)
            d[2].set_color(WHITE)
        else:
            d[2].set_color(BLACK)

    def push(self, n, scene, run_time=1):
        d = self.decimals
        r = self.rects
        d_new = self.get_decimals(num=n, color=BLACK )
        d_new.move_to(self.rects[4])

        scene.play(
            d_new.set_color, WHITE,
            d_new.move_to, r[3],
            d[2].move_to, r[2],
            d[1].move_to, r[1],
            d[0].move_to, r[0],
            d[0].set_color, BLACK,
            run_time=run_time,
        )

        d[0] = d[1]
        d[1] = d[2]
        d[2] = d_new

    def push2(self, n, scene, run_time=1,):
        d = self.decimals
        r = self.rects
        d_new = self.get_decimals(num=n, color=BLACK)
        d_new.move_to(self.rects[3])

        scene.play(
            d_new.set_color, WHITE,
            d_new.move_to, r[2],
            d[1].move_to, r[1],
            d[0].move_to, r[0],
            d[0].set_color, BLACK,
            run_time=run_time,
        )

        d[0] = d[1]
        d[1] = d_new


class VarLadderTest(Scene):
    def construct(self):
        ladder = VarLadder()

        # ladder.set_decimal(None, 1, 2)
        # self.add(ladder)
        # self.wait()
        #
        # ladder.push(3, self)
        # ladder.push(4, self)
        # ladder.push(5, self)

        ladder.set_decimal(None,0,None)
        self.add(ladder)
        self.wait()

        ladder.push2(1, self)
        ladder.push2(3, self)
        ladder.push2(5, self)

 

좀 더 일반적으로 사용할 수 있는 컴포넌트로 만들고도 싶었으나, 시간이 없어서, 그냥 for 루프에서의 i와 sum에서 사용할 수 있는 정도의 기능만을 넣었습니다.

 

i 처럼 sequential하게 증가할 때 사용하는 메서드로 push를, sum처럼 sequential하지 않은 것은 push2 메서드를 사용하면 됩니다.

 


음성은 네이버 클로바 서비스를 이용해서 만들고, 이 파일을 manim에서 직접 호출하는 방식으로 만들어 봤습니다.

 

만들어진 동영상은,

 

 

전체 소스 코드는, 

class VarLadder(VGroup):
    CONFIG = {
        "rect_cnt": 3,
        "decimal_scale": 0.7,
        "rect_width": 0.5,
        "rect_height": 0.4
    }

    def __init__(self, **kwargs):
        digest_config(self, kwargs, locals())
        super().__init__(**kwargs)
        self.gen_objects()

    def gen_objects(self):
        def get_rect(idx):
            stroke_width = 0
            if idx == self.rect_cnt - 1:
                stroke_width = 3
            r = Rectangle(fill_opacity=0, stroke_width=stroke_width, stroke_opacity=0.8, stroke_color=GREY, )
            r.set_width(self.rect_width, stretch=True)
            r.set_height(self.rect_height, stretch=True)
            return r

        rects = VGroup(*[get_rect(i) for i in range(self.rect_cnt + 2)])
        rects.arrange(DOWN, buff=0)

        decimals=[]
        for i in range(1,4):
            decimal = self.get_decimals(0, rects[i], color=BLACK)
            decimals.append(decimal)

        self.add(rects, *decimals)
        self.rects = rects
        self.decimals = decimals
        self.cur_seq=0

    def get_decimals(self, num=0, rect=None, color=WHITE):
        t = DecimalNumber(num, num_decimal_places=0, color=color)
        t.scale(self.decimal_scale)
        if rect is not None:
            t.move_to(rect)
        return t

    def set_decimal(self, d0=None, d1=None, d2=None):
        d = self.decimals
        if d0 is not None:
            d[0].set_value(d0)
            d[0].set_color(WHITE)
        else:
            d[0].set_color(BLACK)

        if d1 is not None:
            d[1].set_value(d1)
            d[1].set_color(WHITE)
        else:
            d[1].set_color(BLACK)

        if d2 is not None:
            d[2].set_value(d2)
            d[2].set_color(WHITE)
        else:
            d[2].set_color(BLACK)

    def push(self, n, scene, run_time=1):
        d = self.decimals
        r = self.rects
        d_new = self.get_decimals(num=n, color=BLACK )
        d_new.move_to(self.rects[4])

        scene.play(
            d_new.set_color, WHITE,
            d_new.move_to, r[3],
            d[2].move_to, r[2],
            d[1].move_to, r[1],
            d[0].move_to, r[0],
            d[0].set_color, BLACK,
            run_time=run_time,
        )

        d[0] = d[1]
        d[1] = d[2]
        d[2] = d_new

    def push2(self, n, scene, run_time=1,):
        d = self.decimals
        r = self.rects
        d_new = self.get_decimals(num=n, color=BLACK)
        d_new.move_to(self.rects[3])

        scene.play(
            d_new.set_color, WHITE,
            d_new.move_to, r[2],
            d[1].move_to, r[1],
            d[0].move_to, r[0],
            d[0].set_color, BLACK,
            run_time=run_time,
        )

        d[0] = d[1]
        d[1] = d_new


class VarLadderTest(Scene):
    def construct(self):
        ladder = VarLadder()

        # ladder.set_decimal(None, 1, 2)
        # self.add(ladder)
        # self.wait()
        #
        # ladder.push(3, self)
        # ladder.push(4, self)
        # ladder.push(5, self)

        ladder.set_decimal(None,0,None)
        self.add(ladder)
        self.wait()

        ladder.push2(1, self)
        ladder.push2(3, self)
        ladder.push2(5, self)

 

여기에 사용된 음성 파일은, 

 

01_왼쪽에_코드가_있고_오른편에서_변수_i와_sum이_어떻게_변하는지_보여줄겁니다.mp3
0.08MB
02_먼저_첫번_째_코드가_실행되면_변수_아이의_값이_1이됩니다.mp3
0.07MB
03_두번_째_코드가_실행되면_원래_있던_썸_값인_영에다가_아이_값인_일을_더하면_합은_1.mp3
0.11MB
04_다시_포_루프로_돌아가서_아이가_하나_증가해서_2가_됩니다.mp3
0.06MB
05_이제_썸_값은_원래_있던_1에다가_아이값_2를_더해서_3이_됩니다.mp3
0.07MB
06_다시_포_루프로_돌아가서_아이가_하나_증가해서_3이_됩니다.mp3
0.06MB
07_썸_값은_원래_값_3에다가_아이값인_3을_더하면_6이_됩니다.mp3
0.07MB
08_대략_어떻게_되는지_아시겠죠.mp3
0.04MB
09_이런_과정을_100까지_하게되면_썸에는_5050이_들어가게됩니다.mp3
0.07MB
10_끝입니다요.mp3
0.02MB

-끝-

반응형