mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
commit
3868597831
20 changed files with 3611 additions and 193 deletions
|
@ -1,6 +1,8 @@
|
||||||
from active_projects.ode.part1.pendulum import *
|
from active_projects.ode.part1.pendulum import *
|
||||||
from active_projects.ode.part1.staging import *
|
from active_projects.ode.part1.staging import *
|
||||||
from active_projects.ode.part1.pi_scenes import *
|
from active_projects.ode.part1.pi_scenes import *
|
||||||
|
from active_projects.ode.part1.phase_space import *
|
||||||
|
from active_projects.ode.part1.wordy_scenes import *
|
||||||
|
|
||||||
OUTPUT_DIRECTORY = "ode/part1"
|
OUTPUT_DIRECTORY = "ode/part1"
|
||||||
ALL_SCENE_CLASSES = [
|
ALL_SCENE_CLASSES = [
|
||||||
|
@ -14,9 +16,36 @@ ALL_SCENE_CLASSES = [
|
||||||
SomeOfYouWatching,
|
SomeOfYouWatching,
|
||||||
SmallAngleApproximationTex,
|
SmallAngleApproximationTex,
|
||||||
VeryLowAnglePendulum,
|
VeryLowAnglePendulum,
|
||||||
FollowThisThread,
|
FormulasAreLies,
|
||||||
|
TourOfDifferentialEquations,
|
||||||
|
# FollowThisThread,
|
||||||
StrogatzQuote,
|
StrogatzQuote,
|
||||||
# Something...
|
# Something...
|
||||||
ShowGravityAcceleration,
|
ShowGravityAcceleration,
|
||||||
|
AnalyzePendulumForce,
|
||||||
BuildUpEquation,
|
BuildUpEquation,
|
||||||
|
ShowDerivativeVideo,
|
||||||
|
SubtleAirCurrents,
|
||||||
|
DefineODE,
|
||||||
|
ODEvsPDEinFrames,
|
||||||
|
ProveTeacherWrong,
|
||||||
|
SetAsideSeekingSolution,
|
||||||
|
ReferencePiCollisionStateSpaces,
|
||||||
|
VisualizeStates,
|
||||||
|
IntroduceVectorField,
|
||||||
|
BreakingSecondOrderIntoTwoFirstOrder,
|
||||||
|
ShowPendulumPhaseFlow,
|
||||||
|
ShowHighVelocityCase,
|
||||||
|
TweakMuInFormula,
|
||||||
|
TweakMuInVectorField,
|
||||||
|
FromODEToVectorField,
|
||||||
|
LorenzVectorField,
|
||||||
|
ThreeBodiesInSpace,
|
||||||
|
AltThreeBodiesInSpace,
|
||||||
|
ThreeBodySymbols,
|
||||||
|
AskAboutActuallySolving,
|
||||||
|
WriteODESolvingCode,
|
||||||
|
TakeManyTinySteps,
|
||||||
|
InaccurateComputation,
|
||||||
|
HungerForExactness,
|
||||||
]
|
]
|
||||||
|
|
|
@ -41,7 +41,12 @@ class Pendulum(VGroup):
|
||||||
"color": RED,
|
"color": RED,
|
||||||
},
|
},
|
||||||
"theta_label_height": 0.25,
|
"theta_label_height": 0.25,
|
||||||
|
"set_theta_label_height_cap": False,
|
||||||
"n_steps_per_frame": 100,
|
"n_steps_per_frame": 100,
|
||||||
|
"include_theta_label": True,
|
||||||
|
"include_velocity_vector": False,
|
||||||
|
"velocity_vector_multiple": 0.5,
|
||||||
|
"max_velocity_vector_length_to_length_ratio": 0.5,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
@ -52,7 +57,10 @@ class Pendulum(VGroup):
|
||||||
self.rotating_group = VGroup(self.rod, self.weight)
|
self.rotating_group = VGroup(self.rod, self.weight)
|
||||||
self.create_dashed_line()
|
self.create_dashed_line()
|
||||||
self.create_angle_arc()
|
self.create_angle_arc()
|
||||||
self.add_theta_label()
|
if self.include_theta_label:
|
||||||
|
self.add_theta_label()
|
||||||
|
if self.include_velocity_vector:
|
||||||
|
self.add_velocity_vector()
|
||||||
|
|
||||||
self.set_theta(self.initial_theta)
|
self.set_theta(self.initial_theta)
|
||||||
self.update()
|
self.update()
|
||||||
|
@ -82,23 +90,36 @@ class Pendulum(VGroup):
|
||||||
self.get_fixed_point() + self.length * DOWN,
|
self.get_fixed_point() + self.length * DOWN,
|
||||||
**self.dashed_line_config
|
**self.dashed_line_config
|
||||||
)
|
)
|
||||||
|
line.add_updater(
|
||||||
|
lambda l: l.move_to(self.get_fixed_point(), UP)
|
||||||
|
)
|
||||||
self.add_to_back(line)
|
self.add_to_back(line)
|
||||||
|
|
||||||
def create_angle_arc(self):
|
def create_angle_arc(self):
|
||||||
self.angle_arc = always_redraw(lambda: Arc(
|
self.angle_arc = always_redraw(lambda: Arc(
|
||||||
arc_center=self.get_fixed_point(),
|
arc_center=self.get_fixed_point(),
|
||||||
start_angle=-90 * DEGREES,
|
start_angle=-90 * DEGREES,
|
||||||
angle=self.get_theta(),
|
angle=self.get_arc_angle_theta(),
|
||||||
**self.angle_arc_config,
|
**self.angle_arc_config,
|
||||||
))
|
))
|
||||||
self.add(self.angle_arc)
|
self.add(self.angle_arc)
|
||||||
|
|
||||||
|
def get_arc_angle_theta(self):
|
||||||
|
# Might be changed in certain scenes
|
||||||
|
return self.get_theta()
|
||||||
|
|
||||||
def add_velocity_vector(self):
|
def add_velocity_vector(self):
|
||||||
def make_vector():
|
def make_vector():
|
||||||
omega = self.get_omega()
|
omega = self.get_omega()
|
||||||
theta = self.get_theta()
|
theta = self.get_theta()
|
||||||
|
mvlr = self.max_velocity_vector_length_to_length_ratio
|
||||||
|
max_len = mvlr * self.rod.get_length()
|
||||||
|
vvm = self.velocity_vector_multiple
|
||||||
|
multiple = np.clip(
|
||||||
|
vvm * omega, -max_len, max_len
|
||||||
|
)
|
||||||
vector = Vector(
|
vector = Vector(
|
||||||
0.5 * omega * RIGHT,
|
multiple * RIGHT,
|
||||||
**self.velocity_vector_config,
|
**self.velocity_vector_config,
|
||||||
)
|
)
|
||||||
vector.rotate(theta, about_point=ORIGIN)
|
vector.rotate(theta, about_point=ORIGIN)
|
||||||
|
@ -110,17 +131,23 @@ 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(self.theta_label_height)
|
||||||
vect = arc_center - top
|
if self.set_theta_label_height_cap:
|
||||||
vect = normalize(vect) * (1 + self.theta_label_height)
|
max_height = self.angle_arc.get_width()
|
||||||
l.move_to(top + vect)
|
if label.get_height() > max_height:
|
||||||
label.add_updater(update_label)
|
label.set_height(max_height)
|
||||||
self.add(label)
|
top = self.get_fixed_point()
|
||||||
|
arc_center = self.angle_arc.point_from_proportion(0.5)
|
||||||
|
vect = arc_center - top
|
||||||
|
norm = get_norm(vect)
|
||||||
|
vect = normalize(vect) * (norm + self.theta_label_height)
|
||||||
|
label.move_to(top + vect)
|
||||||
|
return label
|
||||||
|
|
||||||
#
|
#
|
||||||
def get_theta(self):
|
def get_theta(self):
|
||||||
|
@ -841,12 +868,13 @@ 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,
|
||||||
|
"set_theta_label_height_cap": True,
|
||||||
},
|
},
|
||||||
"g_vect_config": {
|
"g_vect_config": {
|
||||||
"length_multiple": 0.25,
|
"length_multiple": 0.25,
|
||||||
|
@ -857,25 +885,33 @@ class BuildUpEquation(MovingCameraScene):
|
||||||
|
|
||||||
def construct(self):
|
def construct(self):
|
||||||
self.add_pendulum()
|
self.add_pendulum()
|
||||||
|
self.add_g_vect()
|
||||||
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()
|
|
||||||
self.show_derivatives()
|
self.emphasize_theta()
|
||||||
self.show_equation()
|
self.show_arc_length()
|
||||||
self.talk_about_sine_component()
|
self.show_angular_velocity()
|
||||||
self.add_air_resistance()
|
self.show_angular_acceleration()
|
||||||
|
self.circle_g_sin_formula()
|
||||||
|
|
||||||
def add_pendulum(self):
|
def add_pendulum(self):
|
||||||
self.pendulum = Pendulum(**self.pendulum_config)
|
pendulum = Pendulum(**self.pendulum_config)
|
||||||
self.add(self.pendulum)
|
theta_tracker = ValueTracker(pendulum.get_theta())
|
||||||
|
pendulum.add_updater(lambda p: p.set_theta(
|
||||||
|
theta_tracker.get_value()
|
||||||
|
))
|
||||||
|
|
||||||
def show_constraint(self):
|
self.add(pendulum)
|
||||||
|
self.pendulum = pendulum
|
||||||
|
self.theta_tracker = theta_tracker
|
||||||
|
|
||||||
|
def add_g_vect(self):
|
||||||
pendulum = self.pendulum
|
pendulum = self.pendulum
|
||||||
weight = pendulum.weight
|
|
||||||
|
|
||||||
g_vect = self.g_vect = GravityVector(
|
g_vect = self.g_vect = GravityVector(
|
||||||
pendulum, **self.g_vect_config,
|
pendulum, **self.g_vect_config,
|
||||||
|
@ -887,24 +923,20 @@ class BuildUpEquation(MovingCameraScene):
|
||||||
g_vect, RIGHT, buff=-SMALL_BUFF,
|
g_vect, RIGHT, buff=-SMALL_BUFF,
|
||||||
))
|
))
|
||||||
|
|
||||||
theta_tracker = ValueTracker(pendulum.get_theta())
|
|
||||||
|
|
||||||
p = weight.get_center()
|
|
||||||
path = CubicBezier([p, p + 3 * DOWN, p + 3 * UP, p])
|
|
||||||
|
|
||||||
g_word.suspend_updating()
|
|
||||||
self.play(
|
self.play(
|
||||||
GrowArrow(g_vect),
|
GrowArrow(g_vect),
|
||||||
FadeInFrom(g_word, UP, lag_ratio=0.1),
|
FadeInFrom(g_word, UP, lag_ratio=0.1),
|
||||||
)
|
)
|
||||||
g_word.resume_updating()
|
|
||||||
|
def show_constraint(self):
|
||||||
|
pendulum = self.pendulum
|
||||||
|
weight = pendulum.weight
|
||||||
|
p = weight.get_center()
|
||||||
|
path = CubicBezier([p, p + 3 * DOWN, p + 3 * UP, p])
|
||||||
|
|
||||||
self.play(MoveAlongPath(weight, path, run_time=2))
|
self.play(MoveAlongPath(weight, path, run_time=2))
|
||||||
self.wait()
|
self.wait()
|
||||||
|
|
||||||
pendulum.add_updater(lambda p: p.set_theta(
|
|
||||||
theta_tracker.get_value()
|
|
||||||
))
|
|
||||||
arcs = VGroup()
|
arcs = VGroup()
|
||||||
for u in [-1, 2, -1]:
|
for u in [-1, 2, -1]:
|
||||||
d_theta = 40 * DEGREES * u
|
d_theta = 40 * DEGREES * u
|
||||||
|
@ -914,17 +946,17 @@ class BuildUpEquation(MovingCameraScene):
|
||||||
radius=pendulum.length,
|
radius=pendulum.length,
|
||||||
arc_center=pendulum.get_fixed_point(),
|
arc_center=pendulum.get_fixed_point(),
|
||||||
stroke_width=2,
|
stroke_width=2,
|
||||||
stroke_color=RED,
|
stroke_color=GREEN,
|
||||||
stroke_opacity=0.5,
|
stroke_opacity=0.5,
|
||||||
)
|
)
|
||||||
self.play(
|
self.play(
|
||||||
theta_tracker.increment_value, d_theta,
|
self.theta_tracker.increment_value, d_theta,
|
||||||
ShowCreation(arc)
|
ShowCreation(arc)
|
||||||
)
|
)
|
||||||
arcs.add(arc)
|
arcs.add(arc)
|
||||||
pendulum.clear_updaters()
|
|
||||||
self.wait()
|
self.wait()
|
||||||
self.play(FadeOut(arc))
|
|
||||||
|
self.traced_arcs = arcs
|
||||||
|
|
||||||
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,40 +977,691 @@ 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 if self.pendulum.get_theta() >= 0 else LEFT,
|
||||||
|
SMALL_BUFF
|
||||||
|
))
|
||||||
|
|
||||||
|
def create_vect_label(vect, tex, direction):
|
||||||
|
label = TexMobject(tex)
|
||||||
|
label.set_stroke(width=0, 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[0][0], g_term[0][1]),
|
||||||
|
FadeOut(g_word[0][1:]),
|
||||||
|
Write(g_term[0][0]),
|
||||||
|
)
|
||||||
|
self.add(g_term)
|
||||||
|
self.wait()
|
||||||
|
for label in g_sin_label, g_cos_label:
|
||||||
|
self.play(
|
||||||
|
GrowFromPoint(label[0], g_term.get_center()),
|
||||||
|
TransformFromCopy(g_term, label[1][:2]),
|
||||||
|
GrowFromPoint(label[1][2:], g_term.get_center()),
|
||||||
|
remover=True
|
||||||
|
)
|
||||||
|
self.add(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,
|
||||||
|
)
|
||||||
|
new_comp_line_sytle = {
|
||||||
|
"stroke_width": 0.5,
|
||||||
|
"stroke_opacity": 0.25,
|
||||||
|
}
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
to_fade.set_opacity, 0.25,
|
||||||
|
self.g_vect.component_lines.set_style,
|
||||||
|
new_comp_line_sytle
|
||||||
|
)
|
||||||
|
self.g_vect.component_lines.add_updater(
|
||||||
|
lambda m: m.set_style(**new_comp_line_sytle)
|
||||||
|
)
|
||||||
|
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 = Line(
|
||||||
|
*g_vect.component_lines[0].get_start_and_end()
|
||||||
|
)
|
||||||
|
adjascent = g_line.copy()
|
||||||
|
opposite.set_stroke(BLUE, 5, opacity=1)
|
||||||
|
adjascent.set_stroke(YELLOW, 5)
|
||||||
|
|
||||||
|
vectors.save_state()
|
||||||
|
vectors.suspend_updating()
|
||||||
|
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(g_line),
|
||||||
|
FadeOut(arc),
|
||||||
|
FadeOut(theta_label),
|
||||||
|
)
|
||||||
|
|
||||||
|
vectors.resume_updating()
|
||||||
|
|
||||||
def ask_about_what_to_do(self):
|
def ask_about_what_to_do(self):
|
||||||
|
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=3.5,
|
||||||
|
)
|
||||||
|
g_sin_copy = g_sin_label.copy()
|
||||||
|
g_sin_copy.remove(g_sin_copy[0])
|
||||||
|
g_sin_copy.generate_target()
|
||||||
|
g_sin_copy.target.scale(1 / 0.75)
|
||||||
|
g_sin_copy.target.rotate(-angle)
|
||||||
|
a_eq = TexMobject("a=")
|
||||||
|
thought_term = VGroup(a_eq, g_sin_copy.target)
|
||||||
|
thought_term.arrange(RIGHT, buff=SMALL_BUFF)
|
||||||
|
thought_term.move_to(bubble.get_bubble_center())
|
||||||
|
|
||||||
|
rect = SurroundingRectangle(g_sin_copy.target)
|
||||||
|
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),
|
||||||
|
Write(a_eq),
|
||||||
|
MoveToTarget(g_sin_copy),
|
||||||
|
randy.look_at, bubble,
|
||||||
|
)
|
||||||
|
thought_term.remove(g_sin_copy.target)
|
||||||
|
thought_term.add(g_sin_copy)
|
||||||
|
self.play(Blink(randy))
|
||||||
|
self.wait()
|
||||||
|
self.play(
|
||||||
|
ShowCreationThenDestruction(
|
||||||
|
thought_term.copy().set_style(
|
||||||
|
stroke_color=YELLOW,
|
||||||
|
stroke_width=2,
|
||||||
|
fill_opacity=0,
|
||||||
|
),
|
||||||
|
run_time=2,
|
||||||
|
lag_ratio=0.2,
|
||||||
|
),
|
||||||
|
randy.change, "confused", thought_term,
|
||||||
|
)
|
||||||
|
self.play(Blink(randy))
|
||||||
|
self.play(
|
||||||
|
FadeOut(randy),
|
||||||
|
FadeOut(bubble),
|
||||||
|
thought_term.next_to, self.pendulum, DOWN, LARGE_BUFF
|
||||||
|
)
|
||||||
|
|
||||||
|
self.accleration_equation = thought_term
|
||||||
|
|
||||||
|
def emphasize_theta(self):
|
||||||
|
pendulum = self.pendulum
|
||||||
|
|
||||||
|
self.play(FocusOn(pendulum.theta_label))
|
||||||
|
self.play(Indicate(pendulum.theta_label))
|
||||||
|
old_updaters = pendulum.get_updaters()
|
||||||
|
pendulum.clear_updaters(recursive=False)
|
||||||
|
pendulum.start_swinging()
|
||||||
|
self.wait(5)
|
||||||
|
pendulum.clear_updaters()
|
||||||
|
for updater in old_updaters:
|
||||||
|
pendulum.add_updater(updater)
|
||||||
|
|
||||||
|
def show_arc_length(self):
|
||||||
|
pendulum = self.pendulum
|
||||||
|
to_fade = VGroup(
|
||||||
|
self.g_vect,
|
||||||
|
self.g_term,
|
||||||
|
self.g_vect.component_lines,
|
||||||
|
self.g_vect.tangent,
|
||||||
|
self.g_vect.perp,
|
||||||
|
self.g_sin_label,
|
||||||
|
self.g_cos_label,
|
||||||
|
)
|
||||||
|
angle = pendulum.get_theta()
|
||||||
|
height = pendulum.length
|
||||||
|
top = pendulum.get_fixed_point()
|
||||||
|
|
||||||
|
line = Line(UP, DOWN)
|
||||||
|
line.set_height(height)
|
||||||
|
line.move_to(top, UP)
|
||||||
|
arc = Arc(
|
||||||
|
start_angle=-90 * DEGREES,
|
||||||
|
angle=angle,
|
||||||
|
arc_center=top,
|
||||||
|
radius=height,
|
||||||
|
stroke_color=GREEN,
|
||||||
|
)
|
||||||
|
|
||||||
|
brace = Brace(Line(ORIGIN, 5 * UP), RIGHT)
|
||||||
|
brace.point = VectorizedPoint(brace.get_right())
|
||||||
|
brace.add(brace.point)
|
||||||
|
brace.set_height(angle)
|
||||||
|
brace.move_to(ORIGIN, DL)
|
||||||
|
brace.apply_complex_function(np.exp)
|
||||||
|
brace.scale(height)
|
||||||
|
brace.rotate(-90 * DEGREES)
|
||||||
|
brace.move_to(arc)
|
||||||
|
brace.shift(MED_SMALL_BUFF * normalize(
|
||||||
|
arc.point_from_proportion(0.5) - top
|
||||||
|
))
|
||||||
|
x_sym = TexMobject("x")
|
||||||
|
x_sym.set_color(GREEN)
|
||||||
|
x_sym.next_to(brace.point, DR, buff=SMALL_BUFF)
|
||||||
|
|
||||||
|
rhs = TexMobject("=", "L", "\\theta")
|
||||||
|
rhs.set_color_by_tex("\\theta", BLUE)
|
||||||
|
rhs.next_to(x_sym, RIGHT)
|
||||||
|
rhs.shift(0.7 * SMALL_BUFF * UP)
|
||||||
|
line_L = TexMobject("L")
|
||||||
|
line_L.next_to(
|
||||||
|
pendulum.rod.get_center(), UR, SMALL_BUFF,
|
||||||
|
)
|
||||||
|
|
||||||
|
if hasattr(self, "traced_arcs"):
|
||||||
|
self.play(FadeOut(self.traced_arcs))
|
||||||
|
self.play(FadeOut(to_fade))
|
||||||
|
self.play(
|
||||||
|
ShowCreation(arc),
|
||||||
|
Rotate(line, angle, about_point=top),
|
||||||
|
UpdateFromAlphaFunc(
|
||||||
|
line, lambda m, a: m.set_stroke(
|
||||||
|
width=2 * there_and_back(a)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
GrowFromPoint(
|
||||||
|
brace, line.get_bottom(),
|
||||||
|
path_arc=angle
|
||||||
|
),
|
||||||
|
)
|
||||||
|
self.play(FadeInFrom(x_sym, UP))
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# Show equation
|
||||||
|
line.set_stroke(BLUE, 5)
|
||||||
|
self.play(
|
||||||
|
ShowCreationThenFadeOut(line),
|
||||||
|
FadeInFromDown(line_L)
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
TransformFromCopy(
|
||||||
|
line_L, rhs.get_part_by_tex("L")
|
||||||
|
),
|
||||||
|
Write(rhs.get_part_by_tex("="))
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
TransformFromCopy(
|
||||||
|
pendulum.theta_label,
|
||||||
|
rhs.get_parts_by_tex("\\theta"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.add(rhs)
|
||||||
|
|
||||||
|
def show_angular_velocity(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def show_velocity_and_position(self):
|
def show_angular_acceleration(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def circle_g_sin_formula(self):
|
||||||
|
self.play(
|
||||||
|
ShowCreationThenFadeAround(
|
||||||
|
self.accleration_equation
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
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):
|
||||||
|
CONFIG = {
|
||||||
|
"tex_config": {
|
||||||
|
"tex_to_color_map": {
|
||||||
|
"{a}": YELLOW,
|
||||||
|
"{v}": RED,
|
||||||
|
"{x}": GREEN,
|
||||||
|
"\\theta": BLUE,
|
||||||
|
"{L}": WHITE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
# self.add_center_line()
|
||||||
|
self.show_derivatives()
|
||||||
|
self.show_theta_double_dot_equation()
|
||||||
|
self.talk_about_sine_component()
|
||||||
|
self.add_air_resistance()
|
||||||
|
|
||||||
|
def add_center_line(self):
|
||||||
|
line = Line(UP, DOWN)
|
||||||
|
line.set_height(FRAME_HEIGHT)
|
||||||
|
line.set_stroke(WHITE, 1)
|
||||||
|
self.add(line)
|
||||||
|
|
||||||
def show_derivatives(self):
|
def show_derivatives(self):
|
||||||
pass
|
a_eq = TexMobject(
|
||||||
|
"{a}", "=", "{d{v} \\over dt}",
|
||||||
|
**self.tex_config,
|
||||||
|
)
|
||||||
|
v_eq = TexMobject(
|
||||||
|
"{v}", "=", "{d{x} \\over dt}",
|
||||||
|
**self.tex_config,
|
||||||
|
)
|
||||||
|
x_eq = TexMobject(
|
||||||
|
"{x} = {L} \\theta",
|
||||||
|
**self.tex_config,
|
||||||
|
)
|
||||||
|
eqs = VGroup(a_eq, v_eq, x_eq)
|
||||||
|
eqs.arrange(DOWN, buff=LARGE_BUFF)
|
||||||
|
eqs.to_corner(UL)
|
||||||
|
|
||||||
def show_equation(self):
|
v_rhs = TexMobject(
|
||||||
pass
|
"={L}{d\\theta \\over dt}",
|
||||||
|
"=", "{L}\\dot{\\theta}",
|
||||||
|
**self.tex_config,
|
||||||
|
)
|
||||||
|
|
||||||
|
v_rhs.next_to(v_eq, RIGHT, SMALL_BUFF)
|
||||||
|
v_rhs.shift(
|
||||||
|
UP * (v_eq[1].get_bottom()[1] - v_rhs[0].get_bottom()[1])
|
||||||
|
)
|
||||||
|
a_rhs = TexMobject(
|
||||||
|
"={L}{d", "\\dot{\\theta}", "\\over dt}",
|
||||||
|
"=", "{L}\\ddot{\\theta}",
|
||||||
|
**self.tex_config,
|
||||||
|
)
|
||||||
|
a_rhs.next_to(a_eq, RIGHT, SMALL_BUFF)
|
||||||
|
a_rhs.shift(
|
||||||
|
UP * (a_eq[1].get_bottom()[1] - a_rhs[0].get_bottom()[1])
|
||||||
|
)
|
||||||
|
|
||||||
|
# a_eq
|
||||||
|
self.play(Write(a_eq))
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# v_eq
|
||||||
|
self.play(
|
||||||
|
TransformFromCopy(
|
||||||
|
a_eq.get_part_by_tex("{v}"),
|
||||||
|
v_eq.get_part_by_tex("{v}"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.play(TransformFromCopy(v_eq[:1], v_eq[1:]))
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# x_eq
|
||||||
|
self.play(
|
||||||
|
TransformFromCopy(
|
||||||
|
v_eq.get_part_by_tex("{x}"),
|
||||||
|
x_eq.get_part_by_tex("{x}"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.play(Write(x_eq[1:]))
|
||||||
|
self.wait()
|
||||||
|
for tex in "L", "\\theta":
|
||||||
|
self.play(ShowCreationThenFadeAround(
|
||||||
|
x_eq.get_part_by_tex(tex)
|
||||||
|
))
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# v_rhs
|
||||||
|
self.play(*[
|
||||||
|
TransformFromCopy(
|
||||||
|
x_eq.get_part_by_tex(tex),
|
||||||
|
v_rhs.get_part_by_tex(tex),
|
||||||
|
)
|
||||||
|
for tex in ("=", "{L}", "\\theta")
|
||||||
|
])
|
||||||
|
self.play(
|
||||||
|
TransformFromCopy(v_eq[-3], v_rhs[2]),
|
||||||
|
TransformFromCopy(v_eq[-1], v_rhs[4]),
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.play(
|
||||||
|
Write(v_rhs[-5]),
|
||||||
|
TransformFromCopy(*v_rhs.get_parts_by_tex("{L}")),
|
||||||
|
TransformFromCopy(v_rhs[3:4], v_rhs[-3:])
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.play(ShowCreationThenFadeAround(v_rhs[2:4]))
|
||||||
|
self.play(ShowCreationThenFadeAround(v_rhs[4]))
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# a_rhs
|
||||||
|
self.play(*[
|
||||||
|
TransformFromCopy(
|
||||||
|
v_rhs.get_parts_by_tex(tex)[-1],
|
||||||
|
a_rhs.get_part_by_tex(tex),
|
||||||
|
)
|
||||||
|
for tex in ("=", "{L}", "\\theta", "\\dot")
|
||||||
|
])
|
||||||
|
self.play(
|
||||||
|
TransformFromCopy(a_eq[-3], a_rhs[2]),
|
||||||
|
TransformFromCopy(a_eq[-1], a_rhs[6]),
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.play(
|
||||||
|
Write(a_rhs[-5]),
|
||||||
|
TransformFromCopy(*a_rhs.get_parts_by_tex("{L}")),
|
||||||
|
TransformFromCopy(a_rhs[3:4], a_rhs[-3:]),
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
self.equations = VGroup(
|
||||||
|
a_eq, v_eq, x_eq,
|
||||||
|
v_rhs, a_rhs,
|
||||||
|
)
|
||||||
|
|
||||||
|
def show_theta_double_dot_equation(self):
|
||||||
|
equations = self.equations
|
||||||
|
a_deriv = equations[0]
|
||||||
|
a_rhs = equations[-1][-5:].copy()
|
||||||
|
|
||||||
|
shift_vect = 1.5 * DOWN
|
||||||
|
|
||||||
|
equals = TexMobject("=")
|
||||||
|
equals.rotate(90 * DEGREES)
|
||||||
|
equals.next_to(a_deriv[0], UP, MED_LARGE_BUFF)
|
||||||
|
g_sin_eq = TexMobject(
|
||||||
|
"-", "g", "\\sin", "(", "\\theta", ")",
|
||||||
|
**self.tex_config,
|
||||||
|
)
|
||||||
|
g_sin_eq.next_to(
|
||||||
|
equals, UP,
|
||||||
|
buff=MED_LARGE_BUFF,
|
||||||
|
aligned_edge=LEFT,
|
||||||
|
)
|
||||||
|
g_sin_eq.to_edge(LEFT)
|
||||||
|
g_sin_eq.shift(shift_vect)
|
||||||
|
|
||||||
|
shift_vect += (
|
||||||
|
g_sin_eq[1].get_center() -
|
||||||
|
a_deriv[0].get_center()
|
||||||
|
)[0] * RIGHT
|
||||||
|
|
||||||
|
equals.shift(shift_vect)
|
||||||
|
a_rhs.shift(shift_vect)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
equations.shift, shift_vect,
|
||||||
|
Write(equals),
|
||||||
|
GrowFromPoint(
|
||||||
|
g_sin_eq, 2 * RIGHT + 3 * DOWN
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.play(
|
||||||
|
a_rhs.next_to, g_sin_eq, RIGHT,
|
||||||
|
a_rhs.shift, SMALL_BUFF * UP,
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# Fade equations
|
||||||
|
self.play(
|
||||||
|
FadeOut(equals),
|
||||||
|
equations.shift, DOWN,
|
||||||
|
equations.fade, 0.5,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Rotate sides
|
||||||
|
equals, L, ddot, theta, junk = a_rhs
|
||||||
|
L_dd_theta = VGroup(L, ddot, theta)
|
||||||
|
minus, g, sin, lp, theta2, rp = g_sin_eq
|
||||||
|
m2, g2, over, L2 = frac = TexMobject("-", "{g", "\\over", "L}")
|
||||||
|
frac.next_to(equals, RIGHT)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
L_dd_theta.next_to, equals, LEFT,
|
||||||
|
L_dd_theta.shift, SMALL_BUFF * UP,
|
||||||
|
g_sin_eq.next_to, equals, RIGHT,
|
||||||
|
path_arc=PI / 2,
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
ReplacementTransform(g, g2),
|
||||||
|
ReplacementTransform(minus, m2),
|
||||||
|
ReplacementTransform(L, L2),
|
||||||
|
Write(over),
|
||||||
|
g_sin_eq[2:].next_to, over, RIGHT, SMALL_BUFF,
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# Surround
|
||||||
|
rect = SurroundingRectangle(VGroup(g_sin_eq, frac, ddot))
|
||||||
|
rect.stretch(1.1, 0)
|
||||||
|
dashed_rect = DashedVMobject(
|
||||||
|
rect, num_dashes=50, positive_space_ratio=1,
|
||||||
|
)
|
||||||
|
dashed_rect.shuffle()
|
||||||
|
dashed_rect.save_state()
|
||||||
|
dashed_rect.space_out_submobjects(1.1)
|
||||||
|
for piece in dashed_rect:
|
||||||
|
piece.rotate(90 * DEGREES)
|
||||||
|
dashed_rect.fade(1)
|
||||||
|
self.play(Restore(dashed_rect, lag_ratio=0.05))
|
||||||
|
dashed_rect.generate_target()
|
||||||
|
dashed_rect.target.space_out_submobjects(0.9)
|
||||||
|
dashed_rect.target.fade(1)
|
||||||
|
for piece in dashed_rect.target:
|
||||||
|
piece.rotate(90 * DEGREES)
|
||||||
|
self.play(MoveToTarget(
|
||||||
|
dashed_rect,
|
||||||
|
lag_ratio=0.05,
|
||||||
|
remover=True
|
||||||
|
))
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
self.main_equation = VGroup(
|
||||||
|
ddot, theta, equals,
|
||||||
|
m2, L2, over, g2,
|
||||||
|
sin, lp, theta2, rp,
|
||||||
|
)
|
||||||
|
|
||||||
def talk_about_sine_component(self):
|
def talk_about_sine_component(self):
|
||||||
pass
|
main_equation = self.main_equation
|
||||||
|
gL_part = main_equation[4:7]
|
||||||
|
sin_part = main_equation[7:]
|
||||||
|
sin = sin_part[0]
|
||||||
|
|
||||||
|
morty = Mortimer(height=1.5)
|
||||||
|
morty.next_to(sin, DR, buff=LARGE_BUFF)
|
||||||
|
morty.add_updater(lambda m: m.look_at(sin))
|
||||||
|
|
||||||
|
self.play(ShowCreationThenFadeAround(gL_part))
|
||||||
|
self.wait()
|
||||||
|
self.play(ShowCreationThenFadeAround(sin_part))
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
self.play(FadeIn(morty))
|
||||||
|
sin.save_state()
|
||||||
|
self.play(
|
||||||
|
morty.change, "angry",
|
||||||
|
sin.next_to, morty, LEFT, {"aligned_edge": UP},
|
||||||
|
)
|
||||||
|
self.play(Blink(morty))
|
||||||
|
morty.clear_updaters()
|
||||||
|
self.play(
|
||||||
|
morty.change, "concerned_musician",
|
||||||
|
morty.look, DR,
|
||||||
|
)
|
||||||
|
self.play(Restore(sin))
|
||||||
|
self.play(FadeOut(morty))
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# Emphasize theta as input
|
||||||
|
theta = sin_part[2]
|
||||||
|
arrow = Vector(0.5 * UP, color=WHITE)
|
||||||
|
arrow.next_to(theta, DOWN, SMALL_BUFF)
|
||||||
|
word = TextMobject("Input")
|
||||||
|
word.next_to(arrow, DOWN)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
FadeInFrom(word, UP),
|
||||||
|
GrowArrow(arrow)
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
ShowCreationThenDestruction(
|
||||||
|
theta.copy().set_style(
|
||||||
|
fill_opacity=0,
|
||||||
|
stroke_width=2,
|
||||||
|
stroke_color=YELLOW,
|
||||||
|
),
|
||||||
|
lag_ratio=0.1,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.play(FadeOut(arrow), FadeOut(word))
|
||||||
|
|
||||||
def add_air_resistance(self):
|
def add_air_resistance(self):
|
||||||
pass
|
main_equation = self.main_equation
|
||||||
|
tdd_eq = main_equation[:3]
|
||||||
|
rhs = main_equation[3:]
|
||||||
|
|
||||||
|
new_term = TexMobject(
|
||||||
|
"-", "\\mu", "\\dot{", "\\theta}",
|
||||||
|
)
|
||||||
|
new_term.set_color_by_tex("\\theta", BLUE)
|
||||||
|
new_term.move_to(main_equation)
|
||||||
|
new_term.shift(0.5 * SMALL_BUFF * UP)
|
||||||
|
new_term[0].align_to(rhs[0], UP)
|
||||||
|
|
||||||
|
brace = Brace(new_term, DOWN)
|
||||||
|
words = brace.get_text("Air resistance")
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
FadeInFromDown(new_term),
|
||||||
|
tdd_eq.next_to, new_term, LEFT,
|
||||||
|
tdd_eq.align_to, tdd_eq, UP,
|
||||||
|
rhs.next_to, new_term, RIGHT,
|
||||||
|
rhs.align_to, rhs, UP,
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
GrowFromCenter(brace),
|
||||||
|
Write(words)
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
|
||||||
class NewSceneName(Scene):
|
class NewSceneName(Scene):
|
||||||
|
|
1239
active_projects/ode/part1/phase_space.py
Normal file
1239
active_projects/ode/part1/phase_space.py
Normal file
File diff suppressed because it is too large
Load diff
|
@ -88,7 +88,7 @@ class FormulasAreLies(PiCreatureScene):
|
||||||
)
|
)
|
||||||
self.wait()
|
self.wait()
|
||||||
self.play(you.change, "confused")
|
self.play(you.change, "confused")
|
||||||
self.wait(0)
|
self.wait()
|
||||||
self.play(
|
self.play(
|
||||||
you.change, "angry",
|
you.change, "angry",
|
||||||
ShowCreation(bubble),
|
ShowCreation(bubble),
|
||||||
|
@ -103,6 +103,282 @@ class FormulasAreLies(PiCreatureScene):
|
||||||
return You().flip().to_corner(DR)
|
return You().flip().to_corner(DR)
|
||||||
|
|
||||||
|
|
||||||
class NewSceneName(Scene):
|
# class TourOfDifferentialEquations(Scene):
|
||||||
|
# def construct(self):
|
||||||
|
# pass
|
||||||
|
|
||||||
|
|
||||||
|
class ProveTeacherWrong(TeacherStudentsScene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
pass
|
tex_config = {
|
||||||
|
"tex_to_color_map": {"{\\theta}": BLUE}
|
||||||
|
}
|
||||||
|
func = TexMobject(
|
||||||
|
"{\\theta}(t)", "=",
|
||||||
|
"\\theta_0", "\\cos(\\sqrt{g / L} \\cdot t)",
|
||||||
|
**tex_config,
|
||||||
|
)
|
||||||
|
d_func = TexMobject(
|
||||||
|
"\\dot {\\theta}(t)", "=",
|
||||||
|
"-\\left(\\sqrt{g / L}\\right)",
|
||||||
|
"\\theta_0", "\\sin(\\sqrt{g / L} \\cdot t)",
|
||||||
|
**tex_config,
|
||||||
|
)
|
||||||
|
dd_func = TexMobject(
|
||||||
|
"\\ddot {\\theta}(t)", "=",
|
||||||
|
"-\\left(g / L\\right)",
|
||||||
|
"\\theta_0", "\\cos(\\sqrt{g / L} \\cdot t)",
|
||||||
|
**tex_config,
|
||||||
|
)
|
||||||
|
ode = TexMobject(
|
||||||
|
"\\ddot {\\theta}({t})", "=",
|
||||||
|
"-\\mu \\dot {\\theta}({t})",
|
||||||
|
"-{g \\over L} \\sin\\big({\\theta}({t})\\big)",
|
||||||
|
**tex_config,
|
||||||
|
)
|
||||||
|
arrows = [TexMobject("\\Downarrow") for x in range(2)]
|
||||||
|
|
||||||
|
VGroup(func, d_func, dd_func, ode, *arrows).scale(0.7)
|
||||||
|
|
||||||
|
teacher = self.teacher
|
||||||
|
you = self.students[2]
|
||||||
|
|
||||||
|
self.student_thinks(ode)
|
||||||
|
you.add_updater(lambda m: m.look_at(func))
|
||||||
|
self.teacher_holds_up(func)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
group = VGroup(arrows[0], d_func, arrows[1], dd_func)
|
||||||
|
group.arrange(DOWN)
|
||||||
|
group.move_to(func, DOWN)
|
||||||
|
|
||||||
|
arrow = Arrow(
|
||||||
|
group.get_corner(UL),
|
||||||
|
ode.get_top(),
|
||||||
|
path_arc=PI / 2,
|
||||||
|
)
|
||||||
|
q_marks = VGroup(*[
|
||||||
|
TexMobject("?").scale(1.5).next_to(
|
||||||
|
arrow.point_from_proportion(a),
|
||||||
|
UP
|
||||||
|
)
|
||||||
|
for a in np.linspace(0.2, 0.8, 5)
|
||||||
|
])
|
||||||
|
cycle_animation(VFadeInThenOut(
|
||||||
|
q_marks,
|
||||||
|
lag_ratio=0.2,
|
||||||
|
run_time=4,
|
||||||
|
rate_func=squish_rate_func(smooth, 0, 0.5)
|
||||||
|
))
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
func.next_to, group, UP,
|
||||||
|
LaggedStartMap(
|
||||||
|
FadeInFrom, group,
|
||||||
|
lambda m: (m, UP)
|
||||||
|
),
|
||||||
|
teacher.change, "guilty",
|
||||||
|
you.change, "sassy",
|
||||||
|
)
|
||||||
|
|
||||||
|
rect = SurroundingRectangle(
|
||||||
|
VGroup(group, func)
|
||||||
|
)
|
||||||
|
dashed_rect = DashedVMobject(rect, num_dashes=75)
|
||||||
|
animated_rect = AnimatedBoundary(dashed_rect, cycle_rate=1)
|
||||||
|
|
||||||
|
self.wait()
|
||||||
|
self.add(animated_rect, q_marks)
|
||||||
|
self.play(
|
||||||
|
ShowCreation(arrow),
|
||||||
|
# FadeInFromDown(q_mark),
|
||||||
|
self.get_student_changes("confused", "confused")
|
||||||
|
)
|
||||||
|
self.wait(4)
|
||||||
|
self.change_student_modes(
|
||||||
|
*3 * ["pondering"],
|
||||||
|
self.teacher.change, "maybe"
|
||||||
|
)
|
||||||
|
self.wait(8)
|
||||||
|
|
||||||
|
|
||||||
|
class AskAboutActuallySolving(TeacherStudentsScene):
|
||||||
|
def construct(self):
|
||||||
|
ode = get_ode()
|
||||||
|
ode.to_corner(UL)
|
||||||
|
self.add(ode)
|
||||||
|
morty = self.teacher
|
||||||
|
|
||||||
|
self.student_says(
|
||||||
|
"Yeah yeah, but how do\\\\"
|
||||||
|
"you acutally \\emph{solve} it?",
|
||||||
|
student_index=1,
|
||||||
|
target_mode="sassy",
|
||||||
|
added_anims=[morty.change, "thinking"],
|
||||||
|
)
|
||||||
|
self.change_student_modes(
|
||||||
|
"confused", "sassy", "confused",
|
||||||
|
look_at_arg=ode,
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.teacher_says(
|
||||||
|
"What do you mean\\\\ by ``solve''?",
|
||||||
|
target_mode="speaking",
|
||||||
|
added_anims=[self.get_student_changes(
|
||||||
|
*3 * ["erm"]
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
self.play(self.students[1].change, "angry")
|
||||||
|
self.wait(3)
|
||||||
|
|
||||||
|
|
||||||
|
class HungerForExactness(TeacherStudentsScene):
|
||||||
|
def construct(self):
|
||||||
|
students = self.students
|
||||||
|
you = students[2]
|
||||||
|
teacher = self.teacher
|
||||||
|
|
||||||
|
ode = get_ode()
|
||||||
|
ode.to_corner(UL)
|
||||||
|
left_part = ode[:5]
|
||||||
|
friction_part = ode[5:11]
|
||||||
|
right_part = ode[11:]
|
||||||
|
self.add(ode)
|
||||||
|
frictionless_group = VGroup(left_part, right_part)
|
||||||
|
|
||||||
|
proposed_solution = TexMobject(
|
||||||
|
"\\theta_0\\cos((\\sqrt{g/L})t)e^{-\\mu t}"
|
||||||
|
)
|
||||||
|
proposed_solution.next_to(
|
||||||
|
you.get_corner(UL), UP, buff=0.7
|
||||||
|
)
|
||||||
|
proposed_solution_rect = SurroundingRectangle(
|
||||||
|
proposed_solution, buff=MED_SMALL_BUFF,
|
||||||
|
)
|
||||||
|
proposed_solution_rect.set_color(BLUE)
|
||||||
|
proposed_solution_rect.round_corners()
|
||||||
|
|
||||||
|
solution_p1 = TexMobject(
|
||||||
|
"""
|
||||||
|
\\theta(t) = 2\\text{am}\\left(
|
||||||
|
\\frac{\\sqrt{2g + Lc_1} (t + c_2)}{2\\sqrt{L}},
|
||||||
|
\\frac{4g}{2g + Lc_1}
|
||||||
|
\\right)
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
solution_p1.to_corner(UL)
|
||||||
|
solution_p2 = TexMobject(
|
||||||
|
"c_1, c_2 = \\text{Constants depending on initial conditions}"
|
||||||
|
)
|
||||||
|
solution_p2.set_color(LIGHT_GREY)
|
||||||
|
solution_p2.scale(0.75)
|
||||||
|
solution_p3 = TexMobject(
|
||||||
|
"""
|
||||||
|
\\text{am}(u, k) =
|
||||||
|
\\int_0^u \\text{dn}(v, k)\\,dv
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
solution_p3.name = TextMobject(
|
||||||
|
"(Jacobi amplitude function)"
|
||||||
|
)
|
||||||
|
solution_p4 = TexMobject(
|
||||||
|
"""
|
||||||
|
\\text{dn}(u, k) =
|
||||||
|
\\sqrt{1 - k^2 \\sin^2(\\phi)}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
solution_p4.name = TextMobject(
|
||||||
|
"(Jacobi elliptic function)"
|
||||||
|
)
|
||||||
|
solution_p5 = TextMobject("Where $\\phi$ satisfies")
|
||||||
|
solution_p6 = TexMobject(
|
||||||
|
"""
|
||||||
|
u = \\int_0^\\phi \\frac{dt}{\\sqrt{1 - k^2 \\sin^2(t)}}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
solution = VGroup(
|
||||||
|
solution_p1,
|
||||||
|
solution_p2,
|
||||||
|
solution_p3,
|
||||||
|
solution_p4,
|
||||||
|
solution_p5,
|
||||||
|
solution_p6,
|
||||||
|
)
|
||||||
|
solution.arrange(DOWN)
|
||||||
|
solution.scale(0.7)
|
||||||
|
solution.to_corner(UL, buff=MED_SMALL_BUFF)
|
||||||
|
solution.set_stroke(width=0, background=True)
|
||||||
|
|
||||||
|
solution.remove(solution_p2)
|
||||||
|
solution_p1.add(solution_p2)
|
||||||
|
solution.remove(solution_p5)
|
||||||
|
solution_p6.add(solution_p5)
|
||||||
|
|
||||||
|
for part in [solution_p3, solution_p4]:
|
||||||
|
part.name.scale(0.7 * 0.7)
|
||||||
|
part.name.set_color(LIGHT_GREY)
|
||||||
|
part.name.next_to(part, RIGHT)
|
||||||
|
part.add(part.name)
|
||||||
|
|
||||||
|
self.student_says(
|
||||||
|
"Right, but like,\\\\"
|
||||||
|
"what \\emph{is} $\\theta(t)$?",
|
||||||
|
target_mode="sassy",
|
||||||
|
added_anims=[teacher.change, "guilty"],
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.play(
|
||||||
|
FadeInFromDown(proposed_solution),
|
||||||
|
RemovePiCreatureBubble(
|
||||||
|
you,
|
||||||
|
target_mode="raise_left_hand",
|
||||||
|
look_at_arg=proposed_solution,
|
||||||
|
),
|
||||||
|
teacher.change, "pondering",
|
||||||
|
students[0].change, "pondering",
|
||||||
|
students[1].change, "hesitant",
|
||||||
|
)
|
||||||
|
self.play(ShowCreation(proposed_solution_rect))
|
||||||
|
self.play(
|
||||||
|
proposed_solution.shift, 3 * RIGHT,
|
||||||
|
proposed_solution_rect.shift, 3 * RIGHT,
|
||||||
|
you.change, "raise_right_hand", teacher.eyes,
|
||||||
|
)
|
||||||
|
self.wait(3)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
FadeOut(proposed_solution),
|
||||||
|
FadeOut(proposed_solution_rect),
|
||||||
|
ode.move_to, self.hold_up_spot, DOWN,
|
||||||
|
ode.shift, LEFT,
|
||||||
|
teacher.change, "raise_right_hand",
|
||||||
|
self.get_student_changes(*3 * ["pondering"])
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
ode.save_state()
|
||||||
|
self.play(
|
||||||
|
left_part.move_to, friction_part, RIGHT,
|
||||||
|
left_part.match_y, left_part,
|
||||||
|
friction_part.to_corner, DR,
|
||||||
|
friction_part.fade, 0.5,
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
modes = ["erm", "sad", "sad", "horrified"]
|
||||||
|
for part, mode in zip(solution, modes):
|
||||||
|
self.play(
|
||||||
|
FadeInFrom(part, UP),
|
||||||
|
self.get_student_changes(
|
||||||
|
*3 * [mode],
|
||||||
|
look_at_arg=part,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.wait(3)
|
||||||
|
self.play(
|
||||||
|
FadeOutAndShift(solution, 2 * LEFT),
|
||||||
|
Restore(ode),
|
||||||
|
self.get_student_changes(*3 * ["sick"])
|
||||||
|
)
|
||||||
|
self.wait(3)
|
||||||
|
|
|
@ -14,3 +14,43 @@ class You(PiCreature):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"color": BLUE_C,
|
"color": BLUE_C,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_ode():
|
||||||
|
tex_config = {
|
||||||
|
"tex_to_color_map": {
|
||||||
|
"{\\theta}": BLUE,
|
||||||
|
"{\\dot\\theta}": YELLOW,
|
||||||
|
"{\\ddot\\theta}": RED,
|
||||||
|
"{t}": WHITE,
|
||||||
|
"{\\mu}": WHITE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ode = TexMobject(
|
||||||
|
"{\\ddot\\theta}({t})", "=",
|
||||||
|
"-{\\mu} {\\dot\\theta}({t})",
|
||||||
|
"-{g \\over L} \\sin\\big({\\theta}({t})\\big)",
|
||||||
|
**tex_config,
|
||||||
|
)
|
||||||
|
return ode
|
||||||
|
|
||||||
|
|
||||||
|
def pendulum_vector_field_func(point, mu=0.1, g=9.8, L=3):
|
||||||
|
theta, omega = point[:2]
|
||||||
|
return np.array([
|
||||||
|
omega,
|
||||||
|
-np.sqrt(g / L) * np.sin(theta) - mu * omega,
|
||||||
|
0,
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def get_vector_symbol(*texs, **kwargs):
|
||||||
|
config = {
|
||||||
|
"include_background_rectangle": True,
|
||||||
|
"bracket_h_buff": SMALL_BUFF,
|
||||||
|
"bracket_v_buff": SMALL_BUFF,
|
||||||
|
"element_alignment_corner": ORIGIN,
|
||||||
|
}
|
||||||
|
config.update(kwargs)
|
||||||
|
array = [[tex] for tex in texs]
|
||||||
|
return Matrix(array, **config)
|
||||||
|
|
File diff suppressed because it is too large
Load diff
262
active_projects/ode/part1/wordy_scenes.py
Normal file
262
active_projects/ode/part1/wordy_scenes.py
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
from big_ol_pile_of_manim_imports import *
|
||||||
|
from active_projects.ode.part1.shared_constructs import *
|
||||||
|
|
||||||
|
|
||||||
|
class SmallAngleApproximationTex(Scene):
|
||||||
|
def construct(self):
|
||||||
|
approx = TexMobject(
|
||||||
|
"\\sin", "(", "\\theta", ") \\approx \\theta",
|
||||||
|
tex_to_color_map={"\\theta": RED},
|
||||||
|
arg_separator="",
|
||||||
|
)
|
||||||
|
|
||||||
|
implies = TexMobject("\\Downarrow")
|
||||||
|
period = TexMobject(
|
||||||
|
"\\text{Period}", "\\approx",
|
||||||
|
"2\\pi \\sqrt{\\,{L} / {g}}",
|
||||||
|
**Lg_formula_config,
|
||||||
|
)
|
||||||
|
group = VGroup(approx, implies, period)
|
||||||
|
group.arrange(DOWN)
|
||||||
|
|
||||||
|
approx_brace = Brace(approx, UP, buff=SMALL_BUFF)
|
||||||
|
approx_words = TextMobject(
|
||||||
|
"For small $\\theta$",
|
||||||
|
tex_to_color_map={"$\\theta$": RED},
|
||||||
|
)
|
||||||
|
approx_words.scale(0.75)
|
||||||
|
approx_words.next_to(approx_brace, UP, SMALL_BUFF)
|
||||||
|
|
||||||
|
self.add(approx, approx_brace, approx_words)
|
||||||
|
self.play(
|
||||||
|
Write(implies),
|
||||||
|
FadeInFrom(period, LEFT)
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
|
||||||
|
class StrogatzQuote(Scene):
|
||||||
|
def construct(self):
|
||||||
|
law_words = "laws of physics"
|
||||||
|
language_words = "language of differential equations"
|
||||||
|
author = "-Steven Strogatz"
|
||||||
|
quote = TextMobject(
|
||||||
|
"""
|
||||||
|
\\Large
|
||||||
|
``Since Newton, mankind has come to realize
|
||||||
|
that the laws of physics are always expressed
|
||||||
|
in the language of differential equations.''\\\\
|
||||||
|
""" + author,
|
||||||
|
alignment="",
|
||||||
|
arg_separator=" ",
|
||||||
|
substrings_to_isolate=[law_words, language_words, author]
|
||||||
|
)
|
||||||
|
law_part = quote.get_part_by_tex(law_words)
|
||||||
|
language_part = quote.get_part_by_tex(language_words)
|
||||||
|
author_part = quote.get_part_by_tex(author)
|
||||||
|
quote.set_width(12)
|
||||||
|
quote.to_edge(UP)
|
||||||
|
quote[-2].shift(SMALL_BUFF * LEFT)
|
||||||
|
author_part.shift(RIGHT + 0.5 * DOWN)
|
||||||
|
author_part.scale(1.2, about_edge=UL)
|
||||||
|
|
||||||
|
movers = VGroup(*quote[:-1].family_members_with_points())
|
||||||
|
for mover in movers:
|
||||||
|
mover.save_state()
|
||||||
|
disc = Circle(radius=0.05)
|
||||||
|
disc.set_stroke(width=0)
|
||||||
|
disc.set_fill(BLACK, 0)
|
||||||
|
disc.move_to(mover)
|
||||||
|
mover.become(disc)
|
||||||
|
self.play(
|
||||||
|
FadeInFrom(author_part, LEFT),
|
||||||
|
LaggedStartMap(
|
||||||
|
# FadeInFromLarge,
|
||||||
|
# quote[:-1].family_members_with_points(),
|
||||||
|
Restore, movers,
|
||||||
|
lag_ratio=0.005,
|
||||||
|
run_time=2,
|
||||||
|
)
|
||||||
|
# FadeInFromDown(quote[:-1]),
|
||||||
|
# lag_ratio=0.01,
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.play(
|
||||||
|
Write(law_part.copy().set_color(YELLOW)),
|
||||||
|
run_time=1,
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.play(
|
||||||
|
Write(language_part.copy().set_color(BLUE)),
|
||||||
|
run_time=1.5,
|
||||||
|
)
|
||||||
|
self.wait(2)
|
||||||
|
|
||||||
|
|
||||||
|
class SetAsideSeekingSolution(Scene):
|
||||||
|
def construct(self):
|
||||||
|
ode = get_ode()
|
||||||
|
ode.to_edge(UP)
|
||||||
|
q1 = TextMobject("Find an exact solution")
|
||||||
|
q1.set_color(YELLOW)
|
||||||
|
q2 = TexMobject(
|
||||||
|
"\\text{What is }", "\\theta", "(t)",
|
||||||
|
"\\text{'s personality?}",
|
||||||
|
tex_to_color_map={"\\theta": BLUE},
|
||||||
|
arg_separator="",
|
||||||
|
)
|
||||||
|
theta = q2.get_part_by_tex("\\theta")
|
||||||
|
|
||||||
|
for q in q1, q2:
|
||||||
|
q.scale(1.5)
|
||||||
|
q.next_to(ode, DOWN, MED_LARGE_BUFF)
|
||||||
|
eyes = Eyes(theta, height=0.1)
|
||||||
|
|
||||||
|
self.add(ode)
|
||||||
|
self.add(q1)
|
||||||
|
self.wait()
|
||||||
|
self.play(
|
||||||
|
q1.scale, 0.3,
|
||||||
|
q1.to_corner, UR, MED_SMALL_BUFF,
|
||||||
|
)
|
||||||
|
self.play(FadeInFrom(q2, DOWN))
|
||||||
|
self.play(
|
||||||
|
eyes.blink,
|
||||||
|
rate_func=lambda t: smooth(1 - t),
|
||||||
|
)
|
||||||
|
self.play(eyes.look_at, q2.get_left())
|
||||||
|
self.play(eyes.look_at, q2.get_right())
|
||||||
|
self.play(
|
||||||
|
eyes.blink,
|
||||||
|
rate_func=squish_rate_func(there_and_back)
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.play(
|
||||||
|
eyes.change_mode, "confused",
|
||||||
|
eyes.look_at, ode.get_left(),
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
eyes.blink,
|
||||||
|
rate_func=squish_rate_func(there_and_back)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ThreeBodySymbols(Scene):
|
||||||
|
def construct(self):
|
||||||
|
self.init_coord_groups()
|
||||||
|
self.introduce_coord_groups()
|
||||||
|
self.count_coords()
|
||||||
|
|
||||||
|
def init_coord_groups(self):
|
||||||
|
kwargs = {
|
||||||
|
"bracket_v_buff": 2 * SMALL_BUFF
|
||||||
|
}
|
||||||
|
positions = VGroup(*[
|
||||||
|
get_vector_symbol(*[
|
||||||
|
"{}_{}".format(s, i)
|
||||||
|
for s in "xyz"
|
||||||
|
], **kwargs)
|
||||||
|
for i in range(1, 4)
|
||||||
|
])
|
||||||
|
velocities = VGroup(*[
|
||||||
|
get_vector_symbol(*[
|
||||||
|
"p^{}_{}".format(s, i)
|
||||||
|
for s in "xyz"
|
||||||
|
], **kwargs)
|
||||||
|
for i in range(1, 4)
|
||||||
|
])
|
||||||
|
groups = VGroup(positions, velocities)
|
||||||
|
colors = [GREEN, RED, BLUE]
|
||||||
|
for group in groups:
|
||||||
|
for matrix in group:
|
||||||
|
matrix.coords = matrix.get_entries()
|
||||||
|
for coord, color in zip(matrix.coords, colors):
|
||||||
|
coord.set_color(color)
|
||||||
|
group.arrange(RIGHT)
|
||||||
|
groups.arrange(DOWN, buff=LARGE_BUFF)
|
||||||
|
groups.to_edge(LEFT)
|
||||||
|
|
||||||
|
self.coord_groups = groups
|
||||||
|
|
||||||
|
def introduce_coord_groups(self):
|
||||||
|
groups = self.coord_groups
|
||||||
|
x_group, p_group = groups
|
||||||
|
x_word = TextMobject("Positions")
|
||||||
|
p_word = TextMobject("Momenta")
|
||||||
|
words = VGroup(x_word, p_word)
|
||||||
|
for word, group in zip(words, groups):
|
||||||
|
word.next_to(group, UP)
|
||||||
|
|
||||||
|
rect_groups = VGroup()
|
||||||
|
for group in groups:
|
||||||
|
rect_group = VGroup(*[
|
||||||
|
SurroundingRectangle(
|
||||||
|
VGroup(*[
|
||||||
|
tm.coords[i]
|
||||||
|
for tm in group
|
||||||
|
]),
|
||||||
|
color=WHITE,
|
||||||
|
stroke_width=2,
|
||||||
|
)
|
||||||
|
for i in range(3)
|
||||||
|
])
|
||||||
|
rect_groups.add(rect_group)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
*[
|
||||||
|
LaggedStartMap(
|
||||||
|
FadeInFrom, group,
|
||||||
|
lambda m: (m, UP),
|
||||||
|
run_time=1,
|
||||||
|
)
|
||||||
|
for group in groups
|
||||||
|
],
|
||||||
|
*map(FadeInFromDown, words),
|
||||||
|
)
|
||||||
|
for rect_group in rect_groups:
|
||||||
|
self.play(
|
||||||
|
ShowCreationThenFadeOut(
|
||||||
|
rect_group,
|
||||||
|
lag_ratio=0.5,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
def count_coords(self):
|
||||||
|
coord_copies = VGroup()
|
||||||
|
for group in self.coord_groups:
|
||||||
|
for tex_mob in group:
|
||||||
|
for coord in tex_mob.coords:
|
||||||
|
coord_copy = coord.copy()
|
||||||
|
coord_copy.set_stroke(
|
||||||
|
WHITE, 2, background=True
|
||||||
|
)
|
||||||
|
coord_copies.add(coord_copy)
|
||||||
|
|
||||||
|
count = Integer()
|
||||||
|
count_word = TextMobject("18", "degrees \\\\ of freedom")[1]
|
||||||
|
count_group = VGroup(count, count_word)
|
||||||
|
count_group.arrange(
|
||||||
|
RIGHT,
|
||||||
|
aligned_edge=DOWN,
|
||||||
|
)
|
||||||
|
count_group.scale(1.5)
|
||||||
|
count_group.next_to(
|
||||||
|
self.coord_groups, RIGHT,
|
||||||
|
aligned_edge=DOWN,
|
||||||
|
)
|
||||||
|
count.add_updater(
|
||||||
|
lambda m: m.set_value(len(coord_copies))
|
||||||
|
)
|
||||||
|
count.add_updater(
|
||||||
|
lambda m: m.next_to(count_word[0][0], LEFT, aligned_edge=DOWN)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.add(count_group)
|
||||||
|
self.play(
|
||||||
|
# ChangeDecimalToValue(count, len(coord_copies)),
|
||||||
|
ShowIncreasingSubsets(coord_copies),
|
||||||
|
run_time=1.5,
|
||||||
|
rate_func=linear,
|
||||||
|
)
|
||||||
|
self.play(FadeOut(coord_copies))
|
|
@ -35,6 +35,7 @@ from manimlib.camera.moving_camera import *
|
||||||
from manimlib.camera.three_d_camera import *
|
from manimlib.camera.three_d_camera import *
|
||||||
|
|
||||||
from manimlib.mobject.coordinate_systems import *
|
from manimlib.mobject.coordinate_systems import *
|
||||||
|
from manimlib.mobject.changing import *
|
||||||
from manimlib.mobject.frame import *
|
from manimlib.mobject.frame import *
|
||||||
from manimlib.mobject.functions import *
|
from manimlib.mobject.functions import *
|
||||||
from manimlib.mobject.geometry import *
|
from manimlib.mobject.geometry import *
|
||||||
|
|
|
@ -64,10 +64,11 @@ class DrawBorderThenFill(Animation):
|
||||||
def get_outline(self):
|
def get_outline(self):
|
||||||
outline = self.mobject.copy()
|
outline = self.mobject.copy()
|
||||||
outline.set_fill(opacity=0)
|
outline.set_fill(opacity=0)
|
||||||
outline.set_stroke(
|
for sm in outline.family_members_with_points():
|
||||||
color=self.get_stroke_color(outline),
|
sm.set_stroke(
|
||||||
width=self.stroke_width
|
color=self.get_stroke_color(sm),
|
||||||
)
|
width=self.stroke_width
|
||||||
|
)
|
||||||
return outline
|
return outline
|
||||||
|
|
||||||
def get_stroke_color(self, vmobject):
|
def get_stroke_color(self, vmobject):
|
||||||
|
|
|
@ -4,6 +4,7 @@ from manimlib.animation.transform import Transform
|
||||||
from manimlib.constants import DOWN
|
from manimlib.constants import DOWN
|
||||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||||
from manimlib.utils.bezier import interpolate
|
from manimlib.utils.bezier import interpolate
|
||||||
|
from manimlib.utils.rate_functions import there_and_back
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_FADE_LAG_RATIO = 0
|
DEFAULT_FADE_LAG_RATIO = 0
|
||||||
|
@ -149,3 +150,10 @@ class VFadeOut(VFadeIn):
|
||||||
|
|
||||||
def interpolate_submobject(self, submob, start, alpha):
|
def interpolate_submobject(self, submob, start, alpha):
|
||||||
super().interpolate_submobject(submob, start, 1 - alpha)
|
super().interpolate_submobject(submob, start, 1 - alpha)
|
||||||
|
|
||||||
|
|
||||||
|
class VFadeInThenOut(VFadeIn):
|
||||||
|
CONFIG = {
|
||||||
|
"rate_func": there_and_back,
|
||||||
|
"remover": True,
|
||||||
|
}
|
||||||
|
|
61
manimlib/mobject/changing.py
Normal file
61
manimlib/mobject/changing.py
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
from manimlib.constants import *
|
||||||
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||||
|
from manimlib.utils.rate_functions import smooth
|
||||||
|
|
||||||
|
|
||||||
|
class AnimatedBoundary(VGroup):
|
||||||
|
CONFIG = {
|
||||||
|
"colors": [BLUE_D, BLUE_B, BLUE_E, GREY_BROWN],
|
||||||
|
"max_stroke_width": 3,
|
||||||
|
"cycle_rate": 0.5,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, vmobject, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.vmobject = vmobject
|
||||||
|
self.boundary_copies = [
|
||||||
|
vmobject.copy().set_style(
|
||||||
|
stroke_width=0,
|
||||||
|
fill_opacity=0
|
||||||
|
)
|
||||||
|
for x in range(2)
|
||||||
|
]
|
||||||
|
self.add(*self.boundary_copies)
|
||||||
|
self.total_time = 0
|
||||||
|
self.add_updater(lambda m, dt: self.update(dt))
|
||||||
|
|
||||||
|
def update(self, dt):
|
||||||
|
# Not actual time, but something which passes at
|
||||||
|
# an altered rate to make the implementation below
|
||||||
|
# cleaner
|
||||||
|
time = self.total_time * self.cycle_rate
|
||||||
|
growing, fading = self.boundary_copies
|
||||||
|
colors = self.colors
|
||||||
|
msw = self.max_stroke_width
|
||||||
|
vmobject = self.vmobject
|
||||||
|
|
||||||
|
index = int(time % len(colors))
|
||||||
|
alpha = smooth(time % 1)
|
||||||
|
|
||||||
|
if int(time) % 2 == 0:
|
||||||
|
bounds = (0, alpha)
|
||||||
|
else:
|
||||||
|
bounds = (1 - alpha, 1)
|
||||||
|
self.full_family_become_partial(growing, vmobject, *bounds)
|
||||||
|
growing.set_stroke(colors[index], width=msw)
|
||||||
|
|
||||||
|
if time >= 1:
|
||||||
|
self.full_family_become_partial(fading, vmobject, 0, 1)
|
||||||
|
fading.set_stroke(
|
||||||
|
color=colors[index - 1],
|
||||||
|
width=(1 - alpha) * msw
|
||||||
|
)
|
||||||
|
|
||||||
|
self.total_time += dt
|
||||||
|
|
||||||
|
def full_family_become_partial(self, mob1, mob2, a, b):
|
||||||
|
family1 = mob1.family_members_with_points()
|
||||||
|
family2 = mob2.family_members_with_points()
|
||||||
|
for sm1, sm2 in zip(family1, family2):
|
||||||
|
sm1.pointwise_become_partial(sm2, a, b)
|
||||||
|
return self
|
|
@ -288,7 +288,7 @@ class NumberPlane(Axes):
|
||||||
def get_lines_parallel_to_axis(self, axis1, axis2, freq, ratio):
|
def get_lines_parallel_to_axis(self, axis1, axis2, freq, ratio):
|
||||||
line = Line(axis1.get_start(), axis1.get_end())
|
line = Line(axis1.get_start(), axis1.get_end())
|
||||||
dense_freq = (1 + ratio)
|
dense_freq = (1 + ratio)
|
||||||
step = 1 / dense_freq
|
step = (1 / dense_freq) * freq
|
||||||
|
|
||||||
lines1 = VGroup()
|
lines1 = VGroup()
|
||||||
lines2 = VGroup()
|
lines2 = VGroup()
|
||||||
|
@ -336,6 +336,7 @@ class NumberPlane(Axes):
|
||||||
axis.get_edge_center(edge), direction,
|
axis.get_edge_center(edge), direction,
|
||||||
buff=buff
|
buff=buff
|
||||||
)
|
)
|
||||||
|
label.shift_onto_screen(buff=MED_SMALL_BUFF)
|
||||||
return label
|
return label
|
||||||
|
|
||||||
def get_axis_labels(self, x_label_tex="x", y_label_tex="y"):
|
def get_axis_labels(self, x_label_tex="x", y_label_tex="y"):
|
||||||
|
|
|
@ -543,7 +543,7 @@ class Arrow(Line):
|
||||||
"buff": MED_SMALL_BUFF,
|
"buff": MED_SMALL_BUFF,
|
||||||
"tip_width_to_length_ratio": 1,
|
"tip_width_to_length_ratio": 1,
|
||||||
"max_tip_length_to_length_ratio": 0.25,
|
"max_tip_length_to_length_ratio": 0.25,
|
||||||
"max_stroke_width_to_length_ratio": 6,
|
"max_stroke_width_to_length_ratio": 4,
|
||||||
"preserve_tip_size_when_scaling": True,
|
"preserve_tip_size_when_scaling": True,
|
||||||
"rectangular_stem_width": 0.05,
|
"rectangular_stem_width": 0.05,
|
||||||
}
|
}
|
||||||
|
@ -557,6 +557,9 @@ class Arrow(Line):
|
||||||
self.set_stroke_width_from_length()
|
self.set_stroke_width_from_length()
|
||||||
|
|
||||||
def scale(self, factor, **kwargs):
|
def scale(self, factor, **kwargs):
|
||||||
|
if self.get_length() == 0:
|
||||||
|
return self
|
||||||
|
|
||||||
has_tip = self.has_tip()
|
has_tip = self.has_tip()
|
||||||
has_start_tip = self.has_start_tip()
|
has_start_tip = self.has_start_tip()
|
||||||
if has_tip or has_start_tip:
|
if has_tip or has_start_tip:
|
||||||
|
|
|
@ -56,6 +56,8 @@ class Matrix(VMobject):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"v_buff": 0.8,
|
"v_buff": 0.8,
|
||||||
"h_buff": 1.3,
|
"h_buff": 1.3,
|
||||||
|
"bracket_h_buff": MED_SMALL_BUFF,
|
||||||
|
"bracket_v_buff": MED_SMALL_BUFF,
|
||||||
"add_background_rectangles_to_entries": False,
|
"add_background_rectangles_to_entries": False,
|
||||||
"include_background_rectangle": False,
|
"include_background_rectangle": False,
|
||||||
"element_to_mobject": TexMobject,
|
"element_to_mobject": TexMobject,
|
||||||
|
@ -101,10 +103,12 @@ class Matrix(VMobject):
|
||||||
def add_brackets(self):
|
def add_brackets(self):
|
||||||
bracket_pair = TexMobject("\\big[", "\\big]")
|
bracket_pair = TexMobject("\\big[", "\\big]")
|
||||||
bracket_pair.scale(2)
|
bracket_pair.scale(2)
|
||||||
bracket_pair.stretch_to_fit_height(self.get_height() + 0.5)
|
bracket_pair.stretch_to_fit_height(
|
||||||
|
self.get_height() + 2 * self.bracket_v_buff
|
||||||
|
)
|
||||||
l_bracket, r_bracket = bracket_pair.split()
|
l_bracket, r_bracket = bracket_pair.split()
|
||||||
l_bracket.next_to(self, LEFT)
|
l_bracket.next_to(self, LEFT, self.bracket_h_buff)
|
||||||
r_bracket.next_to(self, RIGHT)
|
r_bracket.next_to(self, RIGHT, self.bracket_h_buff)
|
||||||
self.add(l_bracket, r_bracket)
|
self.add(l_bracket, r_bracket)
|
||||||
self.brackets = VGroup(l_bracket, r_bracket)
|
self.brackets = VGroup(l_bracket, r_bracket)
|
||||||
return self
|
return self
|
||||||
|
|
|
@ -1107,7 +1107,7 @@ class Mobject(Container):
|
||||||
# Errors
|
# Errors
|
||||||
def throw_error_if_no_points(self):
|
def throw_error_if_no_points(self):
|
||||||
if self.has_no_points():
|
if self.has_no_points():
|
||||||
message = "Cannot call Mobject.{}" +\
|
message = "Cannot call Mobject.{} " +\
|
||||||
"for a Mobject with no points"
|
"for a Mobject with no points"
|
||||||
caller_name = sys._getframe(1).f_code.co_name
|
caller_name = sys._getframe(1).f_code.co_name
|
||||||
raise Exception(message.format(caller_name))
|
raise Exception(message.format(caller_name))
|
||||||
|
|
|
@ -141,8 +141,10 @@ class NumberLine(Line):
|
||||||
self.decimal_number_config,
|
self.decimal_number_config,
|
||||||
number_config or {},
|
number_config or {},
|
||||||
)
|
)
|
||||||
scale_val = scale_val or self.number_scale_val
|
if scale_val is None:
|
||||||
direction = direction or self.label_direction
|
scale_val = self.number_scale_val
|
||||||
|
if direction is None:
|
||||||
|
direction = self.label_direction
|
||||||
buff = buff or self.line_to_number_buff
|
buff = buff or self.line_to_number_buff
|
||||||
|
|
||||||
num_mob = DecimalNumber(number, **number_config)
|
num_mob = DecimalNumber(number, **number_config)
|
||||||
|
@ -181,4 +183,7 @@ class UnitInterval(NumberLine):
|
||||||
"tick_frequency": 0.1,
|
"tick_frequency": 0.1,
|
||||||
"numbers_with_elongated_ticks": [0, 1],
|
"numbers_with_elongated_ticks": [0, 1],
|
||||||
"number_at_center": 0.5,
|
"number_at_center": 0.5,
|
||||||
|
"decimal_number_config": {
|
||||||
|
"num_decimal_places": 1,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ class BackgroundRectangle(SurroundingRectangle):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"color": BLACK,
|
"color": BLACK,
|
||||||
"stroke_width": 0,
|
"stroke_width": 0,
|
||||||
|
"stroke_opacity": 0,
|
||||||
"fill_opacity": 0.75,
|
"fill_opacity": 0.75,
|
||||||
"buff": 0
|
"buff": 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,7 @@ class SingleStringTexMobject(SVGMobject):
|
||||||
# Need to add blank subscript or superscript
|
# Need to add blank subscript or superscript
|
||||||
tex.endswith("_"),
|
tex.endswith("_"),
|
||||||
tex.endswith("^"),
|
tex.endswith("^"),
|
||||||
|
tex.endswith("dot"),
|
||||||
])
|
])
|
||||||
if should_add_filler:
|
if should_add_filler:
|
||||||
filler = "{\\quad}"
|
filler = "{\\quad}"
|
||||||
|
|
|
@ -577,6 +577,8 @@ class VMobject(Mobject):
|
||||||
again.
|
again.
|
||||||
"""
|
"""
|
||||||
for submob in self.family_members_with_points():
|
for submob in self.family_members_with_points():
|
||||||
|
if len(submob.points) < self.n_points_per_cubic_curve:
|
||||||
|
continue
|
||||||
a1, h1, h2, a2 = submob.get_anchors_and_handles()
|
a1, h1, h2, a2 = submob.get_anchors_and_handles()
|
||||||
a1_to_h1 = h1 - a1
|
a1_to_h1 = h1 - a1
|
||||||
a2_to_h2 = h2 - a2
|
a2_to_h2 = h2 - a2
|
||||||
|
@ -918,22 +920,22 @@ class DashedVMobject(VMobject):
|
||||||
VMobject.__init__(self, **kwargs)
|
VMobject.__init__(self, **kwargs)
|
||||||
num_dashes = self.num_dashes
|
num_dashes = self.num_dashes
|
||||||
ps_ratio = self.positive_space_ratio
|
ps_ratio = self.positive_space_ratio
|
||||||
|
if num_dashes > 0:
|
||||||
|
# End points of the unit interval for division
|
||||||
|
alphas = np.linspace(0, 1, num_dashes + 1)
|
||||||
|
|
||||||
# End points of the unit interval for division
|
# This determines the length of each "dash"
|
||||||
alphas = np.linspace(0, 1, num_dashes + 1)
|
full_d_alpha = (1.0 / num_dashes)
|
||||||
|
partial_d_alpha = full_d_alpha * ps_ratio
|
||||||
|
|
||||||
# This determines the length of each "dash"
|
# Rescale so that the last point of vmobject will
|
||||||
full_d_alpha = (1.0 / num_dashes)
|
# be the end of the last dash
|
||||||
partial_d_alpha = full_d_alpha * ps_ratio
|
alphas /= (1 - full_d_alpha + partial_d_alpha)
|
||||||
|
|
||||||
# Rescale so that the last point of vmobject will
|
self.add(*[
|
||||||
# be the end of the last dash
|
vmobject.get_subcurve(alpha, alpha + partial_d_alpha)
|
||||||
alphas /= (1 - full_d_alpha + partial_d_alpha)
|
for alpha in alphas[:-1]
|
||||||
|
])
|
||||||
self.add(*[
|
|
||||||
vmobject.get_subcurve(alpha, alpha + partial_d_alpha)
|
|
||||||
for alpha in alphas[:-1]
|
|
||||||
])
|
|
||||||
# Family is already taken care of by get_subcurve
|
# Family is already taken care of by get_subcurve
|
||||||
# implementation
|
# implementation
|
||||||
self.match_style(vmobject, family=False)
|
self.match_style(vmobject, family=False)
|
||||||
|
|
|
@ -42,6 +42,7 @@ class ThreeDScene(Scene):
|
||||||
|
|
||||||
def stop_ambient_camera_rotation(self):
|
def stop_ambient_camera_rotation(self):
|
||||||
self.camera.theta_tracker.clear_updaters()
|
self.camera.theta_tracker.clear_updaters()
|
||||||
|
self.remove(self.camera.theta_tracker)
|
||||||
|
|
||||||
def move_camera(self,
|
def move_camera(self,
|
||||||
phi=None,
|
phi=None,
|
||||||
|
@ -68,12 +69,8 @@ class ThreeDScene(Scene):
|
||||||
self.camera.frame_center.move_to,
|
self.camera.frame_center.move_to,
|
||||||
frame_center
|
frame_center
|
||||||
))
|
))
|
||||||
is_camera_rotating = self.ambient_camera_rotation in self.continual_animations
|
|
||||||
if is_camera_rotating:
|
|
||||||
self.remove(self.ambient_camera_rotation)
|
|
||||||
self.play(*anims + added_anims)
|
self.play(*anims + added_anims)
|
||||||
if is_camera_rotating:
|
|
||||||
self.add(self.ambient_camera_rotation)
|
|
||||||
|
|
||||||
def get_moving_mobjects(self, *animations):
|
def get_moving_mobjects(self, *animations):
|
||||||
moving_mobjects = Scene.get_moving_mobjects(self, *animations)
|
moving_mobjects = Scene.get_moving_mobjects(self, *animations)
|
||||||
|
|
Loading…
Add table
Reference in a new issue