Merge pull request #481 from 3b1b/ode

Ode
This commit is contained in:
Grant Sanderson 2019-03-26 17:53:44 -07:00 committed by GitHub
commit 3868597831
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 3611 additions and 193 deletions

View file

@ -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,
]

View file

@ -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()
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):
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
vect = normalize(vect) * (1 + self.theta_label_height)
l.move_to(top + vect)
label.add_updater(update_label)
self.add(label)
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):

File diff suppressed because it is too large Load diff

View file

@ -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)

View file

@ -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

View 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))

View file

@ -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 *

View file

@ -64,8 +64,9 @@ 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),
for sm in outline.family_members_with_points():
sm.set_stroke(
color=self.get_stroke_color(sm),
width=self.stroke_width
)
return outline

View file

@ -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,
}

View 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

View file

@ -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"):

View file

@ -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:

View file

@ -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

View file

@ -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))

View file

@ -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,
}
}

View file

@ -25,6 +25,7 @@ class BackgroundRectangle(SurroundingRectangle):
CONFIG = {
"color": BLACK,
"stroke_width": 0,
"stroke_opacity": 0,
"fill_opacity": 0.75,
"buff": 0
}

View file

@ -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}"

View file

@ -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,7 +920,7 @@ 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)

View file

@ -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)