End of brachistochrone problem

This commit is contained in:
Grant Sanderson
2016-03-31 23:56:04 -07:00
parent 3faf30dfd0
commit 8414022b81
9 changed files with 582 additions and 39 deletions

View File

@ -203,8 +203,12 @@ class PathSlidingScene(Scene):
theta = angle_of_vector(point_b - point_a)
mobject.rotate(theta)
mobject.shift(points[index])
self.midslide_action(point_a, theta)
return mobject
def midslide_action(self, point, angle):
pass
def write_time(self, time):
if hasattr(self, "time_mob"):
self.remove(self.time_mob)
@ -594,10 +598,112 @@ class WhatGovernsSpeed(PathSlidingScene):
class ThetaTInsteadOfXY(Scene):
def construct(self):
cycloid = Cycloid()
index = cycloid.get_num_points()/3
point = cycloid.points[index]
vect = cycloid.points[index+1]-point
vect /= np.linalg.norm(vect)
vect *= 3
vect_mob = Vector(point, vect)
dot = Dot(point)
xy = TexMobject("\\big( x(t), y(t) \\big)")
xy.next_to(dot, UP+RIGHT, buff = 0.1)
vert_line = Line(2*DOWN, 2*UP)
vert_line.shift(point)
angle = vect_mob.get_angle() + np.pi/2
arc = Arc(angle, radius = 1, start_angle = -np.pi/2)
arc.shift(point)
theta = TexMobject("\\theta(t)")
theta.next_to(arc, DOWN, buff = 0.1, aligned_edge = LEFT)
theta.shift(0.2*RIGHT)
self.play(ShowCreation(cycloid))
self.play(ShowCreation(dot))
self.play(ShimmerIn(xy))
self.dither()
self.play(
FadeOut(xy),
ShowCreation(vect_mob)
)
self.play(
ShowCreation(arc),
ShowCreation(vert_line),
ShimmerIn(theta)
)
self.dither()
class DefineCurveWithKnob(PathSlidingScene):
def construct(self):
self.knob = Circle(color = BLUE_D)
self.knob.add_line(UP, DOWN)
self.knob.to_corner(UP+RIGHT)
self.knob.shift(0.5*DOWN)
self.last_angle = np.pi/2
arrow = Vector(ORIGIN, RIGHT)
arrow.next_to(self.knob, LEFT)
words = TextMobject("Turn this knob over time to define the curve")
words.next_to(arrow, LEFT)
self.path = self.get_path()
self.path.shift(1.5*DOWN)
self.path.show()
self.path.highlight(BLACK)
randy = Randolph()
randy.scale(RANDY_SCALE_VAL)
randy.shift(-randy.get_bottom())
self.play(ShimmerIn(words))
self.play(ShowCreation(arrow))
self.play(ShowCreation(self.knob))
self.dither()
self.add(self.path)
self.slide(randy, self.path)
self.dither()
def get_path(self):
return Cycloid(end_theta = 2*np.pi)
def midslide_action(self, point, angle):
d_angle = angle-self.last_angle
self.knob.rotate_in_place(d_angle)
self.last_angle = angle
self.path.highlight(BLUE_D, lambda p : p[0] < point[0])
class WonkyDefineCurveWithKnob(DefineCurveWithKnob):
def get_path(self):
return ParametricFunction(
lambda t : t*RIGHT + (-0.2*t-np.sin(2*np.pi*t/6))*UP,
start = -7,
end = 10
)
class SlowDefineCurveWithKnob(DefineCurveWithKnob):
def get_path(self):
return ParametricFunction(
lambda t : t*RIGHT + (np.exp(-(t+2)**2)-0.2*np.exp(t-2)),
start = -4,
end = 4
)
class BumpyDefineCurveWithKnob(DefineCurveWithKnob):
def get_path(self):
result = FunctionGraph(
lambda x : 0.05*(x**2)+0.1*np.sin(2*x)
)
result.rotate(-np.pi/20)
result.scale(0.7)
result.shift(DOWN)
return result

View File

