본문 바로가기

Programming/Manim Project

[컴포넌트]VerticalBarChart 만들기 (3. 사용법)

반응형

기본 사용법

아무 파라미터 없이 VerticalBarChart를 만들면, VerticalBarChart의 CONFIG에 지정되어 있는 샘플 데이터 및 파라미터 값으로 디폴트 바 차트가 만들어집니다. 

 

단, 이때 is_add_bars=True로 해줘야 막대가 같이 나타납니다. 

 

is_add_bars=False가 디폴트로, chart를 add하면 막대를 제외한 나머지가 화면에 표출되고, 막대는 chart.get_bar_animation()에 의해 얻어지는 애니메이션에 의해 표출됩니다.
from manimlib.imports import *
from src.comp.plot import VerticalBarChart

class SimpleTest(Scene):
    def construct(self):
        chart = VerticalBarChart(is_add_bars=True)
        self.add(chart)
        self.wait()

 

 

데이터 지정해서 막대 차트 만들기

데이터는 (막대 이름, 막대 값, 막대 칼라)인 튜플값이 모인 리스트로 전달해 주면 됩니다.

 

data = [
            ("Mullet", 30, RED), ("Mackerel", 50, RED),
            ("Salmon", 80, RED), ("Shark", 60, RED), ("Whale", 40, RED),
        ]
        
chart = VerticalBarChart(data=data)        

 

d5 = [("A", 30, RED), ("B", 50, RED), ("C", 80, RED), ("D", 60, RED), ("E", 40, RED), ]
chart = VerticalBarChart(data=d5)

 

여러 가지 파라미터 지정해서 바 차트 만들기

CONFIG에 있는 모든 파라미터의 지정이 가능합니다.

 

    CONFIG = {
        "is_add_bars": False, # False: not add, later want to do animation
        "title": "Bar Chart", # or None
        "title_color": WHITE,
        "title_size": 0.6,
        "title_buff": 0.5,
        "title_font": None,

        "x_axis_length": 10,
        "x_min":0,
        "x_max":100,

        "x_axis_title": 'X values',
        "x_axis_title_color": WHITE,
        "x_axis_title_size": 0.4,
        "x_axis_title_buff": 0.2,

        "y_axis_title": 'Y values',
        "y_axis_title_color": WHITE,
        "y_axis_title_size": 0.4,
        "y_axis_title_buff": 0.2,

        "y_min": 0,
        "y_max": 100,
        "y_tick_frequency": 20,
        "y_axis_length": 5,


        "y_labels_color": WHITE,
        "y_labels_size": 0.3,
        "y_labels_buff": 0.2,
        "y_labels_decimal_places": 0,

        "graph_origin": np.array([-5,-2, 0]),

        "bar_names": ["A", "B", "C", "D", "E"],
        "bar_names_color": WHITE,
        "bar_names_buff": 0.2,
        "bar_names_size": 0.3,

        "bar_values": [30, 50, 80, 60, 40],
        "bar_values_position": UP, # UP: above the bar,  DOWN: below the bar
        "bar_values_buff": 0.3,  #buff to the bar edge
        "gap_ratio": 0.2,  # gap ratio with the rectangle's width -> gap_width / rect_width
        "edge_buff": 0.5,  # for the space between x_axis's start and first rectangle, last rect and the end

        "bar_colors": [RED, RED, RED, RED, RED],
        "bar_fill_opacity": 1.0,
        "bar_stroke_width": 0,
    }

 

 

 

지정 방법은,

 

        bar_chart = VerticalBarChart(
            data=data,
            graph_origin=np.array([-5.5, -2, 0]),
            x_axis_length=6.5, y_axis_length=4.5,
            bar_names_size=0.25,
            title=None,
            is_add_bars=is_add_bars,
            gap_ratio=gap_ratio,
            bar_values_position=bar_values_position,
        )

 

샘플 예제

