mirror of
https://github.com/3b1b/manim.git
synced 2025-08-01 06:22:54 +08:00
AnalyzePendulumForce
This commit is contained in:
@ -18,5 +18,6 @@ ALL_SCENE_CLASSES = [
|
|||||||
StrogatzQuote,
|
StrogatzQuote,
|
||||||
# Something...
|
# Something...
|
||||||
ShowGravityAcceleration,
|
ShowGravityAcceleration,
|
||||||
|
AnalyzePendulumForce,
|
||||||
BuildUpEquation,
|
BuildUpEquation,
|
||||||
]
|
]
|
||||||
|
@ -110,17 +110,21 @@ class Pendulum(VGroup):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def add_theta_label(self):
|
def add_theta_label(self):
|
||||||
label = self.theta_label = TexMobject("\\theta")
|
self.theta_label = always_redraw(self.get_label)
|
||||||
label.set_height(self.theta_label_height)
|
self.add(self.theta_label)
|
||||||
|
|
||||||
def update_label(l):
|
def get_label(self):
|
||||||
top = self.get_fixed_point()
|
label = TexMobject("\\theta")
|
||||||
arc_center = self.angle_arc.point_from_proportion(0.5)
|
label.set_height(min(
|
||||||
vect = arc_center - top
|
self.theta_label_height,
|
||||||
vect = normalize(vect) * (1 + self.theta_label_height)
|
self.angle_arc.get_width(),
|
||||||
l.move_to(top + vect)
|
))
|
||||||
label.add_updater(update_label)
|
top = self.get_fixed_point()
|
||||||
self.add(label)
|
arc_center = self.angle_arc.point_from_proportion(0.5)
|
||||||
|
vect = arc_center - top
|
||||||
|
vect = normalize(vect) * (1 + self.theta_label_height)
|
||||||
|
label.move_to(top + vect)
|
||||||
|
return label
|
||||||
|
|
||||||
#
|
#
|
||||||
def get_theta(self):
|
def get_theta(self):
|
||||||
@ -841,12 +845,12 @@ class VeryLowAnglePendulum(LowAnglePendulum):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class BuildUpEquation(MovingCameraScene):
|
class AnalyzePendulumForce(MovingCameraScene):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"pendulum_config": {
|
"pendulum_config": {
|
||||||
"length": 5,
|
"length": 5,
|
||||||
"top_point": 3 * UP,
|
"top_point": 3.5 * UP,
|
||||||
"initial_theta": 45 * DEGREES,
|
"initial_theta": 60 * DEGREES,
|
||||||
},
|
},
|
||||||
"g_vect_config": {
|
"g_vect_config": {
|
||||||
"length_multiple": 0.25,
|
"length_multiple": 0.25,
|
||||||
@ -859,15 +863,12 @@ class BuildUpEquation(MovingCameraScene):
|
|||||||
self.add_pendulum()
|
self.add_pendulum()
|
||||||
self.show_constraint()
|
self.show_constraint()
|
||||||
self.break_g_vect_into_components()
|
self.break_g_vect_into_components()
|
||||||
self.show_angle_geometry()
|
|
||||||
self.show_gsin_formula()
|
self.show_gsin_formula()
|
||||||
self.show_acceleration_at_different_angles()
|
self.show_acceleration_at_different_angles()
|
||||||
|
self.show_angle_geometry()
|
||||||
self.ask_about_what_to_do()
|
self.ask_about_what_to_do()
|
||||||
self.show_velocity_and_position()
|
# TODO, indication of theta?
|
||||||
self.show_derivatives()
|
# pendulum movement? Things like that?
|
||||||
self.show_equation()
|
|
||||||
self.talk_about_sine_component()
|
|
||||||
self.add_air_resistance()
|
|
||||||
|
|
||||||
def add_pendulum(self):
|
def add_pendulum(self):
|
||||||
self.pendulum = Pendulum(**self.pendulum_config)
|
self.pendulum = Pendulum(**self.pendulum_config)
|
||||||
@ -922,9 +923,9 @@ class BuildUpEquation(MovingCameraScene):
|
|||||||
ShowCreation(arc)
|
ShowCreation(arc)
|
||||||
)
|
)
|
||||||
arcs.add(arc)
|
arcs.add(arc)
|
||||||
pendulum.clear_updaters()
|
|
||||||
self.wait()
|
self.wait()
|
||||||
self.play(FadeOut(arc))
|
|
||||||
|
self.theta_tracker = theta_tracker
|
||||||
|
|
||||||
def break_g_vect_into_components(self):
|
def break_g_vect_into_components(self):
|
||||||
g_vect = self.g_vect
|
g_vect = self.g_vect
|
||||||
@ -945,25 +946,218 @@ class BuildUpEquation(MovingCameraScene):
|
|||||||
color=self.perp_line_color,
|
color=self.perp_line_color,
|
||||||
))
|
))
|
||||||
|
|
||||||
self.play(
|
self.play(ShowCreation(g_vect.component_lines))
|
||||||
ShowCreation(g_vect.component_lines),
|
|
||||||
)
|
|
||||||
self.play(GrowArrow(g_vect.tangent))
|
self.play(GrowArrow(g_vect.tangent))
|
||||||
self.wait()
|
self.wait()
|
||||||
self.play(GrowArrow(g_vect.perp))
|
self.play(GrowArrow(g_vect.perp))
|
||||||
self.wait()
|
self.wait()
|
||||||
|
|
||||||
def show_angle_geometry(self):
|
|
||||||
g_vect = self.g_vect
|
|
||||||
|
|
||||||
def show_gsin_formula(self):
|
def show_gsin_formula(self):
|
||||||
pass
|
g_vect = self.g_vect
|
||||||
|
g_word = self.g_word
|
||||||
|
g_word.clear_updaters()
|
||||||
|
|
||||||
|
g_term = self.g_term = TexMobject("-g")
|
||||||
|
g_term.add_updater(lambda m: m.next_to(
|
||||||
|
g_vect,
|
||||||
|
RIGHT * np.sign(self.pendulum.get_theta()),
|
||||||
|
SMALL_BUFF
|
||||||
|
))
|
||||||
|
|
||||||
|
def create_vect_label(vect, tex, direction):
|
||||||
|
label = TexMobject(tex)
|
||||||
|
label.set_stroke(BLACK, 5, background=True)
|
||||||
|
label.add_background_rectangle()
|
||||||
|
label.scale(0.7)
|
||||||
|
max_width = 0.9 * vect.get_length()
|
||||||
|
if label.get_width() > max_width:
|
||||||
|
label.set_width(max_width)
|
||||||
|
angle = vect.get_angle()
|
||||||
|
angle = (angle + PI / 2) % PI - PI / 2
|
||||||
|
label.next_to(ORIGIN, direction, SMALL_BUFF)
|
||||||
|
label.rotate(angle, about_point=ORIGIN)
|
||||||
|
label.shift(vect.get_center())
|
||||||
|
return label
|
||||||
|
|
||||||
|
g_sin_label = always_redraw(lambda: create_vect_label(
|
||||||
|
g_vect.tangent, "-g\\sin(\\theta)", UP,
|
||||||
|
))
|
||||||
|
g_cos_label = always_redraw(lambda: create_vect_label(
|
||||||
|
g_vect.perp, "-g\\cos(\\theta)", DOWN,
|
||||||
|
))
|
||||||
|
|
||||||
|
self.play(ReplacementTransform(g_word, g_term))
|
||||||
|
self.wait()
|
||||||
|
self.play(TransformFromCopy(g_term, g_sin_label))
|
||||||
|
self.wait()
|
||||||
|
self.play(TransformFromCopy(g_term, g_cos_label))
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
self.g_sin_label = g_sin_label
|
||||||
|
self.g_cos_label = g_cos_label
|
||||||
|
|
||||||
def show_acceleration_at_different_angles(self):
|
def show_acceleration_at_different_angles(self):
|
||||||
pass
|
to_fade = VGroup(
|
||||||
|
self.g_cos_label,
|
||||||
|
self.g_vect.perp,
|
||||||
|
self.g_vect.component_lines,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.play(to_fade.set_opacity, 0.25)
|
||||||
|
for mob in to_fade:
|
||||||
|
mob.add_updater(lambda m: m.set_opacity(0.25))
|
||||||
|
|
||||||
|
get_theta = self.pendulum.get_theta
|
||||||
|
theta_decimal = DecimalNumber(include_sign=True)
|
||||||
|
theta_decimal.add_updater(lambda d: d.set_value(
|
||||||
|
get_theta()
|
||||||
|
))
|
||||||
|
theta_decimal.add_updater(lambda m: m.next_to(
|
||||||
|
self.pendulum.theta_label, DOWN
|
||||||
|
))
|
||||||
|
theta_decimal.add_updater(lambda m: m.set_color(
|
||||||
|
GREEN if get_theta() > 0 else RED
|
||||||
|
))
|
||||||
|
|
||||||
|
self.set_theta(0)
|
||||||
|
self.wait(2)
|
||||||
|
self.set_theta(89.9 * DEGREES, run_time=3)
|
||||||
|
self.wait(2)
|
||||||
|
self.set_theta(60 * DEGREES, run_time=2)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
self.play(FadeInFrom(theta_decimal, UP))
|
||||||
|
self.set_theta(-60 * DEGREES, run_time=4)
|
||||||
|
self.set_theta(60 * DEGREES, run_time=4)
|
||||||
|
self.play(FadeOut(theta_decimal))
|
||||||
|
|
||||||
|
def show_angle_geometry(self):
|
||||||
|
g_vect = self.g_vect
|
||||||
|
vectors = VGroup(
|
||||||
|
g_vect, g_vect.tangent, g_vect.perp,
|
||||||
|
)
|
||||||
|
g_line = Line(
|
||||||
|
g_vect.get_start(),
|
||||||
|
g_vect.get_end(),
|
||||||
|
stroke_width=1,
|
||||||
|
stroke_color=WHITE,
|
||||||
|
)
|
||||||
|
|
||||||
|
arc = Arc(
|
||||||
|
start_angle=90 * DEGREES,
|
||||||
|
angle=self.pendulum.get_theta(),
|
||||||
|
radius=0.5,
|
||||||
|
arc_center=g_vect.get_end(),
|
||||||
|
)
|
||||||
|
q_mark = TexMobject("?")
|
||||||
|
q_mark.next_to(arc.get_center(), UL, SMALL_BUFF)
|
||||||
|
theta_label = TexMobject("\\theta")
|
||||||
|
theta_label.move_to(q_mark)
|
||||||
|
|
||||||
|
opposite = g_vect.component_lines[0].copy()
|
||||||
|
adjascent = g_line.copy()
|
||||||
|
opposite.set_stroke(BLUE, 5, opacity=1)
|
||||||
|
adjascent.set_stroke(YELLOW, 5)
|
||||||
|
|
||||||
|
vectors.save_state()
|
||||||
|
vectors.clear_updaters()
|
||||||
|
self.add(g_line, g_vect)
|
||||||
|
self.play(vectors.set_opacity, 0.3)
|
||||||
|
self.play(
|
||||||
|
ShowCreation(arc),
|
||||||
|
Write(q_mark)
|
||||||
|
)
|
||||||
|
self.play(ShowCreationThenFadeAround(q_mark))
|
||||||
|
self.wait()
|
||||||
|
self.play(ShowCreationThenFadeAround(
|
||||||
|
self.pendulum.theta_label
|
||||||
|
))
|
||||||
|
self.play(
|
||||||
|
TransformFromCopy(
|
||||||
|
self.pendulum.theta_label,
|
||||||
|
theta_label,
|
||||||
|
),
|
||||||
|
FadeOut(q_mark)
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.play(ShowCreation(opposite))
|
||||||
|
self.play(ShowCreation(adjascent))
|
||||||
|
self.wait()
|
||||||
|
self.play(
|
||||||
|
FadeOut(opposite),
|
||||||
|
FadeOut(adjascent),
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
Restore(vectors),
|
||||||
|
FadeOut(arc),
|
||||||
|
FadeOut(theta_label),
|
||||||
|
)
|
||||||
|
|
||||||
|
# vectors.resume_updating()
|
||||||
|
|
||||||
def ask_about_what_to_do(self):
|
def ask_about_what_to_do(self):
|
||||||
pass
|
g_vect = self.g_vect
|
||||||
|
g_sin_label = self.g_sin_label
|
||||||
|
angle = g_vect.tangent.get_angle()
|
||||||
|
angle = (angle - PI) % TAU
|
||||||
|
|
||||||
|
randy = You()
|
||||||
|
randy.to_corner(DL)
|
||||||
|
bubble = randy.get_bubble(
|
||||||
|
height=2,
|
||||||
|
width=2,
|
||||||
|
)
|
||||||
|
thought_term = g_sin_label.copy()
|
||||||
|
thought_term.rotate(-angle)
|
||||||
|
thought_term.move_to(bubble.get_bubble_center())
|
||||||
|
rect = SurroundingRectangle(thought_term)
|
||||||
|
rect.rotate(angle)
|
||||||
|
rect.move_to(g_sin_label)
|
||||||
|
|
||||||
|
randy.save_state()
|
||||||
|
randy.fade(1)
|
||||||
|
self.play(randy.restore, randy.change, "pondering")
|
||||||
|
self.play(ShowCreationThenFadeOut(rect))
|
||||||
|
self.play(
|
||||||
|
ShowCreation(bubble),
|
||||||
|
TransformFromCopy(g_sin_label, thought_term),
|
||||||
|
randy.look_at, bubble,
|
||||||
|
)
|
||||||
|
self.play(Blink(randy))
|
||||||
|
self.wait()
|
||||||
|
thought_term.remove(thought_term[0])
|
||||||
|
self.play(
|
||||||
|
ShowCreationThenDestruction(
|
||||||
|
thought_term.copy().set_style(
|
||||||
|
stroke_color=YELLOW,
|
||||||
|
stroke_width=2,
|
||||||
|
fill_opacity=0,
|
||||||
|
),
|
||||||
|
run_time=2
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
randy.change, "confused", thought_term,
|
||||||
|
)
|
||||||
|
self.play(Blink(randy))
|
||||||
|
|
||||||
|
#
|
||||||
|
def set_theta(self, value, *added_anims, **kwargs):
|
||||||
|
kwargs["run_time"] = kwargs.get("run_time", 2)
|
||||||
|
self.play(
|
||||||
|
self.theta_tracker.set_value, value,
|
||||||
|
*added_anims,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BuildUpEquation(Scene):
|
||||||
|
def construct(self):
|
||||||
|
self.show_velocity_and_position()
|
||||||
|
self.show_derivatives()
|
||||||
|
self.show_equation()
|
||||||
|
self.talk_about_sine_component()
|
||||||
|
self.add_air_resistance()
|
||||||
|
|
||||||
def show_velocity_and_position(self):
|
def show_velocity_and_position(self):
|
||||||
pass
|
pass
|
||||||
|
Reference in New Issue
Block a user