@ -502,34 +502,61 @@ class LeviSolution(CycloidScene):
class EquationsForCycloid(CycloidScene):
def construct(self):
CycloidScene.construct(self)
equations = TexMobject("""
x(t) &= Rt - R\\sin(t) \\\\
y(t) &= -R + R\\cos(t)
""")
equations.shift(2*UP)
equations = TexMobject([
"x(t) = Rt - R\\sin(t)",
"y(t) = -R + R\\cos(t)"
])
top, bottom = equations.split()
bottom.next_to(top, DOWN)
equations.center()
equations.to_edge(UP, buff = 1.3)
self.play(ShimmerIn(equations))
self.grow_parts()
self.draw_cycloid(rate_func = None, run_time = 5)
self.dither()
class SlidingObject(CycloidScene, PathSlidingScene):
CONFIG = {
"show_time" : False,
"dither_and_add" : False
}
def construct(self):
args_list = [(True,), (False,)]
@staticmethod
def args_to_string(with_words):
return "WithWords" if with_words else "WithoutWords"
@staticmethod
def string_to_args(string):
return string == "WithWords"
def construct(self, with_words):
CycloidScene.construct(self)
randy = Randolph()
randy.scale(RANDY_SCALE_VAL)
randy.shift(-randy.get_bottom())
central_randy = randy.copy()
start_randy = self.adjust_mobject_to_index(
randy.copy(), 1, self.cycloid.points
)
self.play(ShowCreation(self.cycloid))
if with_words:
words1 = TextMobject("Trajectory due to gravity")
arrow = TexMobject("\\leftrightarrow")
words2 = TextMobject("Trajectory due \\emph{constantly} rotating wheel")
words1.next_to(arrow, LEFT)
words2.next_to(arrow, RIGHT)
words = Mobject(words1, arrow, words2)
words.scale_to_fit_width(2*SPACE_WIDTH-1)
words.to_edge(UP, buff = 0.2)
words.to_edge(LEFT)
self.play(ShowCreation(self.cycloid.copy()))
self.slide(randy, self.cycloid)
self.add(self.slider)
self.dither()
self.grow_parts()
self.draw_cycloid()
@ -538,12 +565,26 @@ class SlidingObject(CycloidScene, PathSlidingScene):
self.dither()
self.roll_back()
self.dither()
radial_line = self.circle.sub_mobjects[0]
self.circle.add(self.slider)
self.circle.get_center = lambda : radial_line.get_start_and_end()[0]
self.draw_cycloid()
if with_words:
self.play(*map(ShimmerIn, [words1, arrow, words2]))
self.dither()
self.remove(self.circle)
start_time = len(self.frames)*self.frame_duration
self.remove(self.slider)
self.slide(central_randy, self.cycloid)
end_time = len(self.frames)*self.frame_duration
self.play_over_time_range(
start_time,
end_time,
RollAlongVector(
self.circle,
self.cycloid.points[-1]-self.cycloid.points[0],
run_time = end_time-start_time,
rate_func = None
)
)
self.add(self.circle, self.slider)
self.dither()

View File

@ -317,28 +317,28 @@ class JohannThinksOfFermat(Scene):
class MathematiciansOfEurope(Scene):
def construct(self):
europe = ImageMobject("1700_Europe", invert = False)
europe = ImageMobject("Europe", use_cache = False)
self.add(europe)
self.freeze_background()
mathematicians = [
("Newton", [-1.6, 0.6, 0]),
("Jacob_Bernoulli",[-1, -0.75, 0]),
("Ehrenfried_von_Tschirnhaus",[-0.5, 0.2, 0]),
("Gottfried_Wilhelm_von_Leibniz",[-0.1, -0.75, 0]),
("Guillaume_de_L'Hopital", [-1.5, -0.5, 0]),
("Newton", [-1.75, -0.75, 0]),
("Jacob_Bernoulli",[-0.75, -1.75, 0]),
("Ehrenfried_von_Tschirnhaus",[0.5, -0.5, 0]),
("Gottfried_Wilhelm_von_Leibniz",[0.2, -1.75, 0]),
("Guillaume_de_L'Hopital", [-1.75, -1.25, 0]),
]
for name, point in mathematicians:
man = ImageMobject(name, invert = False)
if name == "Newton":
name = "Isaac_Newton"
name_mob = TextMobject(name.replace("_", " "))
name_mob.to_corner(UP+LEFT, buff=0.75)
self.add(name_mob)
man.scale_to_fit_height(4)
mobject = Point(man.get_corner(UP+LEFT))
self.play(
DelayByOrder(Transform(mobject, man)),
ShimmerIn(name_mob)
)
self.play(Transform(mobject, man))
man.scale(0.2)
man.shift(point)
self.play(Transform(mobject, man))

View File

