Files

591 lines
17 KiB
Python

from manimlib.imports import *
from old_projects.brachistochrone.curves import *
class RollAlongVector(Animation):
CONFIG = {
"rotation_vector" : OUT,
}
def __init__(self, mobject, vector, **kwargs):
radius = mobject.get_width()/2
radians = get_norm(vector)/radius
last_alpha = 0
digest_config(self, kwargs, locals())
Animation.__init__(self, mobject, **kwargs)
def interpolate_mobject(self, alpha):
d_alpha = alpha - self.last_alpha
self.last_alpha = alpha
self.mobject.rotate_in_place(
d_alpha*self.radians,
self.rotation_vector
)
self.mobject.shift(d_alpha*self.vector)
class CycloidScene(Scene):
CONFIG = {
"point_a" : 6*LEFT+3*UP,
"radius" : 2,
"end_theta" : 2*np.pi
}
def construct(self):
self.generate_cycloid()
self.generate_circle()
self.generate_ceiling()
def grow_parts(self):
self.play(*[
ShowCreation(mob)
for mob in (self.circle, self.ceiling)
])
def generate_cycloid(self):
self.cycloid = Cycloid(
point_a = self.point_a,
radius = self.radius,
end_theta = self.end_theta
)
def generate_circle(self, **kwargs):
self.circle = Circle(radius = self.radius, **kwargs)
self.circle.shift(self.point_a - self.circle.get_top())
radial_line = Line(
self.circle.get_center(), self.point_a
)
self.circle.add(radial_line)
def generate_ceiling(self):
self.ceiling = Line(FRAME_X_RADIUS*LEFT, FRAME_X_RADIUS*RIGHT)
self.ceiling.shift(self.cycloid.get_top()[1]*UP)
def draw_cycloid(self, run_time = 3, *anims, **kwargs):
kwargs["run_time"] = run_time
self.play(
RollAlongVector(
self.circle,
self.cycloid.points[-1]-self.cycloid.points[0],
**kwargs
),
ShowCreation(self.cycloid, **kwargs),
*anims
)
def roll_back(self, run_time = 3, *anims, **kwargs):
kwargs["run_time"] = run_time
self.play(
RollAlongVector(
self.circle,
self.cycloid.points[0]-self.cycloid.points[- 1],
rotation_vector = IN,
**kwargs
),
ShowCreation(
self.cycloid,
rate_func = lambda t : smooth(1-t),
**kwargs
),
*anims
)
self.generate_cycloid()
class IntroduceCycloid(CycloidScene):
def construct(self):
CycloidScene.construct(self)
equation = TexMobject([
"\\dfrac{\\sin(\\theta)}{\\sqrt{y}}",
"= \\text{constant}"
])
sin_sqrt, const = equation.split()
new_eq = equation.copy()
new_eq.to_edge(UP, buff = 1.3)
cycloid_word = TextMobject("Cycloid")
arrow = Arrow(2*UP, cycloid_word)
arrow.reverse_points()
q_mark = TextMobject("?")
self.play(*list(map(ShimmerIn, equation.split())))
self.wait()
self.play(
ApplyMethod(equation.shift, 2.2*UP),
ShowCreation(arrow)
)
q_mark.next_to(sin_sqrt)
self.play(ShimmerIn(cycloid_word))
self.wait()
self.grow_parts()
self.draw_cycloid()
self.wait()
extra_terms = [const, arrow, cycloid_word]
self.play(*[
Transform(mob, q_mark)
for mob in extra_terms
])
self.remove(*extra_terms)
self.roll_back()
q_marks, arrows = self.get_q_marks_and_arrows(sin_sqrt)
self.draw_cycloid(3,
ShowCreation(q_marks),
ShowCreation(arrows)
)
self.wait()
def get_q_marks_and_arrows(self, mob, n_marks = 10):
circle = Circle().replace(mob)
q_marks, arrows = result = [Mobject(), Mobject()]
for x in range(n_marks):
index = (x+0.5)*self.cycloid.get_num_points()/n_marks
q_point = self.cycloid.points[index]
vect = q_point-mob.get_center()
start_point = circle.get_boundary_point(vect)
arrow = Arrow(
start_point, q_point,
color = BLUE_E
)
q_marks.add(TextMobject("?").shift(q_point))
arrows.add(arrow)
for mob in result:
mob.ingest_submobjects()
return result
class LeviSolution(CycloidScene):
CONFIG = {
"cycloid_fraction" : 0.25,
}
def construct(self):
CycloidScene.construct(self)
self.add(self.ceiling)
self.generate_points()
methods = [
self.draw_cycloid,
self.roll_into_position,
self.draw_p_and_c,
self.show_pendulum,
self.show_diameter,
self.show_theta,
self.show_similar_triangles,
self.show_sin_thetas,
self.show_y,
self.rearrange,
]
for method in methods:
method()
self.wait()
def generate_points(self):
index = int(self.cycloid_fraction*self.cycloid.get_num_points())
p_point = self.cycloid.points[index]
p_dot = Dot(p_point)
p_label = TexMobject("P")
p_label.next_to(p_dot, DOWN+LEFT)
c_point = self.point_a + self.cycloid_fraction*self.radius*2*np.pi*RIGHT
c_dot = Dot(c_point)
c_label = TexMobject("C")
c_label.next_to(c_dot, UP)
digest_locals(self)
def roll_into_position(self):
self.play(RollAlongVector(
self.circle,
(1-self.cycloid_fraction)*self.radius*2*np.pi*LEFT,
rotation_vector = IN,
run_time = 2
))
def draw_p_and_c(self):
radial_line = self.circle.submobjects[0] ##Hacky
self.play(Transform(radial_line, self.p_dot))
self.remove(radial_line)
self.add(self.p_dot)
self.play(ShimmerIn(self.p_label))
self.wait()
self.play(Transform(self.ceiling.copy(), self.c_dot))
self.play(ShimmerIn(self.c_label))
def show_pendulum(self, arc_angle = np.pi, arc_color = GREEN):
words = TextMobject(": Instantaneous center of rotation")
words.next_to(self.c_label)
line = Line(self.p_point, self.c_point)
line_angle = line.get_angle()+np.pi
line_length = line.get_length()
line.add(self.p_dot.copy())
line.get_center = lambda : self.c_point
tangent_line = Line(3*LEFT, 3*RIGHT)
tangent_line.rotate(line_angle-np.pi/2)
tangent_line.shift(self.p_point)
tangent_line.set_color(arc_color)
right_angle_symbol = Mobject(
Line(UP, UP+RIGHT),
Line(UP+RIGHT, RIGHT)
)
right_angle_symbol.scale(0.3)
right_angle_symbol.rotate(tangent_line.get_angle()+np.pi)
right_angle_symbol.shift(self.p_point)
self.play(ShowCreation(line))
self.play(ShimmerIn(words))
self.wait()
pairs = [
(line_angle, arc_angle/2),
(line_angle+arc_angle/2, -arc_angle),
(line_angle-arc_angle/2, arc_angle/2),
]
arcs = []
for start, angle in pairs:
arc = Arc(
angle = angle,
radius = line_length,
start_angle = start,
color = GREEN
)
arc.shift(self.c_point)
self.play(
ShowCreation(arc),
ApplyMethod(
line.rotate_in_place,
angle,
path_func = path_along_arc(angle)
),
run_time = 2
)
arcs.append(arc)
self.wait()
self.play(Transform(arcs[1], tangent_line))
self.add(tangent_line)
self.play(ShowCreation(right_angle_symbol))
self.wait()
self.tangent_line = tangent_line
self.right_angle_symbol = right_angle_symbol
self.pc_line = line
self.remove(words, *arcs)
def show_diameter(self):
exceptions = [
self.circle,
self.tangent_line,
self.pc_line,
self.right_angle_symbol
]
everything = set(self.mobjects).difference(exceptions)
everything_copy = Mobject(*everything).copy()
light_everything = everything_copy.copy()
dark_everything = everything_copy.copy()
dark_everything.fade(0.8)
bottom_point = np.array(self.c_point)
bottom_point += 2*self.radius*DOWN
diameter = Line(bottom_point, self.c_point)
brace = Brace(diameter, RIGHT)
diameter_word = TextMobject("Diameter")
d_mob = TexMobject("D")
diameter_word.next_to(brace)
d_mob.next_to(diameter)
self.remove(*everything)
self.play(Transform(everything_copy, dark_everything))
self.wait()
self.play(ShowCreation(diameter))
self.play(GrowFromCenter(brace))
self.play(ShimmerIn(diameter_word))
self.wait()
self.play(*[
Transform(mob, d_mob)
for mob in (brace, diameter_word)
])
self.remove(brace, diameter_word)
self.add(d_mob)
self.play(Transform(everything_copy, light_everything))
self.remove(everything_copy)
self.add(*everything)
self.d_mob = d_mob
self.bottom_point = bottom_point
def show_theta(self, radius = 1):
arc = Arc(
angle = self.tangent_line.get_angle()-np.pi/2,
radius = radius,
start_angle = np.pi/2
)
theta = TexMobject("\\theta")
theta.shift(1.5*arc.get_center())
Mobject(arc, theta).shift(self.bottom_point)
self.play(
ShowCreation(arc),
ShimmerIn(theta)
)
self.arc = arc
self.theta = theta
def show_similar_triangles(self):
y_point = np.array(self.p_point)
y_point[1] = self.point_a[1]
new_arc = Arc(
angle = self.tangent_line.get_angle()-np.pi/2,
radius = 0.5,
start_angle = np.pi
)
new_arc.shift(self.c_point)
new_theta = self.theta.copy()
new_theta.next_to(new_arc, LEFT)
new_theta.shift(0.1*DOWN)
kwargs = {
"stroke_width" : 2*DEFAULT_STROKE_WIDTH,
}
triangle1 = Polygon(
self.p_point, self.c_point, self.bottom_point,
color = MAROON,
**kwargs
)
triangle2 = Polygon(
y_point, self.p_point, self.c_point,
color = WHITE,
**kwargs
)
y_line = Line(self.p_point, y_point)
self.play(
Transform(self.arc.copy(), new_arc),
Transform(self.theta.copy(), new_theta),
run_time = 3
)
self.wait()
self.play(FadeIn(triangle1))
self.wait()
self.play(Transform(triangle1, triangle2))
self.play(ApplyMethod(triangle1.set_color, MAROON))
self.wait()
self.remove(triangle1)
self.add(y_line)
self.y_line = y_line
def show_sin_thetas(self):
pc = Line(self.p_point, self.c_point)
mob = Mobject(self.theta, self.d_mob).copy()
mob.ingest_submobjects()
triplets = [
(pc, "D\\sin(\\theta)", 0.5),
(self.y_line, "D\\sin^2(\\theta)", 0.7),
]
for line, tex, scale in triplets:
trig_mob = TexMobject(tex)
trig_mob.set_width(
scale*line.get_length()
)
trig_mob.shift(-1.2*trig_mob.get_top())
trig_mob.rotate(line.get_angle())
trig_mob.shift(line.get_center())
if line is self.y_line:
trig_mob.shift(0.1*UP)
self.play(Transform(mob, trig_mob))
self.add(trig_mob)
self.wait()
self.remove(mob)
self.d_sin_squared_theta = trig_mob
def show_y(self):
y_equals = TexMobject(["y", "="])
y_equals.shift(2*UP)
y_expression = TexMobject([
"D ", "\\sin", "^2", "(\\theta)"
])
y_expression.next_to(y_equals)
y_expression.shift(0.05*UP+0.1*RIGHT)
temp_expr = self.d_sin_squared_theta.copy()
temp_expr.rotate(-np.pi/2)
temp_expr.replace(y_expression)
y_mob = TexMobject("y")
y_mob.next_to(self.y_line, RIGHT)
y_mob.shift(0.2*UP)
self.play(
Transform(self.d_sin_squared_theta, temp_expr),
ShimmerIn(y_mob),
ShowCreation(y_equals)
)
self.remove(self.d_sin_squared_theta)
self.add(y_expression)
self.y_equals = y_equals
self.y_expression = y_expression
def rearrange(self):
sqrt_nudge = 0.2*LEFT
y, equals = self.y_equals.split()
d, sin, squared, theta = self.y_expression.split()
y_sqrt = TexMobject("\\sqrt{\\phantom{y}}")
d_sqrt = y_sqrt.copy()
y_sqrt.shift(y.get_center()+sqrt_nudge)
d_sqrt.shift(d.get_center()+sqrt_nudge)
self.play(
ShimmerIn(y_sqrt),
ShimmerIn(d_sqrt),
ApplyMethod(squared.shift, 4*UP),
ApplyMethod(theta.shift, 1.5* squared.get_width()*LEFT)
)
self.wait()
y_sqrt.add(y)
d_sqrt.add(d)
sin.add(theta)
sin_over = TexMobject("\\dfrac{\\phantom{\\sin(\\theta)}}{\\quad}")
sin_over.next_to(sin, DOWN, 0.15)
new_eq = equals.copy()
new_eq.next_to(sin_over, LEFT)
one_over = TexMobject("\\dfrac{1}{\\quad}")
one_over.next_to(new_eq, LEFT)
one_over.shift(
(sin_over.get_bottom()[1]-one_over.get_bottom()[1])*UP
)
self.play(
Transform(equals, new_eq),
ShimmerIn(sin_over),
ShimmerIn(one_over),
ApplyMethod(
d_sqrt.next_to, one_over, DOWN,
path_func = path_along_arc(-np.pi)
),
ApplyMethod(
y_sqrt.next_to, sin_over, DOWN,
path_func = path_along_arc(-np.pi)
),
run_time = 2
)
self.wait()
brace = Brace(d_sqrt, DOWN)
constant = TextMobject("Constant")
constant.next_to(brace, DOWN)
self.play(
GrowFromCenter(brace),
ShimmerIn(constant)
)
class EquationsForCycloid(CycloidScene):
def construct(self):
CycloidScene.construct(self)
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=linear, run_time = 5)
self.wait()
class SlidingObject(CycloidScene, PathSlidingScene):
CONFIG = {
"show_time" : False,
"wait_and_add" : False
}
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_FACTOR)
randy.shift(-randy.get_bottom())
central_randy = randy.copy()
start_randy = self.adjust_mobject_to_index(
randy.copy(), 1, self.cycloid.points
)
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.set_width(FRAME_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.wait()
self.grow_parts()
self.draw_cycloid()
self.wait()
self.play(Transform(self.slider, start_randy))
self.wait()
self.roll_back()
self.wait()
if with_words:
self.play(*list(map(ShimmerIn, [words1, arrow, words2])))
self.wait()
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=linear
)
)
self.add(self.circle, self.slider)
self.wait()
class RotateWheel(CycloidScene):
def construct(self):
CycloidScene.construct(self)
self.circle.center()
self.play(Rotating(
self.circle,
axis = OUT,
run_time = 5,
rate_func = smooth
))