Skip to content

Instantly share code, notes, and snippets.

@nathanielbd
Created October 30, 2021 18:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save nathanielbd/25fb7b5a51535552bfa5fee62e49ebff to your computer and use it in GitHub Desktop.
Save nathanielbd/25fb7b5a51535552bfa5fee62e49ebff to your computer and use it in GitHub Desktop.
Manim animations to build intuition for Bowditch-Lissajous curves
from manim import *
class BowditchParametric(Scene):
def construct(self):
x_circle = Circle(radius=1.5).move_to(2*UP)
y_circle = Circle(radius=1).move_to(4*LEFT+2*DOWN)
x_dot = Dot(x_circle.point_from_proportion((2*PI-PI/9)/(2*PI)), radius=0.08, color=YELLOW)
y_dot = Dot(y_circle.point_from_proportion(0), radius=0.08, color=YELLOW)
self.t_offset = 0
rate = 0.25
def x_orbit(dot, dt):
self.t_offset += (dt * rate)
dot.move_to(x_circle.point_from_proportion((17/18+self.t_offset) % 1))
def y_orbit(dot, dt):
dot.move_to(y_circle.point_from_proportion((1-self.t_offset*2) % 1))
def x_radius():
return Line(2*UP, x_dot.get_center(), color=PURPLE)
def y_radius():
return Line(4*LEFT+2*DOWN, y_dot.get_center(), color=PURPLE)
self.curve = VGroup()
curve_start = np.array([x_dot.get_center()[0], y_dot.get_center()[1], 0])
self.curve.add(Line(curve_start, curve_start, color=BLUE))
def get_curve():
last_line = self.curve[-1]
x = x_dot.get_center()[0]
y = y_dot.get_center()[1]
new_line = Line(last_line.get_end(), np.array([x, y, 0]), color=BLUE)
self.curve.add(new_line)
return self.curve
def x_line_to_curve():
return Line(x_dot.get_center(), self.curve[-1].get_end(), stroke_width=2)
def y_line_to_curve():
return Line(y_dot.get_center(), self.curve[-1].get_end(), stroke_width=2)
curve = always_redraw(get_curve)
x_line = always_redraw(x_line_to_curve)
y_line = always_redraw(y_line_to_curve)
x_radius_line = always_redraw(x_radius)
y_radius_line = always_redraw(y_radius)
self.add(x_circle, y_circle, x_dot, y_dot)
self.add(curve, x_radius_line, y_radius_line)
delta_brace = ArcBrace(Arc(radius=1.5, start_angle=17*PI/9, angle=5*PI/9, arc_center=2*UP))
delta_label = MathTex(r'\delta=\frac{5\pi}{9}').next_to(delta_brace, RIGHT).shift(UP*0.5)
A_brace = Brace(x_radius_line)
A_label = A_brace.get_tex(r'A = 3')
B_brace = Brace(y_radius_line)
B_label = B_brace.get_tex(r'B = 2')
self.add(delta_brace, delta_label, A_brace, A_label, B_brace, B_label)
self.wait(1)
self.play(
FadeOut(delta_brace),
FadeOut(delta_label),
FadeOut(A_brace),
FadeOut(A_label),
FadeOut(B_brace),
FadeOut(B_label),
FadeIn(x_line),
FadeIn(y_line)
)
self.wait(1)
x_dot.add_updater(x_orbit)
y_dot.add_updater(y_orbit)
self.wait(6)
class Bowditch3D(ThreeDScene):
def construct(self):
curve = ParametricFunction(
lambda t: np.array([
3 * np.cos(t),
3 * np.sin(t),
2 * np.sin(2*t)
]), color=RED, t_range = np.array([0, 2*PI, 0.01])
).set_shade_in_3d(True)
def update_curve(d, dt):
d.rotate_about_origin(dt)
curve.add_updater(update_curve)
delta_label = MathTex(r'\delta')
delta_scale = Line(ORIGIN, RIGHT*2)
zero = MathTex(r'0')
two_pi = MathTex(r'2\pi')
self.add_fixed_in_frame_mobjects(delta_label, delta_scale, zero, two_pi)
delta_scale.to_corner(UL).shift(DOWN)
delta_label.next_to(delta_scale, UP)
zero.move_to(delta_scale.get_start()+DOWN*0.4)
two_pi.move_to(delta_scale.get_end()+DOWN*0.4)
delta_dial = Dot(delta_scale.get_start()).set_color(BLUE)
delta_belt = VMobject()
delta_belt.set_points_as_corners([delta_dial.get_center(), delta_dial.get_center()]).set_color(BLUE)
def update_belt(prev_belt, dt):
new_belt = prev_belt.copy().set_points_as_corners([delta_scale.get_start(), delta_dial.get_center()])
prev_belt.become(new_belt)
def update_dial(dial, dt):
dial.move_to(dial.get_center()+[dt/PI, 0, 0])
delta_dial.add_updater(update_dial)
delta_belt.add_updater(update_belt)
self.add_fixed_in_frame_mobjects(delta_belt, delta_dial)
axes = ThreeDAxes()
self.add(axes, curve)
self.set_camera_orientation(phi=80 * DEGREES, theta=-10*DEGREES)
self.begin_ambient_camera_rotation(rate=PI/18, about='phi')
self.begin_ambient_camera_rotation(rate=PI/18, about='theta')
self.wait(1)
self.stop_ambient_camera_rotation(about='phi')
self.stop_ambient_camera_rotation(about='theta')
self.set_camera_orientation(phi=90*DEGREES, theta=0)
self.wait(2*PI-1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment