Created
October 30, 2021 18:51
-
-
Save nathanielbd/25fb7b5a51535552bfa5fee62e49ebff to your computer and use it in GitHub Desktop.
Manim animations to build intuition for Bowditch-Lissajous curves
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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