2019-03-12 20:29:23 -07:00
|
|
|
from big_ol_pile_of_manim_imports import *
|
|
|
|
|
|
|
|
|
|
|
|
class Pendulum(VGroup):
|
|
|
|
CONFIG = {
|
|
|
|
"length": 3,
|
|
|
|
"gravity": 9.8,
|
|
|
|
"weight_diameter": 0.5,
|
2019-03-16 22:13:55 -07:00
|
|
|
"initial_theta": 0.3,
|
|
|
|
"omega": 0,
|
|
|
|
"damping": 0.1,
|
|
|
|
"top_point": 2 * UP,
|
2019-03-12 20:29:23 -07:00
|
|
|
"rod_style": {
|
2019-03-16 22:13:55 -07:00
|
|
|
"stroke_width": 3,
|
2019-03-12 20:29:23 -07:00
|
|
|
"stroke_color": LIGHT_GREY,
|
2019-03-16 22:13:55 -07:00
|
|
|
"sheen_direction": UP,
|
|
|
|
"sheen_factor": 1,
|
2019-03-12 20:29:23 -07:00
|
|
|
},
|
|
|
|
"weight_style": {
|
|
|
|
"stroke_width": 0,
|
|
|
|
"fill_opacity": 1,
|
2019-03-16 22:13:55 -07:00
|
|
|
"fill_color": DARK_GREY,
|
2019-03-12 20:29:23 -07:00
|
|
|
"sheen_direction": UL,
|
|
|
|
"sheen_factor": 0.5,
|
2019-03-16 22:13:55 -07:00
|
|
|
},
|
|
|
|
"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,
|
2019-03-12 20:29:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
super().__init__(**kwargs)
|
2019-03-16 22:13:55 -07:00
|
|
|
self.create_fixed_point()
|
2019-03-12 20:29:23 -07:00
|
|
|
self.create_rod()
|
2019-03-16 22:13:55 -07:00
|
|
|
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)
|
2019-03-12 20:29:23 -07:00
|
|
|
|
|
|
|
def create_rod(self):
|
2019-03-16 22:13:55 -07:00
|
|
|
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)
|
2019-03-12 20:29:23 -07:00
|
|
|
|
|
|
|
def create_weight(self):
|
2019-03-16 22:13:55 -07:00
|
|
|
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():
|
|
|
|
vector = Vector(0.5 * self.get_omega() * RIGHT)
|
|
|
|
self.velocity_vector = always_redraw(make_vector)
|
|
|
|
self.add(self.velocity_vector)
|
|
|
|
return self
|
2019-03-12 20:29:23 -07:00
|
|
|
|
2019-03-16 22:13:55 -07:00
|
|
|
#
|
2019-03-12 20:29:23 -07:00
|
|
|
def get_theta(self):
|
2019-03-16 22:13:55 -07:00
|
|
|
theta = self.rod.get_angle() - self.dashed_line.get_angle()
|
|
|
|
theta = (theta + PI) % TAU - PI
|
|
|
|
return theta
|
2019-03-12 20:29:23 -07:00
|
|
|
|
2019-03-16 22:13:55 -07:00
|
|
|
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
|
2019-03-12 20:29:23 -07:00
|
|
|
|
2019-03-16 22:13:55 -07:00
|
|
|
def get_omega(self):
|
|
|
|
return self.omega
|
2019-03-12 20:29:23 -07:00
|
|
|
|
2019-03-16 22:13:55 -07:00
|
|
|
def set_omega(self, omega):
|
|
|
|
self.omega = omega
|
|
|
|
return self
|
2019-03-12 20:29:23 -07:00
|
|
|
|
|
|
|
def get_fixed_point(self):
|
2019-03-16 22:13:55 -07:00
|
|
|
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
|
2019-03-12 20:29:23 -07:00
|
|
|
|
|
|
|
|
|
|
|
class PendulumTest(Scene):
|
|
|
|
def construct(self):
|
2019-03-16 22:13:55 -07:00
|
|
|
pendulum = Pendulum(
|
|
|
|
initial_theta=150 * DEGREES,
|
|
|
|
)
|
|
|
|
self.add(pendulum)
|
|
|
|
pendulum.start_swinging()
|
|
|
|
self.wait(10)
|