여러 가지 파라미터를 지정하면서 바 차트를 만들어본 예제입니다. 

 

여기 있는 코드들을 참조하면 가능한 대부분의 VerticalBarChart 사용법을 알 수 있을 것입니다.

 

 

위 동영상에 대한 소스 코드는,

#plottest.py
from manimlib.imports import *
from src.comp.plot import VerticalBarChart

class VerticalBarChartTest(Scene):
    CONFIG = {
        "camera_config": {"background_color": DARKER_GREY},
        # "camera_config": {"background_image": "C:\\now\manim\\res\\grey_background.png"},
    }
    def construct(self):
        self.introduction()
        self.one_chart_from_data()
        self.two_several_data()
        self.three_bar_width()
        self.four_bar_color()
        self.five_animation()
        self.six_bar_values()
        self.seven_shrink_move()
        self.the_end()

    def get_text(self, str, color=WHITE, size=0.25):
        return Text(str, font='맑은 고딕', stroke_width=0, color=color, size=size)

    def get_title_texts(self, *str):
        texts = VGroup(*[self.get_text(s, color=YELLOW) for s in str])
        texts.arrange(DOWN, aligned_edge=LEFT)
        texts.shift(RIGHT*4).to_edge(UP, buff=1.5)
        return texts

    def get_chart(self, data, is_add_bars=False, gap_ratio=0.2, bar_values_position=UP,):
        bar_chart = VerticalBarChart(
            data=data,
            graph_origin=np.array([-5.5, -2, 0]),
            x_axis_length=6.5, y_axis_length=4.5,
            bar_names_size=0.25,
            title=None,
            is_add_bars=is_add_bars,
            gap_ratio=gap_ratio,
            bar_values_position=bar_values_position,
        )
        return bar_chart

    def introduction(self):
        t1 = self.get_text("Introduction to VerticalBarPloat in manim", size=0.4)
        t2 = self.get_text("by Heejin", size=0.3)
        title = VGroup(t1, t2)

        title.arrange(DOWN).to_edge(UP, buff=1)
        self.play(FadeInFrom(title, UP), run_time=2)
        self.wait()
        self.play(FadeOut(title))

    def one_chart_from_data(self):
        t1 = "HorizontalBarChart shows Bar Chart"
        t2 = "using the given data of turple"
        title = self.get_title_texts(t1, t2)

        s1 = self.get_text(r"data = [")
        s2 = self.get_text(r"('Mullet',30,RED),('Mackerel',50,RED),")
        s3 = self.get_text(r"('Salmon',80,RED),('Shark',60,RED),")
        s4 = self.get_text(r"('Whale',40,RED),")
        s5 = self.get_text(r"]")
        s6 = self.get_text(r"chart = VerticalBarChart(data)")
        ss = VGroup(s1,s2,s3,s4,s5,s6)
        ss.arrange(DOWN, aligned_edge=LEFT)
        s2.shift(RIGHT*0.2)
        s3.shift(RIGHT * 0.2)
        s4.shift(RIGHT * 0.2)
        ss.next_to(title, DOWN, buff=1)


        data = [
            ("Mullet", 30, RED), ("Mackerel", 50, RED),
            ("Salmon", 80, RED), ("Shark", 60, RED),
            ("Whale", 40, RED),
        ]
        bar_chart = VerticalBarChart(data,
                                 graph_origin = np.array([-5.5,-2,0]),
                                 x_axis_length=6.5, y_axis_length=4.5,
                                 bar_names_size=0.25,
                                 title=None,
                                 is_add_bars=False, )
        bar_animation = bar_chart.get_bar_animation()

        self.play(LaggedStart(
            ShowCreation(title),
            ShowCreation(ss),
            FadeIn(bar_chart),
            bar_animation,
        ))

        self.wait(3)
        self.play(
            FadeOut(title),
            FadeOut(ss),
            FadeOut(bar_chart),
        )
        bar_chart.fadeout_bars(self)
        self.wait()

    def two_several_data(self):
        t1 = "Number of bars changes automatically "
        t2 = "according to the given data number"
        title = self.get_title_texts(t1, t2)

        s1 = self.get_text("The number of data:")
        decimal = DecimalNumber(2, num_decimal_places=0 )

        s1.next_to(title, DOWN, aligned_edge=LEFT, buff=1)
        decimal.next_to(s1, RIGHT)

        d2 = [("A", 30, RED), ("B", 50, RED),]
        d3 = [("A", 30, RED), ("B", 50, RED), ("C", 80, RED),  ]
        d4 = [("A", 30, RED), ("B", 50, RED), ("C", 80, RED), ("D", 60, RED),]
        d5 = [("A", 30, RED), ("B", 50, RED), ("C", 80, RED), ("D", 60, RED),("E", 40, RED), ]
        d6 = [("A", 30, RED), ("B", 50, RED), ("C", 80, RED), ("D", 60, RED),("E", 40, RED),("F", 20, RED), ]
        d7 = [("A", 30, RED), ("B", 50, RED), ("C", 80, RED), ("D", 60, RED),("E", 40, RED),("F", 20, RED),("G", 70, RED), ]
        d8 = [("A", 30, RED), ("B", 50, RED), ("C", 80, RED), ("D", 60, RED),("E", 40, RED),("F", 20, RED),("G", 70, RED),("H", 50, RED), ]
        data = [d2, d3, d4, d5, d6, d7, d8, ]

        charts = [self.get_chart(d, is_add_bars=True) for d in data]

        self.play(
            ShowCreation(title),
            ShowCreation(s1),
            ShowCreation(decimal),
            FadeInFromDown(charts[0]),
        )

        for i in range(1,7):
            self.play(
                decimal.set_value, i+2,
                ReplacementTransform(charts[i-1], charts[i], run_time=1.5),
            )

        self.wait(2)

        self.play(
            FadeOut(title),
            FadeOut(s1),
            FadeOut(decimal),
            FadeOut(charts[6]),
        )

    def three_bar_width(self):
        t1 = "Bar's width can be changed "
        t2 = "with the ratio of the width and gap "
        t3 = "between the bars "
        title = self.get_title_texts(t1, t2, t3)

        s1 = self.get_text("gap_ratio:")
        decimal = DecimalNumber(0.1, num_decimal_places=1)

        s1.next_to(title, DOWN, aligned_edge=LEFT, buff=1)
        decimal.next_to(s1, RIGHT)

        d5 = [("A", 30, RED), ("B", 50, RED), ("C", 80, RED), ("D", 60, RED), ("E", 40, RED), ]
        chart = self.get_chart(d5, is_add_bars=True, gap_ratio=0.1)

        self.play(
            FadeInFrom(chart),
            ShowCreation(title),
            FadeIn(s1),
            FadeIn(decimal),
            run_time=2,
        )


        vt = ValueTracker(1, num_decimal_places=1)

        def update_func(mob):
            new_chart = self.get_chart(d5, is_add_bars=True, gap_ratio=vt.get_value()/10)
            chart.become(new_chart)

        chart.add_updater(update_func)
        decimal.add_updater(lambda m: m.set_value(vt.get_value()/10))

        self.play(
            vt.set_value, 15,
            run_time=12,
            rate_func=there_and_back,
        )

        self.wait(2)

        self.play(
            FadeOut(title),
            FadeOut(s1),
            FadeOut(decimal),
            FadeOut(chart),
        )

    def four_bar_color(self):
        t1 = "Bar's color can be changed "
        t2 = "at once or it can be changed "
        t3 = "one by one. "
        title = self.get_title_texts(t1, t2, t3)

        s1 = self.get_text("color:")

        color_strs = ["RED","ORANGE","YELLOW","GREEN","BLUE","PURPLE","different one by one", "different one by one"]
        color_texts= VGroup(*[self.get_text(str) for str in color_strs])

        s1.next_to(title, DOWN, aligned_edge=LEFT, buff=1)
        color_texts.next_to(s1, RIGHT)

        d_red = [("A", 30, RED), ("B", 50, RED), ("C", 80, RED), ("D", 60, RED), ("E", 40, RED), ]
        d_orange = [("A", 30, ORANGE), ("B", 50, ORANGE), ("C", 80, ORANGE), ("D", 60, ORANGE), ("E", 40, ORANGE), ]
        d_yellow = [("A", 30, YELLOW), ("B", 50, YELLOW), ("C", 80, YELLOW), ("D", 60, YELLOW), ("E", 40, YELLOW), ]
        d_green = [("A", 30, GREEN), ("B", 50, GREEN), ("C", 80, GREEN), ("D", 60, GREEN), ("E", 40, GREEN), ]
        d_blue = [("A", 30, BLUE), ("B", 50, BLUE), ("C", 80, BLUE), ("D", 60, BLUE), ("E", 40, BLUE), ]
        d_purple = [("A", 30, PURPLE), ("B", 50, PURPLE), ("C", 80, PURPLE), ("D", 60, PURPLE), ("E", 40, PURPLE), ]
        d_rainbow1 = [("A", 30, RED), ("B", 50, ORANGE), ("C", 80, YELLOW), ("D", 60, GREEN), ("E", 40, BLUE), ]
        d_rainbow2 = [("A", 30, BLUE), ("B", 50, GREEN), ("C", 80, GREY), ("D", 60, RED), ("E", 40, ORANGE), ]
        data = [d_red, d_orange, d_yellow, d_green, d_blue, d_purple, d_rainbow1, d_rainbow2 ]

        charts = [self.get_chart(d, is_add_bars=True) for d in data]

        self.play(
            FadeInFrom(charts[0]),
            ShowCreation(title),
            FadeIn(s1),
            FadeIn(color_texts[0]),
            run_time=2,
        )

        n = len(charts)
        for i in range(1, n):
            self.play(
                ReplacementTransform(color_texts[i-1], color_texts[i], run_time=1),
                ReplacementTransform(charts[i - 1], charts[i], run_time=1.5),
            )

        self.wait(2)

        self.play(
            FadeOut(title),
            FadeOut(s1),
            FadeOut(color_texts[n-1]),
            FadeOut(charts[n-1]),
        )

    def five_animation(self):
        t1 = "Supports all animations for entire chart. "
        t2 = "Additionally there are two animations "
        t3 = "especially for the bars. "
        t4 = "(1) GrowFromEdgePoint "
        t5 = "(2) GrowFromBottomLine "
        title = self.get_title_texts(t1, t2, t3, t4, t5)

        s1 = self.get_text("animation:")

        ani_strs = ["GrowFromEdgePoint","GrowFromBottomLine"]
        ani_texts= VGroup(*[self.get_text(str, color=RED) for str in ani_strs])

        s1.next_to(title, DOWN, aligned_edge=LEFT, buff=1)
        ani_texts.next_to(s1, RIGHT)

        data = [
            ("Mullet", 30, RED), ("Mackerel", 50, RED),
            ("Salmon", 80, RED), ("Shark", 60, RED), ("Whale", 40, RED),
        ]
        chart1 = self.get_chart(data)

        self.play(
            ShowCreation(title),
            FadeInFromDown(chart1, run_time=3)
        )
        self.wait(2)

        ani1 = chart1.get_bar_animation(ani_type='GrowFromEdgePoint')
        self.play(
            FadeIn(s1),
            ShowCreation(ani_texts[0]),
            ani1,
        )
        self.play(ani1)
        self.wait()
        chart1.remove_bars(self)

        #ani2
        ani2 = chart1.get_bar_animation()
        self.play(
            ReplacementTransform(ani_texts[0], ani_texts[1]),
            ani2,
        )
        self.play(ani2)

        self.wait(2)
        chart1.fadeout_bars(self)
        self.play(
            FadeOut(title),
            FadeOut(s1),
            FadeOut(ani_texts[1]),
            FadeOut(chart1),
        )

    def six_bar_values(self):
        t1 = "The number value of bars "
        t2 = "can be displayed over the bar "
        t3 = "or inside bar. "

        title = self.get_title_texts(t1, t2, t3)

        s1 = self.get_text("bar_values_position:")

        pos_strs = ["UP","DOWN"]
        pos_texts= VGroup(*[self.get_text(str, color=RED) for str in pos_strs])

        s1.next_to(title, DOWN, aligned_edge=LEFT, buff=1)
        pos_texts.next_to(s1, RIGHT)

        data = [
            ("Mullet", 30, RED), ("Mackerel", 50, RED),
            ("Salmon", 80, RED), ("Shark", 60, RED), ("Whale", 40, RED),
        ]
        chart1 = self.get_chart(data, is_add_bars=False)
        chart2 = self.get_chart(data, is_add_bars=False, bar_values_position=DOWN)

        self.play(
            ShowCreation(title),
            FadeInFromDown(chart1, run_time=3)
        )
        self.wait(2)

        ani1 = chart1.get_bar_animation()
        self.play(
            FadeIn(s1),
            ShowCreation(pos_texts[0]),
            ani1,
        )
        self.play(ani1)
        self.wait()
        chart1.remove_bars(self)
        self.remove(chart1)
        self.add(chart2)

        #ani2
        ani2 = chart2.get_bar_animation()
        self.play(
            ReplacementTransform(pos_texts[0], pos_texts[1]),
            ani2,
        )
        self.play(ani2)

        self.wait(2)
        chart2.fadeout_bars(self)
        self.play(
            FadeOut(title),
            FadeOut(s1),
            FadeOut(pos_texts[1]),
            FadeOut(chart2),
        )

    def seven_shrink_move(self):
        t1 = "Because VerticalBarChart class inherits VGroup, "
        t2 = "it can be scaled and moved as a Mobject. "

        title = self.get_title_texts(t1, t2)

        data = [
            ("Mullet", 30, RED), ("Mackerel", 50, RED),
            ("Salmon", 80, RED), ("Shark", 60, RED), ("Whale", 40, RED),
        ]
        chart = self.get_chart(data, is_add_bars=True)
        chart.save_state()

        self.play(
            ShowCreation(title),
            FadeInFromDown(chart, run_time=2)
        )
        self.wait(2)

        #1. scale
        vt = ValueTracker(0)
        chart.add_updater(lambda m: m.scale((10-vt.get_value())/10))

        self.play(
            vt.set_value, 7,
            rate_func=there_and_back,
            run_time=4,
        )
        chart.restore()

        #2. rotation
        chart.scale(0.5)
        self.play(
            chart.rotate, 150*DEGREES,
            rate_func=there_and_back,
            run_time = 5,
        )

        #3. move
        self.play(chart.to_edge, LEFT,)
        path = ArcBetweenPoints(LEFT * 4 + UP * 1.5, RIGHT * 4 + DOWN * 2, angle=PI / 2, stroke_width=8)
        self.play(MoveAlongPath(chart, path), rate_func=there_and_back, run_time=6)

        chart.restore()
        self.wait(2)

        self.play(
            FadeOut(title),
            FadeOut(chart),
        )

    def the_end(self):
        t1 = self.get_text("Introduction to VerticalBarPloat in manim", size=0.4)
        t2 = self.get_text("by Heejin", size=0.3)
        title = VGroup(t1, t2)

        s = self.get_text("- The End - ", size=0.6)

        title.arrange(DOWN).to_edge(UP, buff=1)
        self.play(FadeInFrom(title, UP), run_time=1)
        self.wait()

        self.play(FadeIn(s), run_time=3)

        self.wait(2)

 

-끝-

반응형