Files
Grant Sanderson c31aef2689 Working pendulum
2019-03-17 04:04:40 -07:00

209 lines
5.8 KiB
Python

from big_ol_pile_of_manim_imports import *
class Pendulum(VGroup):
CONFIG = {
"length": 3,
"gravity": 9.8,
"weight_diameter": 0.5,
"initial_theta": 0.3,
"omega": 0,
"damping": 0.1,
"top_point": 2 * UP,
"rod_style": {
"stroke_width": 3,
"stroke_color": LIGHT_GREY,
"sheen_direction": UP,
"sheen_factor": 1,
},
"weight_style": {
"stroke_width": 0,
"fill_opacity": 1,
"fill_color": DARK_GREY,
"sheen_direction": UL,
"sheen_factor": 0.5,
},
"dashed_line_config": {
"num_dashes": 25,
"stroke_color": WHITE,
"stroke_width": 2,
},
"angle_arc_config": {
"radius": 1,
"stroke_color": WHITE,
"stroke_width": 2,
},
"velocity_vector_config": {
"color": RED,
},
"n_steps_per_frame": 10,
}
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.create_fixed_point()
self.create_rod()
self.create_weight()
self.rotating_group = VGroup(self.rod, self.weight)
self.create_dashed_line()
self.create_angle_arc()
self.set_theta(self.initial_theta)
self.update()
def create_fixed_point(self):
self.fixed_point_tracker = VectorizedPoint(self.top_point)
def create_rod(self):
rod = self.rod = Line(UP, DOWN)
rod.set_height(self.length)
rod.set_style(**self.rod_style)
rod.move_to(self.get_fixed_point(), UP)
self.add(rod)
def create_weight(self):
weight = self.weight = Circle()
weight.set_width(self.weight_diameter)
weight.set_style(**self.weight_style)
weight.move_to(self.rod.get_end())
self.add(weight)
def create_dashed_line(self):
line = self.dashed_line = DashedLine(
self.get_fixed_point(),
self.get_fixed_point() + self.length * DOWN,
**self.dashed_line_config
)
self.add_to_back(line)
def create_angle_arc(self):
self.angle_arc = always_redraw(lambda: Arc(
arc_center=self.get_fixed_point(),
start_angle=-90 * DEGREES,
angle=self.get_theta(),
**self.angle_arc_config,
))
self.add(self.angle_arc)
def add_velocity_vector(self):
def make_vector():
omega = self.get_omega()
theta = self.get_theta()
vector = Vector(
0.5 * omega * RIGHT,
**self.velocity_vector_config,
)
vector.rotate(theta, about_point=ORIGIN)
vector.shift(self.rod.get_end())
return vector
self.velocity_vector = always_redraw(make_vector)
self.add(self.velocity_vector)
return self
def add_theta_label(self):
label = self.theta_label = TexMobject("\\theta")
def update_label(l):
top = self.get_fixed_point()
arc_center = self.angle_arc.point_from_proportion(0.5)
l.move_to(top + 1.3 * (arc_center - top))
label.add_updater(update_label)
self.add(label)
#
def get_theta(self):
theta = self.rod.get_angle() - self.dashed_line.get_angle()
theta = (theta + PI) % TAU - PI
return theta
def set_theta(self, theta):
self.rotating_group.rotate(
theta - self.get_theta()
)
self.rotating_group.shift(
self.get_fixed_point() - self.rod.get_start(),
)
return self
def get_omega(self):
return self.omega
def set_omega(self, omega):
self.omega = omega
return self
def get_fixed_point(self):
return self.fixed_point_tracker.get_location()
#
def start_swinging(self):
self.add_updater(Pendulum.update_by_gravity)
def end_swinging(self):
self.remove_updater(Pendulum.update_by_gravity)
def update_by_gravity(self, dt):
theta = self.get_theta()
omega = self.get_omega()
nspf = self.n_steps_per_frame
for x in range(nspf):
d_theta = omega * dt / nspf
d_omega = op.add(
-self.damping * omega,
-(self.gravity / self.length) * np.sin(theta),
) * dt / nspf
theta += d_theta
omega += d_omega
self.set_theta(theta)
self.set_omega(omega)
return self
class GravityVector(Vector):
CONFIG = {
"color": YELLOW,
"length": 1,
}
def __init__(self, pendulum, **kwargs):
super().__init__(DOWN)
self.pendulum = pendulum
self.scale(self.length)
self.add_updater(lambda m: m.shift(
pendulum.rod.get_end() - self.get_start(),
))
def add_component_lines(self):
self.component_lines = always_redraw(self.create_component_lines)
self.add(self.component_lines)
def create_component_lines(self):
theta = self.pendulum.get_theta()
x_new = rotate(RIGHT, theta)
base = self.get_start()
tip = self.get_end()
vect = tip - base
corner = base + x_new * np.dot(vect, x_new)
kw = {"dash_length": 0.025}
return VGroup(
DashedLine(base, corner, **kw),
DashedLine(tip, corner, **kw),
)
class PendulumTest(Scene):
def construct(self):
pendulum = Pendulum(
initial_theta=150 * DEGREES,
)
pendulum.add_velocity_vector()
pendulum.add_theta_label()
gravity_vector = GravityVector(pendulum)
gravity_vector.add_component_lines()
self.add(pendulum, gravity_vector)
pendulum.start_swinging()
self.wait(10)