본문 바로가기

Programming/Manim Project

원 위의 점이 돌면서 사인/코사인 곡선 그리기

반응형

사인(Sine)과 코사인(Cosine) 그래프가 왜 그렇게 그려지는지 설명할 때 많이 쓰이는 방법은 다음과 같다.

 

  • 반지름이 1인 원을 그리고, 원의 중심에서 그 원둘레의 임의의 점까지의 직선을 그린 후, x축과 그 직선 간의 사잇각을  $ \theta $라 하자
  • 이 $ \theta $에 대해 $\sin \theta = 세로 $가 되고, $ \cos \theta = 가로$가 된다.  

 

 

 

  • 따라서, 그 어떤 점을 원둘레로 이동시키면, $ \theta $가 커지고, 이에 따라 $\sin \theta $의 값이 변하게 되는데, 이를 원 오른편에 가로축을 그리고, 그 위를 $\theta$의 변화값으로 두고, 세로축에 '세로 길이'를 표시하면, 이게 $ \sin \theta $ 값이 된다. 즉, 사인(Sine) 그래프가 된다.

 

  • 코사인에 대한 것은 $ \cos \theta $가 '가로' 값이기에, 원의 중심에서 아래 편으로 직선을 내려서 축 선으로 삼고, 이 축 선을 따라 $ \theta $의 값이 커지는 것으로 하고, 이 $\theta$값에 대한 '가로길이' 값을 그려나가면, 이게 $\cos \theta$ 그래프가 된다.


사인 곡선만 그리는 것은 여기 참조

 


프로그램에 대한 전체 스케치는,

 

 

먼저 축 라인과 원을 그리자

원을 중심으로 해서, 가로축은 사인 곡선을, 세로축에는 코사인 곡선을 나타낼 것이다.

class SineCosine_Curve(Scene):
    def construct(self):
        self.show_axis()
        self.show_circle()
        
        self.wait()
        
	def show_axis(self):
        x_start = np.array([-6,2,0])
        x_end = np.array([3,2,0])

        y_start = np.array([-4,-3,0])
        y_end = np.array([-4,3.5,0])

        x_axis = Line(x_start, x_end)
        y_axis = Line(y_start, y_end)

        self.add(x_axis, y_axis)
        self.add_xy_labels()

        self.orgin_point = np.array([-4,2,0])
        self.curve_start = np.array([-3,2,0])

    def add_xy_labels(self):
        x_labels = [
            TexMobject("\pi"), TexMobject("2 \pi"),
            TexMobject("3 \pi"), TexMobject("4 \pi"),
        ]

        y_labels = [
            TexMobject("\pi"), TexMobject("2 \pi"),
            TexMobject("3 \pi"), TexMobject("4 \pi"),
        ]

        for i in range(len(x_labels)):  # -2 -1 0 1
            x_labels[i].scale(0.6)
            x_labels[i].next_to(np.array([-2+i,2,0]), DOWN )
            self.add(x_labels[i])

        for i in range(len(y_labels)):  # 1 0 -1 -2
            y_labels[i].scale(0.6)
            y_labels[i].rotate(-PI/2)
            y_labels[i].next_to(np.array([-4, 1-i,0]), LEFT )
            self.add(y_labels[i])

    def show_circle(self):
        circle = Circle(radius=1)
        circle.move_to(self.orgin_point)

        self.add(circle)
        self.circle = circle

 

원 위의 점이 자동으로 움직이게 하자

원과 x축(가로 선)이 만나는 지점에 점을 그리고, 이 점이 원둘레를 따라 돌게 만들 것이다.

 

자동으로 점이 움직이게 하는 것은 dot.add_updater(go_around_circle)을 통해서 이루어진다.

    def move_dot_and_draw_curve(self):
        orbit = self.circle
        orgin_point = self.orgin_point

        dot = Dot(radius=0.08, color=YELLOW)
        dot.move_to(orbit.point_from_proportion(0))
        self.t_offset = 0
        rate = 0.25

        def go_around_circle(mob, dt):
            self.t_offset += (dt * rate)
            # print(self.t_offset)
            mob.move_to(orbit.point_from_proportion(self.t_offset % 1))

        dot.add_updater(go_around_circle) #move dot around the circle

        self.add(dot, orbit)
        self.wait(8.5)

        dot.remove_updater(go_around_circle)

 

 

점의 움직임에 따라, 사인/코사인 곡선이 그려지도록 하자

점(dot)이 움직임에 따라 다음 5개의 라인이 새로 그려지게 한다.

 

 

이에 해당하는 코드는 다음과 같다.

        origin_to_circle_line = always_redraw(get_line_to_circle) # from circle origin to dot

        dot_to_sine_line = always_redraw(get_line_to_sine) # from dot to sine curve
        sine_curve_line = always_redraw(get_sine_curve) # sine curve

        dot_to_cosine_line = always_redraw(get_line_to_cosine)  # from dot to cosine curve
        cosine_curve_line = always_redraw(get_cosine_curve)  # cosine curve

 

 

위 코드를 보면 always_redraw 함수를 이용하고 있는데, always_redraw 함수는 인자로 주어지는 메서드를 프레임이 바뀔 때마다 호출하고, 원래의 객체를 그 메서드가 리턴하는 객체로 교체한다. 

 

