Polar graph: manim animation of r=a sin (b theta)

Let’s look at the animation first:

The above animation shows pretty graph of r=2 sin( 3 theta). The manim scene class rendering the above video is following (download codes and raw videos used above ):

from manim import *
class sin_graph(Scene):
    def construct(self):
        value_tracker = ValueTracker(0.01) #Tracks the end value of both functions
        a_color = PINK
        b_color = BLUE_D
        th_color = RED_D
        r_color = YELLOW
        text_scaleUp=1.2
        text_scaleDown=1.0/text_scaleUp
        a=2
        b=3
        if b%2==0:
            period=2*PI
        else:
            period=PI
        sin_g=a_sin_b_theta(value_tracker,r_color,a,b) #a=2,b=3
        moving_line=MovingLine(value_tracker,sin_g.radius,th_color)
        self.play(Create(sin_g.plane))
        self.wait()
        self.play(Create(sin_g.title))
        self.wait()
        self.play(Create(sin_g.title[2].set_color(a_color)),
                  sin_g.title[2].animate.scale(text_scaleUp))
        self.wait()
        self.play(Create(sin_g.circle.set_color(a_color)),run_time=2)
        self.wait()
        self.play(sin_g.title[2].animate.scale(text_scaleDown))
        self.wait()
        self.play(sin_g.title[4].animate.set_color(b_color))
        self.play(sin_g.title[4].animate.scale(text_scaleUp))
        self.wait()
         self.play(Create(sin_g.leaf_lines.set_color(b_color)),run_time=3)
        self.wait()
        self.play(sin_g.title[4].animate.scale(text_scaleDown))
        self.wait()
        self.play(Create(sin_g.title[5].set_color(th_color)),
                  sin_g.title[5].animate.scale(text_scaleUp))
        self.wait()
        self.play(Create(moving_line.line_ref),run_time=2)
        self.wait()
        self.play(Create(sin_g.title[0].set_color(r_color)),
            sin_g.title[0].animate.scale(text_scaleUp*1.2))
        self.wait()
        self.add(moving_line.line_moving)
        self.add(sin_g)
        self.play(value_tracker.animate.set_value(period), run_time = 10, rate_func = linear)
        self.play(FadeOut(moving_line))
        self.play(FadeOut(sin_g.leaf_lines))
        self.play(FadeOut(sin_g.circle))
        self.play(FadeOut(sin_g.plane))
        self.wait()
        self.play(sin_g.title[0].animate.scale(text_scaleDown),
                  sin_g.title[5].animate.scale(text_scaleDown))
        self.play(sin_g.title.animate.set_color(r_color))
        self.play((sin_g.title).animate.next_to(sin_g.graph,UP))
        self.wait(2)

In above code, you can change the value of b to get different animations.

The the class “class sin_graph(Scene)” above use two more custom VMobjects defined below:

class MovingLine(VMobject):    
    def __init__(self,value_tracker,radius,th_color, **kwargs):
        super().__init__(**kwargs)
        self.radius=radius
        self.theta_tracker = value_tracker
        self.line_moving = Line(np.array([-self.radius,0,0]), np.array([self.radius,0,0]))
        self.line_ref =Line(np.array([-self.radius,0,0]), np.array([self.radius,0,0])).set_color(th_color)
        self.rotation_center = Dot(self.line_ref.get_center())  # Use Dot as rotation center
        
        # Update rotation center position whenever line_ref moves
        self.rotation_center.add_updater(lambda x: x.move_to(self.line_ref.get_center()))
        
        self.line_moving.add_updater(
            lambda x: x.become(self.line_ref.copy()).rotate(
                self.theta_tracker.get_value(), about_point=self.rotation_center.get_center()
            ).set_color(th_color)
        )
        self.add(self.line_ref, self.rotation_center,  self.line_moving)

and another one:

class a_sin_b_theta(VMobject):
    def __init__(self, value_tracker,r_color,a,b, **kwargs):
        super().__init__(**kwargs)
        self.radius=a*1.125
        background_line_style = {
            "stroke_color": BLUE_E,
            "stroke_width": 0.5,
            "stroke_opacity": 1,
        }
        
        self.plane = PolarPlane(radius_max=self.radius, size=self.radius*2,
                                azimuth_units="degrees",
                                azimuth_step=12,
                                background_line_style=background_line_style
                                ).add_coordinates()
        ax=self.plane.get_axes()
        ax.set_stroke(color=WHITE, width=0.5)
        self.graph = always_redraw(
            lambda: ParametricFunction(
                lambda t: self.plane.polar_to_point(a* np.sin(b* t), t),
                t_range=[0, value_tracker.get_value()],
                color=r_color,
            )
        )

        self.dot = always_redraw(
            lambda: Dot(
                fill_color=r_color, fill_opacity=0.8
            ).scale(0.8).move_to(self.graph.get_end())
        )
        self.title=VGroup()
        self.title.add(MathTex("r", color=WHITE))#0
        self.title.add(MathTex(" = ", color=WHITE))#1
        self.title.add(MathTex(str(a), color=WHITE)) #2
        self.title.add(MathTex("\\sin(", color=WHITE)) #3
        self.title.add(MathTex(str(b), color=WHITE))#4
        self.title.add(MathTex("\\theta", color=WHITE))#5
        self.title.add(MathTex(")", color=WHITE))#6
        self.title.arrange(RIGHT, buff=0.1)
        self.title.next_to(self.plane, UP, buff=0.05)


        self.graph.add_updater(
            lambda x: x.become(
                ParametricFunction(
                    lambda t: self.plane.polar_to_point(
                        a * np.sin(b * t), t
                    ),
                    t_range=[0, value_tracker.get_value()],
                    color=r_color,
                )
            )
        )

        self.dot.add_updater(
            lambda x: x.become(
                Dot(fill_color=r_color, fill_opacity=0.8)
                .scale(0.7)
                .move_to(self.graph.get_end())
            )
        )
        self.leaf_lines=VGroup()
   
        i=0
        its=[]
        while len(its)<b:
            it=(2*i+1)/(2*b)
            its.append(it)
            th=it*PI
            if b%2!=0:
                self.leaf_lines.add(Line(np.array([0,0,0]), self.plane.polar_to_point(a * np.sin(b* th), th)))
            else:
                self.leaf_lines.add(Line(self.plane.polar_to_point(-a * np.sin(b* th), th),
                                    self.plane.polar_to_point(a * np.sin(b* th), th)))
            i+=1
      
            
        self.circle=Circle(radius=a)
        self.add(self.graph, self.dot)

Happy manimations!

Hope you enjoy experimenting above codes.

Thanks.

Author: learnsharewithdp

I am a mechanical engineer, digital content creator, ,blogger, educator and excited to learn everyday.

Leave a comment