@ -878,7 +878,59 @@ class WhichPathWouldLightTake(PhotonScene, TryManyPaths):
class StateSnellsLaw(PhotonScene):
def construct(self):
point_a = 3*LEFT+3*UP
point_b = 1.5*RIGHT+3*DOWN
midpoint = ORIGIN
lines, arcs, thetas = [], [], []
counter = it.count(1)
for point in point_a, point_b:
line = Line(point, midpoint, color = RED_D)
angle = np.pi/2-np.abs(np.arctan(line.get_slope()))
arc = Arc(angle, radius = 0.5).rotate(np.pi/2)
if point is point_b:
arc.rotate(np.pi)
line.reverse_points()
theta = TexMobject("\\theta_%d"%counter.next())
theta.scale(0.5)
theta.shift(2*arc.get_center())
arc.shift(midpoint)
theta.shift(midpoint)
lines.append(line)
arcs.append(arc)
thetas.append(theta)
vert_line = Line(2*UP, 2*DOWN)
vert_line.shift(midpoint)
path = Mobject(*lines).ingest_sub_mobjects()
glass = Region(lambda x, y : y < 0, color = BLUE_E)
self.add(glass)
equation = TexMobject([
"\\dfrac{\\sin(\\theta_1)}{v_{\\text{air}}}",
"=",
"\\dfrac{\\sin(\\theta_2)}{v_{\\text{water}}}",
])
equation.to_corner(UP+RIGHT)
exp1, equals, exp2 = equation.split()
snells_law = TextMobject("Snell's Law:")
snells_law.highlight(YELLOW)
snells_law.to_edge(UP)
self.play(ShimmerIn(snells_law))
self.dither()
self.play(ShowCreation(path))
self.play(self.photon_run_along_path(path))
self.dither()
self.play(ShowCreation(vert_line))
self.play(*map(ShowCreation, arcs))
self.play(*map(GrowFromCenter, thetas))
self.dither()
self.play(ShimmerIn(exp1))
self.dither()
self.play(*map(ShimmerIn, [equals, exp2]))
self.dither()

View File