always_redraw의 인자로 사용되도록 코딩한 각 메서드는 다음과 같다.

 

        def go_around_circle(mob, dt):
            self.t_offset += (dt * rate)
            # print(self.t_offset)
            mob.move_to(orbit.point_from_proportion(self.t_offset % 1))

        def get_line_to_circle():
            return Line(orgin_point, dot.get_center(), color=BLUE)

        ### sine
        def get_line_to_sine():
            x = self.curve_start[0] + self.t_offset * 2
            y = dot.get_center()[1]
            return Line(dot.get_center(), np.array([x,y,0]), color=YELLOW_A, stroke_width=2 )

        self.sine_curve = VGroup()
        self.sine_curve.add(Line(self.curve_start,self.curve_start))
        def get_sine_curve():
            last_line = self.sine_curve[-1]
            x = self.curve_start[0] + self.t_offset * 2
            y = dot.get_center()[1]
            new_line = Line(last_line.get_end(),np.array([x,y,0]), color=YELLOW_D)
            self.sine_curve.add(new_line)

            return self.sine_curve

        ### cosine
        def get_line_to_cosine():
            x = dot.get_center()[0]
            y = self.curve_start[1] - self.t_offset * 2
            return Line(dot.get_center(), np.array([x,y,0]), color=YELLOW_A, stroke_width=2 )

        self.cosine_curve = VGroup()
        self.cosine_curve.add(Line(self.curve_start, self.curve_start))

        def get_cosine_curve():
            last_line = self.cosine_curve[-1]
            x = dot.get_center()[0]
            y = self.curve_start[1] - self.t_offset * 2
            new_line = Line(last_line.get_end(), np.array([x, y, 0]), color=YELLOW_D)
            self.cosine_curve.add(new_line)

            return self.cosine_curve

 


모든 사항을 반영한 코드 및 실행 동영상은 다음과 같다.

 

from manimlib.imports import *

class SineCosine_Curve(Scene):
    def construct(self):
        self.show_axis()
        self.show_circle()
        self.move_dot_and_draw_curve()

        self.wait()

    def show_axis(self):
        x_start = np.array([-6,2,0])
        x_end = np.array([3,2,0])

        y_start = np.array([-4,-3,0])
        y_end = np.array([-4,3.5,0])

        x_axis = Line(x_start, x_end)
        y_axis = Line(y_start, y_end)

        self.add(x_axis, y_axis)
        self.add_xy_labels()

        self.orgin_point = np.array([-4,2,0])
        self.curve_start = np.array([-3,2,0])

    def add_xy_labels(self):
        x_labels = [
            TexMobject("\pi"), TexMobject("2 \pi"),
            TexMobject("3 \pi"), TexMobject("4 \pi"),
        ]

        y_labels = [
            TexMobject("\pi"), TexMobject("2 \pi"),
            TexMobject("3 \pi"), TexMobject("4 \pi"),
        ]

        for i in range(len(x_labels)):  # -2 -1 0 1
            x_labels[i].scale(0.6)
            x_labels[i].next_to(np.array([-2+i,2,0]), DOWN )
            self.add(x_labels[i])

        for i in range(len(y_labels)):  # 1 0 -1 -2
            y_labels[i].scale(0.6)
            y_labels[i].rotate(-PI/2)
            y_labels[i].next_to(np.array([-4, 1-i,0]), LEFT )
            self.add(y_labels[i])

    def show_circle(self):
        circle = Circle(radius=1)
        circle.move_to(self.orgin_point)

        self.add(circle)
        self.circle = circle

    def move_dot_and_draw_curve(self):
        orbit = self.circle
        orgin_point = self.orgin_point

        dot = Dot(radius=0.08, color=YELLOW)
        dot.move_to(orbit.point_from_proportion(0))
        self.t_offset = 0
        rate = 0.25

        def go_around_circle(mob, dt):
            self.t_offset += (dt * rate)
            # print(self.t_offset)
            mob.move_to(orbit.point_from_proportion(self.t_offset % 1))

        def get_line_to_circle():
            return Line(orgin_point, dot.get_center(), color=BLUE)

        ### sine
        def get_line_to_sine():
            x = self.curve_start[0] + self.t_offset * 2
            y = dot.get_center()[1]
            return Line(dot.get_center(), np.array([x,y,0]), color=YELLOW_A, stroke_width=2 )

        self.sine_curve = VGroup()
        self.sine_curve.add(Line(self.curve_start,self.curve_start))
        def get_sine_curve():
            last_line = self.sine_curve[-1]
            x = self.curve_start[0] + self.t_offset * 2
            y = dot.get_center()[1]
            new_line = Line(last_line.get_end(),np.array([x,y,0]), color=YELLOW_D)
            self.sine_curve.add(new_line)

            return self.sine_curve

        ### cosine
        def get_line_to_cosine():
            x = dot.get_center()[0]
            y = self.curve_start[1] - self.t_offset * 2
            return Line(dot.get_center(), np.array([x,y,0]), color=YELLOW_A, stroke_width=2 )

        self.cosine_curve = VGroup()
        self.cosine_curve.add(Line(self.curve_start, self.curve_start))

        def get_cosine_curve():
            last_line = self.cosine_curve[-1]
            x = dot.get_center()[0]
            y = self.curve_start[1] - self.t_offset * 2
            new_line = Line(last_line.get_end(), np.array([x, y, 0]), color=YELLOW_D)
            self.cosine_curve.add(new_line)

            return self.cosine_curve


        dot.add_updater(go_around_circle) #move dot around the circle

        origin_to_circle_line = always_redraw(get_line_to_circle) # from circle origin to dot

        dot_to_sine_line = always_redraw(get_line_to_sine) # from dot to sine curve
        sine_curve_line = always_redraw(get_sine_curve) # sine curve

        dot_to_cosine_line = always_redraw(get_line_to_cosine)  # from dot to cosine curve
        cosine_curve_line = always_redraw(get_cosine_curve)  # cosine curve

        self.add(dot, orbit)
        self.add(origin_to_circle_line,
                 dot_to_sine_line, sine_curve_line,
                 dot_to_cosine_line, cosine_curve_line,
         )
        self.wait(8.5)

        dot.remove_updater(go_around_circle)

 

 

 

-끝-

반응형