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.