@ -17,8 +17,10 @@ from topics.characters import *
from topics.functions import ParametricFunction, FunctionGraph
from topics.number_line import *
from mobject.region import Region, region_from_polygon_vertices
from topics.three_dimensions import Stars
from scene import Scene
from brachistochrone.curves import Cycloid
class PhysicalIntuition(Scene):
def construct(self):
@ -232,9 +234,266 @@ class StayedUpAllNight(Scene):
self.dither()
class ThetaTSigmoidGraph(Scene):
class ThetaTGraph(Scene):
def construct(self):
pass
t_axis = NumberLine()
theta_axis = NumberLine().rotate(np.pi/2)
theta_mob = TexMobject("\\theta(t)")
t_mob = TexMobject("t")
theta_mob.next_to(theta_axis, RIGHT)
theta_mob.to_edge(UP)
t_mob.next_to(t_axis, UP)
t_mob.to_edge(RIGHT)
graph = ParametricFunction(
lambda t : 4*t*RIGHT + 2*smooth(t)*UP
)
line = Line(graph.points[0], graph.points[-1], color = WHITE)
q_mark = TextMobject("?")
q_mark.next_to(Point(graph.get_center()), LEFT)
stars = Stars(color = BLACK)
stars.scale(0.1).shift(q_mark.get_center())
squiggle = ParametricFunction(
lambda t : t*RIGHT + 0.2*t*(5-t)*(np.sin(t)**2)*UP,
start = 0,
end = 5
)
self.play(
ShowCreation(t_axis),
ShowCreation(theta_axis),
ShimmerIn(theta_mob),
ShimmerIn(t_mob)
)
self.play(
ShimmerIn(q_mark),
ShowCreation(graph)
)
self.dither()
self.play(
Transform(q_mark, stars),
Transform(graph, line)
)
self.dither()
self.play(Transform(graph, squiggle))
self.dither()
class SolutionsToTheBrachistochrone(Scene):
def construct(self):
r_range = np.arange(0.5, 2, 0.25)
cycloids = Mobject(*[
Cycloid(radius = r, end_theta=2*np.pi)
for r in r_range
])
lower_left = 2*DOWN+6*LEFT
lines = Mobject(*[
Line(
lower_left,
lower_left+5*r*np.cos(np.arctan(r))*RIGHT+2*r*np.sin(np.arctan(r))*UP
)
for r in r_range
])
nl = NumberLine(numbers_with_elongated_ticks = [])
x_axis = nl.copy().shift(3*UP)
y_axis = nl.copy().rotate(np.pi/2).shift(6*LEFT)
t_axis = nl.copy().shift(2*DOWN)
x_label = TexMobject("x")
x_label.next_to(x_axis, DOWN)
x_label.to_edge(RIGHT)
y_label = TexMobject("y")
y_label.next_to(y_axis, RIGHT)
y_label.shift(2*DOWN)
t_label = TexMobject("t")
t_label.next_to(t_axis, UP)
t_label.to_edge(RIGHT)
theta_label = TexMobject("\\theta")
theta_label.next_to(y_axis, RIGHT)
theta_label.to_edge(UP)
words = TextMobject("Boundary conditions?")
words.next_to(lines, RIGHT)
words.shift(2*UP)
self.play(ShowCreation(x_axis), ShimmerIn(x_label))
self.play(ShowCreation(y_axis), ShimmerIn(y_label))
self.play(ShowCreation(cycloids))
self.dither()
self.play(
Transform(cycloids, lines),
Transform(x_axis, t_axis),
Transform(x_label, t_label),
Transform(y_label, theta_label),
run_time = 2
)
self.dither()
self.play(ShimmerIn(words))
self.dither()
class VideoLayout(Scene):
def construct(self):
left, right = 5*LEFT, 5*RIGHT
top_words = TextMobject("The next 15 minutes of your life:")
top_words.to_edge(UP)
line = Line(left, right, color = BLUE_D)
for a in np.arange(0, 4./3, 1./3):
vect = interpolate(left, right, a)
line.add_line(vect+0.2*DOWN, vect+0.2*UP)
left_brace = Brace(
Mobject(
Point(left),
Point(interpolate(left, right, 2./3))
),
DOWN
)
right_brace = Brace(
Mobject(
Point(interpolate(left, right, 2./3)),
Point(right)
),
UP
)
left_brace.words = map(TextMobject, [
"Problem statement",
"History",
"Johann Bernoulli's cleverness"
])
curr = left_brace
right_brace.words = map(TextMobject, [
"Challenge",
"Mark Levi's cleverness",
])
for brace in left_brace, right_brace:
curr = brace
direction = DOWN if brace is left_brace else UP
for word in brace.words:
word.next_to(curr, direction)
curr = word
right_brace.words.reverse()
self.play(ShimmerIn(top_words))
self.play(ShowCreation(line))
for brace in left_brace, right_brace:
self.play(GrowFromCenter(brace))
self.dither()
for word in brace.words:
self.play(ShimmerIn(word))
self.dither()
class ShortestPathProblem(Scene):
def construct(self):
point_a, point_b = 3*LEFT, 3*RIGHT
dots = []
for point, char in [(point_a, "A"), (point_b, "B")]:
dot = Dot(point)
letter = TexMobject(char)
letter.next_to(dot, UP+LEFT)
dot.add(letter)
dots.append(dot)
path = ParametricFunction(
lambda t : (t/2 + np.cos(t))*RIGHT + np.sin(t)*UP,
start = -2*np.pi,
end = 2*np.pi
)
path.scale(6/(2*np.pi))
path.shift(point_a - path.points[0])
path.highlight(RED)
line = Line(point_a, point_b)
words = TextMobject("Shortest path from $A$ to $B$")
words.to_edge(UP)
self.play(
ShimmerIn(words),
*map(GrowFromCenter, dots)
)
self.play(ShowCreation(path))
self.play(Transform(
path, line,
path_func = path_along_arc(np.pi)
))
self.dither()
class MathBetterThanTalking(Scene):
def construct(self):
mathy = Mathematician()
mathy.to_corner(DOWN+LEFT)
bubble = ThoughtBubble()
bubble.pin_to(mathy)
bubble.write("Math $>$ Talking about math")
self.add(mathy)
self.play(ShowCreation(bubble))
self.play(ShimmerIn(bubble.content))
self.dither()
self.play(ApplyMethod(
mathy.blink,
rate_func = squish_rate_func(there_and_back, 0.4, 0.6)
))
class DetailsOfProofBox(Scene):
def construct(self):
rect = Rectangle(height = 4, width = 6, color = WHITE)
words = TextMobject("Details of proof")
words.to_edge(UP)
self.play(
ShowCreation(rect),
ShimmerIn(words)
)
self.dither()
class TalkedAboutSnellsLaw(Scene):
def construct(self):
randy = Randolph()
randy.to_corner(DOWN+LEFT)
morty = Mortimer()
morty.to_edge(DOWN+RIGHT)
randy.bubble = SpeechBubble().pin_to(randy)
morty.bubble = SpeechBubble().pin_to(morty)
phrases = [
"Let's talk about Snell's law",
"I love Snell's law",
"It's like running from \\\\ a beach into the ocean",
"It's like two constant \\\\ tension springs",
]
self.add(randy, morty)
talkers = it.cycle([randy, morty])
for talker, phrase in zip(talkers, phrases):
talker.bubble.write(phrase)
self.play(
FadeIn(talker.bubble),
ShimmerIn(talker.bubble.content)
)
self.play(ApplyMethod(
talker.blink,
rate_func = squish_rate_func(there_and_back)
))
self.dither()
self.remove(talker.bubble, talker.bubble.content)
class YetAnotherMarkLevi(Scene):
def construct(self):
words = TextMobject("Yet another bit of Mark Levi cleverness")
words.to_edge(UP)
levi = ImageMobject("Mark_Levi", invert = False)
levi.scale_to_fit_width(6)
levi.show()
self.add(levi)
self.play(ShimmerIn(words))
self.dither(2)
@ -249,7 +508,5 @@ class ThetaTSigmoidGraph(Scene):

