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(): vector = Vector(0.5 * self.get_omega() * RIGHT) self.velocity_vector = always_redraw(make_vector) self.add(self.velocity_vector) return self # 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 PendulumTest(Scene): def construct(self): pendulum = Pendulum( initial_theta=150 * DEGREES, ) self.add(pendulum) pendulum.start_swinging() self.wait(10)