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.staging 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"
|
||||
ALL_SCENE_CLASSES = [
|
||||
|
@ -14,9 +16,36 @@ ALL_SCENE_CLASSES = [
|
|||
SomeOfYouWatching,
|
||||
SmallAngleApproximationTex,
|
||||
VeryLowAnglePendulum,
|
||||
FollowThisThread,
|
||||
FormulasAreLies,
|
||||
TourOfDifferentialEquations,
|
||||
# FollowThisThread,
|
||||
StrogatzQuote,
|
||||
# Something...
|
||||
ShowGravityAcceleration,
|
||||
AnalyzePendulumForce,
|
||||
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,
|
||||
},
|
||||
"theta_label_height": 0.25,
|
||||
"set_theta_label_height_cap": False,
|
||||
"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):
|
||||
|
@ -52,7 +57,10 @@ class Pendulum(VGroup):
|
|||
self.rotating_group = VGroup(self.rod, self.weight)
|
||||
self.create_dashed_line()
|
||||
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.update()
|
||||
|
@ -82,23 +90,36 @@ class Pendulum(VGroup):
|
|||
self.get_fixed_point() + self.length * DOWN,
|
||||
**self.dashed_line_config
|
||||
)
|
||||
line.add_updater(
|
||||
lambda l: l.move_to(self.get_fixed_point(), UP)
|
||||
)
|
||||
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(),
|
||||
angle=self.get_arc_angle_theta(),
|
||||
**self.angle_arc_config,
|
||||
))
|
||||
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 make_vector():
|
||||
omega = self.get_omega()
|
||||
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(
|
||||
0.5 * omega * RIGHT,
|
||||
multiple * RIGHT,
|
||||
**self.velocity_vector_config,
|
||||
)
|
||||
vector.rotate(theta, about_point=ORIGIN)
|
||||
|
@ -110,17 +131,23 @@ class Pendulum(VGroup):
|
|||
return self
|
||||
|
||||
def add_theta_label(self):
|
||||
label = self.theta_label = TexMobject("\\theta")
|
||||
label.set_height(self.theta_label_height)
|
||||
self.theta_label = always_redraw(self.get_label)
|
||||
self.add(self.theta_label)
|
||||
|
||||
def update_label(l):
|
||||
top = self.get_fixed_point()
|
||||
arc_center = self.angle_arc.point_from_proportion(0.5)
|
||||
vect = arc_center - top
|
||||
vect = normalize(vect) * (1 + self.theta_label_height)
|
||||
l.move_to(top + vect)
|
||||
label.add_updater(update_label)
|
||||
self.add(label)
|
||||
def get_label(self):
|
||||
label = TexMobject("\\theta")
|
||||
label.set_height(self.theta_label_height)
|
||||
if self.set_theta_label_height_cap:
|
||||
max_height = self.angle_arc.get_width()
|
||||
if label.get_height() > max_height:
|
||||
label.set_height(max_height)
|
||||
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):
|
||||
|
@ -841,12 +868,13 @@ class VeryLowAnglePendulum(LowAnglePendulum):
|
|||
}
|
||||
|
||||
|
||||
class BuildUpEquation(MovingCameraScene):
|
||||
class AnalyzePendulumForce(MovingCameraScene):
|
||||
CONFIG = {
|
||||
"pendulum_config": {
|
||||
"length": 5,
|
||||
"top_point": 3 * UP,
|
||||
"initial_theta": 45 * DEGREES,
|
||||
"top_point": 3.5 * UP,
|
||||
"initial_theta": 60 * DEGREES,
|
||||
"set_theta_label_height_cap": True,
|
||||
},
|
||||
"g_vect_config": {
|
||||
"length_multiple": 0.25,
|
||||
|
@ -857,25 +885,33 @@ class BuildUpEquation(MovingCameraScene):
|
|||
|
||||
def construct(self):
|
||||
self.add_pendulum()
|
||||
self.add_g_vect()
|
||||
self.show_constraint()
|
||||
self.break_g_vect_into_components()
|
||||
self.show_angle_geometry()
|
||||
self.show_gsin_formula()
|
||||
self.show_acceleration_at_different_angles()
|
||||
self.show_angle_geometry()
|
||||
self.ask_about_what_to_do()
|
||||
self.show_velocity_and_position()
|
||||
self.show_derivatives()
|
||||
self.show_equation()
|
||||
self.talk_about_sine_component()
|
||||
self.add_air_resistance()
|
||||
|
||||
self.emphasize_theta()
|
||||
self.show_arc_length()
|
||||
self.show_angular_velocity()
|
||||
self.show_angular_acceleration()
|
||||
self.circle_g_sin_formula()
|
||||
|
||||
def add_pendulum(self):
|
||||
self.pendulum = Pendulum(**self.pendulum_config)
|
||||
self.add(self.pendulum)
|
||||
pendulum = Pendulum(**self.pendulum_config)
|
||||
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
|
||||
weight = pendulum.weight
|
||||
|
||||
g_vect = self.g_vect = GravityVector(
|
||||
pendulum, **self.g_vect_config,
|
||||
|
@ -887,24 +923,20 @@ class BuildUpEquation(MovingCameraScene):
|
|||
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(
|
||||
GrowArrow(g_vect),
|
||||
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.wait()
|
||||
|
||||
pendulum.add_updater(lambda p: p.set_theta(
|
||||
theta_tracker.get_value()
|
||||
))
|
||||
arcs = VGroup()
|
||||
for u in [-1, 2, -1]:
|
||||
d_theta = 40 * DEGREES * u
|
||||
|
@ -914,17 +946,17 @@ class BuildUpEquation(MovingCameraScene):
|
|||
radius=pendulum.length,
|
||||
arc_center=pendulum.get_fixed_point(),
|
||||
stroke_width=2,
|
||||
stroke_color=RED,
|
||||
stroke_color=GREEN,
|
||||
stroke_opacity=0.5,
|
||||
)
|
||||
self.play(
|
||||
theta_tracker.increment_value, d_theta,
|
||||
self.theta_tracker.increment_value, d_theta,
|
||||
ShowCreation(arc)
|
||||
)
|
||||
arcs.add(arc)
|
||||
pendulum.clear_updaters()
|
||||
self.wait()
|
||||
self.play(FadeOut(arc))
|
||||
|
||||
self.traced_arcs = arcs
|
||||
|
||||
def break_g_vect_into_components(self):
|
||||
g_vect = self.g_vect
|
||||
|
@ -945,40 +977,691 @@ class BuildUpEquation(MovingCameraScene):
|
|||
color=self.perp_line_color,
|
||||
))
|
||||
|
||||
self.play(
|
||||
ShowCreation(g_vect.component_lines),
|
||||
)
|
||||
self.play(ShowCreation(g_vect.component_lines))
|
||||
self.play(GrowArrow(g_vect.tangent))
|
||||
self.wait()
|
||||
self.play(GrowArrow(g_vect.perp))
|
||||
self.wait()
|
||||
|
||||
def show_angle_geometry(self):
|
||||
g_vect = self.g_vect
|
||||
|
||||
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):
|
||||
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):
|
||||
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
|
||||
|
||||
def show_velocity_and_position(self):
|
||||
def show_angular_acceleration(self):
|
||||
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):
|
||||
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):
|
||||
pass
|
||||
v_rhs = TexMobject(
|
||||
"={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):
|
||||
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):
|
||||
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):
|
||||
|
|
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.play(you.change, "confused")
|
||||
self.wait(0)
|
||||
self.wait()
|
||||
self.play(
|
||||
you.change, "angry",
|
||||
ShowCreation(bubble),
|
||||
|
@ -103,6 +103,282 @@ class FormulasAreLies(PiCreatureScene):
|
|||
return You().flip().to_corner(DR)
|
||||
|
||||
|
||||
class NewSceneName(Scene):
|
||||
# class TourOfDifferentialEquations(Scene):
|
||||
# def construct(self):
|
||||
# pass
|
||||
|
||||
|
||||
class ProveTeacherWrong(TeacherStudentsScene):
|
||||
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 = {
|
||||
"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.mobject.coordinate_systems import *
|
||||
from manimlib.mobject.changing import *
|
||||
from manimlib.mobject.frame import *
|
||||
from manimlib.mobject.functions import *
|
||||
from manimlib.mobject.geometry import *
|
||||
|
|
|
@ -64,10 +64,11 @@ class DrawBorderThenFill(Animation):
|
|||
def get_outline(self):
|
||||
outline = self.mobject.copy()
|
||||
outline.set_fill(opacity=0)
|
||||
outline.set_stroke(
|
||||
color=self.get_stroke_color(outline),
|
||||
width=self.stroke_width
|
||||
)
|
||||
for sm in outline.family_members_with_points():
|
||||
sm.set_stroke(
|
||||
color=self.get_stroke_color(sm),
|
||||
width=self.stroke_width
|
||||
)
|
||||
return outline
|
||||
|
||||
def get_stroke_color(self, vmobject):
|
||||
|
|
|
@ -4,6 +4,7 @@ from manimlib.animation.transform import Transform
|
|||
from manimlib.constants import DOWN
|
||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||
from manimlib.utils.bezier import interpolate
|
||||
from manimlib.utils.rate_functions import there_and_back
|
||||
|
||||
|
||||
DEFAULT_FADE_LAG_RATIO = 0
|
||||
|
@ -149,3 +150,10 @@ class VFadeOut(VFadeIn):
|
|||
|
||||
def interpolate_submobject(self, submob, start, 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):
|
||||
line = Line(axis1.get_start(), axis1.get_end())
|
||||
dense_freq = (1 + ratio)
|
||||
step = 1 / dense_freq
|
||||
step = (1 / dense_freq) * freq
|
||||
|
||||
lines1 = VGroup()
|
||||
lines2 = VGroup()
|
||||
|
@ -336,6 +336,7 @@ class NumberPlane(Axes):
|
|||
axis.get_edge_center(edge), direction,
|
||||
buff=buff
|
||||
)
|
||||
label.shift_onto_screen(buff=MED_SMALL_BUFF)
|
||||
return label
|
||||
|
||||
def get_axis_labels(self, x_label_tex="x", y_label_tex="y"):
|
||||
|
|
|
@ -543,7 +543,7 @@ class Arrow(Line):
|
|||
"buff": MED_SMALL_BUFF,
|
||||
"tip_width_to_length_ratio": 1,
|
||||
"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,
|
||||
"rectangular_stem_width": 0.05,
|
||||
}
|
||||
|
@ -557,6 +557,9 @@ class Arrow(Line):
|
|||
self.set_stroke_width_from_length()
|
||||
|
||||
def scale(self, factor, **kwargs):
|
||||
if self.get_length() == 0:
|
||||
return self
|
||||
|
||||
has_tip = self.has_tip()
|
||||
has_start_tip = self.has_start_tip()
|
||||
if has_tip or has_start_tip:
|
||||
|
|
|
@ -56,6 +56,8 @@ class Matrix(VMobject):
|
|||
CONFIG = {
|
||||
"v_buff": 0.8,
|
||||
"h_buff": 1.3,
|
||||
"bracket_h_buff": MED_SMALL_BUFF,
|
||||
"bracket_v_buff": MED_SMALL_BUFF,
|
||||
"add_background_rectangles_to_entries": False,
|
||||
"include_background_rectangle": False,
|
||||
"element_to_mobject": TexMobject,
|
||||
|
@ -101,10 +103,12 @@ class Matrix(VMobject):
|
|||
def add_brackets(self):
|
||||
bracket_pair = TexMobject("\\big[", "\\big]")
|
||||
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.next_to(self, LEFT)
|
||||
r_bracket.next_to(self, RIGHT)
|
||||
l_bracket.next_to(self, LEFT, self.bracket_h_buff)
|
||||
r_bracket.next_to(self, RIGHT, self.bracket_h_buff)
|
||||
self.add(l_bracket, r_bracket)
|
||||
self.brackets = VGroup(l_bracket, r_bracket)
|
||||
return self
|
||||
|
|
|
@ -1107,7 +1107,7 @@ class Mobject(Container):
|
|||
# Errors
|
||||
def throw_error_if_no_points(self):
|
||||
if self.has_no_points():
|
||||
message = "Cannot call Mobject.{}" +\
|
||||
message = "Cannot call Mobject.{} " +\
|
||||
"for a Mobject with no points"
|
||||
caller_name = sys._getframe(1).f_code.co_name
|
||||
raise Exception(message.format(caller_name))
|
||||
|
|
|
@ -141,8 +141,10 @@ class NumberLine(Line):
|
|||
self.decimal_number_config,
|
||||
number_config or {},
|
||||
)
|
||||
scale_val = scale_val or self.number_scale_val
|
||||
direction = direction or self.label_direction
|
||||
if scale_val is None:
|
||||
scale_val = self.number_scale_val
|
||||
if direction is None:
|
||||
direction = self.label_direction
|
||||
buff = buff or self.line_to_number_buff
|
||||
|
||||
num_mob = DecimalNumber(number, **number_config)
|
||||
|
@ -181,4 +183,7 @@ class UnitInterval(NumberLine):
|
|||
"tick_frequency": 0.1,
|
||||
"numbers_with_elongated_ticks": [0, 1],
|
||||
"number_at_center": 0.5,
|
||||
"decimal_number_config": {
|
||||
"num_decimal_places": 1,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ class BackgroundRectangle(SurroundingRectangle):
|
|||
CONFIG = {
|
||||
"color": BLACK,
|
||||
"stroke_width": 0,
|
||||
"stroke_opacity": 0,
|
||||
"fill_opacity": 0.75,
|
||||
"buff": 0
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ class SingleStringTexMobject(SVGMobject):
|
|||
# Need to add blank subscript or superscript
|
||||
tex.endswith("_"),
|
||||
tex.endswith("^"),
|
||||
tex.endswith("dot"),
|
||||
])
|
||||
if should_add_filler:
|
||||
filler = "{\\quad}"
|
||||
|
|
|
@ -577,6 +577,8 @@ class VMobject(Mobject):
|
|||
again.
|
||||
"""
|
||||
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_to_h1 = h1 - a1
|
||||
a2_to_h2 = h2 - a2
|
||||
|
@ -918,22 +920,22 @@ class DashedVMobject(VMobject):
|
|||
VMobject.__init__(self, **kwargs)
|
||||
num_dashes = self.num_dashes
|
||||
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
|
||||
alphas = np.linspace(0, 1, num_dashes + 1)
|
||||
# This determines the length of each "dash"
|
||||
full_d_alpha = (1.0 / num_dashes)
|
||||
partial_d_alpha = full_d_alpha * ps_ratio
|
||||
|
||||
# This determines the length of each "dash"
|
||||
full_d_alpha = (1.0 / num_dashes)
|
||||
partial_d_alpha = full_d_alpha * ps_ratio
|
||||
# Rescale so that the last point of vmobject will
|
||||
# be the end of the last dash
|
||||
alphas /= (1 - full_d_alpha + partial_d_alpha)
|
||||
|
||||
# Rescale so that the last point of vmobject will
|
||||
# be the end of the last dash
|
||||
alphas /= (1 - full_d_alpha + partial_d_alpha)
|
||||
|
||||
self.add(*[
|
||||
vmobject.get_subcurve(alpha, 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
|
||||
# implementation
|
||||
self.match_style(vmobject, family=False)
|
||||
|
|
|
@ -42,6 +42,7 @@ class ThreeDScene(Scene):
|
|||
|
||||
def stop_ambient_camera_rotation(self):
|
||||
self.camera.theta_tracker.clear_updaters()
|
||||
self.remove(self.camera.theta_tracker)
|
||||
|
||||
def move_camera(self,
|
||||
phi=None,
|
||||
|
@ -68,12 +69,8 @@ class ThreeDScene(Scene):
|
|||
self.camera.frame_center.move_to,
|
||||
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)
|
||||
if is_camera_rotating:
|
||||
self.add(self.ambient_camera_rotation)
|
||||
|
||||
def get_moving_mobjects(self, *animations):
|
||||
moving_mobjects = Scene.get_moving_mobjects(self, *animations)
|
||||
|
|
Loading…
Add table
Reference in a new issue