View File

@ -387,7 +387,7 @@ class ContinuouslyObeyingSnellsLaw(MultilayeredScene):
def snells_law_at_every_point(self, cycloid, chopped_cycloid):
square = Square(side_length = 0.2, color = WHITE)
words = TextMobject(["Snell's law ", " at every point"])
words = TextMobject(["Snell's law ", "everywhere"])
snells, rest = words.split()
colon = TextMobject(":")
words.next_to(square)

View File

@ -469,6 +469,100 @@ class BalanceCompetingFactors(Scene):
class Challenge(Scene):
def construct(self):
self.add(TextMobject("""
Can you find a new solution to the
Brachistochrone problem by finding
an intuitive reason that time-minimizing
curves look like straight lines in
$t$-$\\theta$ space?
"""))
self.dither()
class Section1(Scene):
def construct(self):
self.add(TextMobject("Section 1: Johann Bernoulli's insight"))
self.dither()
class Section2(Scene):
def construct(self):
self.add(TextMobject(
"Section 2: Mark Levi's insight, and a challenge",
size = "\\large"
))
self.dither()
class NarratorInterjection(Scene):
def construct(self):
words1 = TexMobject("<\\text{Narrator interjection}>")
words2 = TexMobject("<\\!/\\text{Narrator interjection}>")
self.add(words1)
self.dither()
self.clear()
self.add(words2)
self.dither()
class ThisCouldBeTheEnd(Scene):
def construct(self):
words = TextMobject([
"This could be the end\\dots",
"but\\dots"
])
for part in words.split():
self.play(ShimmerIn(part))
self.dither()
class MyOwnChallenge(Scene):
def construct(self):
self.add(TextMobject("My own challenge:"))
self.dither()
class WarmupChallenge(Scene):
def construct(self):
self.add(TextMobject("\\large Warm-up challenge: Confirm this for yourself"))
self.dither()
class FindAnotherSolution(Scene):
def construct(self):
self.add(TextMobject("Find another brachistochrone solution\\dots"))
self.dither()
class ProofOfSnellsLaw(Scene):
def construct(self):
self.add(TextMobject("Proof of Snell's law:"))
self.dither()
class CondensedVersion(Scene):
def construct(self):
snells = TextMobject("Snell's")
snells.shift(-snells.get_left())
snells.to_edge(UP)
for vect in [RIGHT, RIGHT, LEFT, DOWN, DOWN, DOWN]:
snells.add(snells.copy().next_to(snells, vect))
snells.ingest_sub_mobjects()
snells.show()
condensed = TextMobject("condensed")
self.add(snells)
self.dither()
self.play(DelayByOrder(
Transform(snells, condensed, run_time = 2)
))
self.dither()

View File

@ -188,7 +188,6 @@ class Scene(object):
self.frames[index] = self.get_frame()
for animation in animations:
animation.clean_up()
self.repaint_mojects()
return self
def dither(self, duration = DEFAULT_DITHER_TIME):

View File

@ -130,8 +130,6 @@ class Mortimer(PiCreature):
}
def __init__(self, **kwargs):
PiCreature.__init__(self, **kwargs)
# self.highlight(DARK_BROWN)
self.give_straight_face()
self.rotate(np.pi, UP)
@ -139,10 +137,6 @@ class Mathematician(PiCreature):
CONFIG = {
"color" : GREY,
}
def __init__(self, **kwargs):
PiCreature.__init__(self, **kwargs)
self.give_straight_face()
class Bubble(Mobject):
CONFIG = {