mirror of
https://github.com/3b1b/manim.git
synced 2025-07-28 20:43:56 +08:00
824 lines
26 KiB
Python
824 lines
26 KiB
Python
import numbers
|
|
from manimlib.imports import *
|
|
from functools import reduce
|
|
|
|
OPERATION_COLORS = [YELLOW, GREEN, BLUE_B]
|
|
|
|
def get_equation(index, x = 2, y = 3, z = 8, expression_only = False):
|
|
assert(index in [0, 1, 2])
|
|
if index == 0:
|
|
tex1 = "\\sqrt[%d]{%d}"%(y, z),
|
|
tex2 = " = %d"%x
|
|
elif index == 1:
|
|
tex1 = "\\log_%d(%d)"%(x, z),
|
|
tex2 = " = %d"%y
|
|
elif index == 2:
|
|
tex1 = "%d^%d"%(x, y),
|
|
tex2 = " = %d"%z
|
|
if expression_only:
|
|
tex = tex1
|
|
else:
|
|
tex = tex1+tex2
|
|
return TexMobject(tex).set_color(OPERATION_COLORS[index])
|
|
|
|
def get_inverse_rules():
|
|
return list(map(TexMobject, [
|
|
"x^{\\log_x(z)} = z",
|
|
"\\log_x\\left(x^y \\right) = y",
|
|
"\\sqrt[y]{x^y} = x",
|
|
"\\left(\\sqrt[y]{z}\\right)^y = z",
|
|
"\\sqrt[\\log_x(z)]{z} = x",
|
|
"\\log_{\\sqrt[y]{z}}(z) = y",
|
|
]))
|
|
|
|
def get_top_inverse_rules():
|
|
result = []
|
|
pairs = [#Careful of order here!
|
|
(0, 2),
|
|
(0, 1),
|
|
(1, 0),
|
|
(1, 2),
|
|
(2, 0),
|
|
(2, 1),
|
|
]
|
|
for i, j in pairs:
|
|
top = get_top_inverse(i, j)
|
|
char = ["x", "y", "z"][j]
|
|
eq = TexMobject("= %s"%char)
|
|
eq.scale(2)
|
|
eq.next_to(top, RIGHT)
|
|
diff = eq.get_center() - top.triangle.get_center()
|
|
eq.shift(diff[1]*UP)
|
|
result.append(VMobject(top, eq))
|
|
return result
|
|
|
|
def get_top_inverse(i, j):
|
|
args = [None]*3
|
|
k = set([0, 1, 2]).difference([i, j]).pop()
|
|
args[i] = ["x", "y", "z"][i]
|
|
big_top = TOP(*args)
|
|
args[j] = ["x", "y", "z"][j]
|
|
lil_top = TOP(*args, triangle_height_to_number_height = 1.5)
|
|
big_top.set_value(k, lil_top)
|
|
return big_top
|
|
|
|
class TOP(VMobject):
|
|
CONFIG = {
|
|
"triangle_height_to_number_height" : 3,
|
|
"offset_multiple" : 1.5,
|
|
"radius" : 1.5,
|
|
}
|
|
def __init__(self, x = None, y = None, z = None, **kwargs):
|
|
digest_config(self, kwargs, locals())
|
|
VMobject.__init__(self, **kwargs)
|
|
|
|
def generate_points(self):
|
|
vertices = [
|
|
self.radius*rotate_vector(RIGHT, 7*np.pi/6 - i*2*np.pi/3)
|
|
for i in range(3)
|
|
]
|
|
self.triangle = Polygon(
|
|
*vertices,
|
|
color = WHITE,
|
|
stroke_width = 5
|
|
)
|
|
self.values = [VMobject()]*3
|
|
self.set_values(self.x, self.y, self.z)
|
|
|
|
def set_values(self, x, y, z):
|
|
for i, mob in enumerate([x, y, z]):
|
|
self.set_value(i, mob)
|
|
|
|
def set_value(self, index, value):
|
|
self.values[index] = self.put_on_vertex(index, value)
|
|
self.reset_submobjects()
|
|
|
|
def put_on_vertex(self, index, value):
|
|
assert(index in [0, 1, 2])
|
|
if value is None:
|
|
value = VectorizedPoint()
|
|
if isinstance(value, numbers.Number):
|
|
value = str(value)
|
|
if isinstance(value, str):
|
|
value = TexMobject(value)
|
|
if isinstance(value, TOP):
|
|
return self.put_top_on_vertix(index, value)
|
|
self.rescale_corner_mobject(value)
|
|
value.center()
|
|
if index == 0:
|
|
offset = -value.get_corner(UP+RIGHT)
|
|
elif index == 1:
|
|
offset = -value.get_bottom()
|
|
elif index == 2:
|
|
offset = -value.get_corner(UP+LEFT)
|
|
value.shift(self.offset_multiple*offset)
|
|
anchors = self.triangle.get_anchors_and_handles()[0]
|
|
value.shift(anchors[index])
|
|
return value
|
|
|
|
def put_top_on_vertix(self, index, top):
|
|
top.set_height(2*self.get_value_height())
|
|
vertices = np.array(top.get_vertices())
|
|
vertices[index] = 0
|
|
start = reduce(op.add, vertices)/2
|
|
end = self.triangle.get_anchors_and_handles()[0][index]
|
|
top.shift(end-start)
|
|
return top
|
|
|
|
def put_in_vertex(self, index, mobject):
|
|
self.rescale_corner_mobject(mobject)
|
|
mobject.center()
|
|
mobject.shift(interpolate(
|
|
self.get_center(),
|
|
self.get_vertices()[index],
|
|
0.7
|
|
))
|
|
return mobject
|
|
|
|
|
|
def get_surrounding_circle(self, color = YELLOW):
|
|
return Circle(
|
|
radius = 1.7*self.radius,
|
|
color = color
|
|
).shift(
|
|
self.triangle.get_center(),
|
|
(self.triangle.get_height()/6)*DOWN
|
|
)
|
|
|
|
def rescale_corner_mobject(self, mobject):
|
|
mobject.set_height(self.get_value_height())
|
|
return self
|
|
|
|
def get_value_height(self):
|
|
return self.triangle.get_height()/self.triangle_height_to_number_height
|
|
|
|
def get_center(self):
|
|
return center_of_mass(self.get_vertices())
|
|
|
|
def get_vertices(self):
|
|
return self.triangle.get_anchors_and_handles()[0][:3]
|
|
|
|
def reset_submobjects(self):
|
|
self.submobjects = [self.triangle] + self.values
|
|
return self
|
|
|
|
|
|
class IntroduceNotation(Scene):
|
|
def construct(self):
|
|
top = TOP()
|
|
equation = TexMobject("2^3 = 8")
|
|
equation.to_corner(UP+LEFT)
|
|
two, three, eight = [
|
|
top.put_on_vertex(i, num)
|
|
for i, num in enumerate([2, 3, 8])
|
|
]
|
|
|
|
self.play(FadeIn(equation))
|
|
self.wait()
|
|
self.play(ShowCreation(top))
|
|
for num in two, three, eight:
|
|
self.play(ShowCreation(num), run_time=2)
|
|
self.wait()
|
|
|
|
class ShowRule(Scene):
|
|
args_list = [(0,), (1,), (2,)]
|
|
|
|
@staticmethod
|
|
def args_to_string(index):
|
|
return str(index)
|
|
|
|
@staticmethod
|
|
def string_to_args(index_string):
|
|
result = int(index_string)
|
|
assert(result in [0, 1, 2])
|
|
return result
|
|
|
|
def construct(self, index):
|
|
equation = get_equation(index)
|
|
equation.to_corner(UP+LEFT)
|
|
top = TOP(2, 3, 8)
|
|
new_top = top.copy()
|
|
equals = TexMobject("=").scale(1.5)
|
|
new_top.next_to(equals, LEFT, buff = 1)
|
|
new_top.values[index].next_to(equals, RIGHT, buff = 1)
|
|
circle = Circle(
|
|
radius = 1.7*top.radius,
|
|
color = OPERATION_COLORS[index]
|
|
)
|
|
|
|
|
|
self.add(equation, top)
|
|
self.wait()
|
|
self.play(
|
|
Transform(top, new_top),
|
|
ShowCreation(equals)
|
|
)
|
|
|
|
circle.shift(new_top.triangle.get_center_of_mass())
|
|
new_circle = circle.copy()
|
|
new_top.put_on_vertex(index, new_circle)
|
|
self.wait()
|
|
self.play(ShowCreation(circle))
|
|
self.wait()
|
|
self.play(
|
|
Transform(circle, new_circle),
|
|
ApplyMethod(new_top.values[index].set_color, circle.color)
|
|
)
|
|
self.wait()
|
|
|
|
|
|
class AllThree(Scene):
|
|
def construct(self):
|
|
tops = []
|
|
equations = []
|
|
args = (2, 3, 8)
|
|
for i in 2, 1, 0:
|
|
new_args = list(args)
|
|
new_args[i] = None
|
|
top = TOP(*new_args, triangle_height_to_number_height = 2)
|
|
# top.set_color(OPERATION_COLORS[i])
|
|
top.shift(i*4.5*LEFT)
|
|
equation = get_equation(i, expression_only = True)
|
|
equation.scale(3)
|
|
equation.next_to(top, DOWN, buff = 0.7)
|
|
tops.append(top)
|
|
equations.append(equation)
|
|
VMobject(*tops+equations).center()
|
|
# name = TextMobject("Triangle of Power")
|
|
# name.to_edge(UP)
|
|
|
|
for top, eq in zip(tops, equations):
|
|
self.play(FadeIn(top), FadeIn(eq))
|
|
self.wait(3)
|
|
# self.play(Write(name))
|
|
self.wait()
|
|
|
|
class SixDifferentInverses(Scene):
|
|
def construct(self):
|
|
rules = get_inverse_rules()
|
|
vects = it.starmap(op.add, it.product(
|
|
[3*UP, 0.5*UP, 2*DOWN], [2*LEFT, 2*RIGHT]
|
|
))
|
|
for rule, vect in zip(rules, vects):
|
|
rule.shift(vect)
|
|
general_idea = TexMobject("f(f^{-1}(a)) = a")
|
|
|
|
self.play(Write(VMobject(*rules)))
|
|
self.wait()
|
|
for s, color in (rules[:4], GREEN), (rules[4:], RED):
|
|
mob = VMobject(*s)
|
|
self.play(ApplyMethod(mob.set_color, color))
|
|
self.wait()
|
|
self.play(ApplyMethod(mob.set_color, WHITE))
|
|
self.play(
|
|
ApplyMethod(VMobject(*rules[::2]).to_edge, LEFT),
|
|
ApplyMethod(VMobject(*rules[1::2]).to_edge, RIGHT),
|
|
GrowFromCenter(general_idea)
|
|
)
|
|
self.wait()
|
|
|
|
top_rules = get_top_inverse_rules()
|
|
for rule, top_rule in zip(rules, top_rules):
|
|
top_rule.set_height(1.5)
|
|
top_rule.center()
|
|
top_rule.shift(rule.get_center())
|
|
self.play(*list(map(FadeOut, rules)))
|
|
self.remove(*rules)
|
|
self.play(*list(map(GrowFromCenter, top_rules)))
|
|
self.wait()
|
|
self.remove(general_idea)
|
|
rules = get_inverse_rules()
|
|
original = None
|
|
for i, (top_rule, rule) in enumerate(zip(top_rules, rules)):
|
|
rule.center().to_edge(UP)
|
|
rule.set_color(GREEN if i < 4 else RED)
|
|
self.add(rule)
|
|
new_top_rule = top_rule.copy().center().scale(1.5)
|
|
anims = [Transform(top_rule, new_top_rule)]
|
|
if original is not None:
|
|
anims.append(FadeIn(original))
|
|
original = top_rule.copy()
|
|
self.play(*anims)
|
|
self.wait()
|
|
self.animate_top_rule(top_rule)
|
|
self.remove(rule)
|
|
|
|
def animate_top_rule(self, top_rule):
|
|
lil_top, lil_symbol, symbol_index = None, None, None
|
|
big_top = top_rule.submobjects[0]
|
|
equals, right_symbol = top_rule.submobjects[1].split()
|
|
for i, value in enumerate(big_top.values):
|
|
if isinstance(value, TOP):
|
|
lil_top = value
|
|
elif isinstance(value, TexMobject):
|
|
symbol_index = i
|
|
else:
|
|
lil_symbol_index = i
|
|
lil_symbol = lil_top.values[lil_symbol_index]
|
|
|
|
assert(lil_top is not None and lil_symbol is not None)
|
|
cancel_parts = [
|
|
VMobject(top.triangle, top.values[symbol_index])
|
|
for top in (lil_top, big_top)
|
|
]
|
|
new_symbol = lil_symbol.copy()
|
|
new_symbol.replace(right_symbol)
|
|
vect = equals.get_center() - right_symbol.get_center()
|
|
new_symbol.shift(2*vect[0]*RIGHT)
|
|
self.play(
|
|
Transform(*cancel_parts, rate_func = rush_into)
|
|
)
|
|
self.play(
|
|
FadeOut(VMobject(*cancel_parts)),
|
|
Transform(lil_symbol, new_symbol, rate_func = rush_from)
|
|
)
|
|
self.wait()
|
|
self.remove(lil_symbol, top_rule, VMobject(*cancel_parts))
|
|
|
|
|
|
class SixSixSix(Scene):
|
|
def construct(self):
|
|
randy = Randolph(mode = "pondering").to_corner()
|
|
bubble = ThoughtBubble().pin_to(randy)
|
|
rules = get_inverse_rules()
|
|
sixes = TexMobject(["6", "6", "6"], next_to_buff = 1)
|
|
sixes.to_corner(UP+RIGHT)
|
|
sixes = sixes.split()
|
|
speech_bubble = SpeechBubble()
|
|
speech_bubble.pin_to(randy)
|
|
speech_bubble.write("I'll just study art!")
|
|
|
|
self.add(randy)
|
|
self.play(ShowCreation(bubble))
|
|
bubble.add_content(VectorizedPoint())
|
|
for i, rule in enumerate(rules):
|
|
if i%2 == 0:
|
|
anim = ShowCreation(sixes[i/2])
|
|
else:
|
|
anim = Blink(randy)
|
|
self.play(
|
|
ApplyMethod(bubble.add_content, rule),
|
|
anim
|
|
)
|
|
self.wait()
|
|
self.wait()
|
|
words = speech_bubble.content
|
|
equation = bubble.content
|
|
speech_bubble.clear()
|
|
bubble.clear()
|
|
self.play(
|
|
ApplyMethod(randy.change_mode, "angry"),
|
|
Transform(bubble, speech_bubble),
|
|
Transform(equation, words),
|
|
FadeOut(VMobject(*sixes))
|
|
)
|
|
self.wait()
|
|
|
|
class AdditiveProperty(Scene):
|
|
def construct(self):
|
|
exp_rule, log_rule = self.write_old_style_rules()
|
|
t_exp_rule, t_log_rule = self.get_new_style_rules()
|
|
|
|
self.play(
|
|
ApplyMethod(exp_rule.to_edge, UP),
|
|
ApplyMethod(log_rule.to_edge, DOWN, 1.5)
|
|
)
|
|
t_exp_rule.next_to(exp_rule, DOWN)
|
|
t_exp_rule.set_color(GREEN)
|
|
t_log_rule.next_to(log_rule, UP)
|
|
t_log_rule.set_color(RED)
|
|
self.play(
|
|
FadeIn(t_exp_rule),
|
|
FadeIn(t_log_rule),
|
|
ApplyMethod(exp_rule.set_color, GREEN),
|
|
ApplyMethod(log_rule.set_color, RED),
|
|
)
|
|
self.wait()
|
|
all_tops = [m for m in t_exp_rule.split()+t_log_rule.split() if isinstance(m, TOP)]
|
|
self.put_in_circles(all_tops)
|
|
self.set_color_appropriate_parts(t_exp_rule, t_log_rule)
|
|
|
|
|
|
|
|
|
|
def write_old_style_rules(self):
|
|
start = TexMobject("a^x a^y = a^{x+y}")
|
|
end = TexMobject("\\log_a(xy) = \\log_a(x) + \\log_a(y)")
|
|
start.shift(UP)
|
|
end.shift(DOWN)
|
|
a1, x1, a2, y1, eq1, a3, p1, x2, y2 = start.split()
|
|
a4, x3, y3, eq2, a5, x4, p2, a6, y4 = np.array(end.split())[
|
|
[3, 5, 6, 8, 12, 14, 16, 20, 22]
|
|
]
|
|
start_copy = start.copy()
|
|
self.play(Write(start_copy))
|
|
self.wait()
|
|
self.play(Transform(
|
|
VMobject(a1, x1, a2, y1, eq1, a3, p1, x2, a3.copy(), y2),
|
|
VMobject(a4, x3, a4.copy(), y3, eq2, a5, p2, x4, a6, y4)
|
|
))
|
|
self.play(Write(end))
|
|
self.clear()
|
|
self.add(start_copy, end)
|
|
self.wait()
|
|
return start_copy, end
|
|
|
|
def get_new_style_rules(self):
|
|
upper_mobs = [
|
|
TOP("a", "x", "R"), Dot(),
|
|
TOP("a", "y", "R"), TexMobject("="),
|
|
TOP("a", "x+y")
|
|
]
|
|
lower_mobs = [
|
|
TOP("a", None, "xy"), TexMobject("="),
|
|
TOP("a", None, "x"), TexMobject("+"),
|
|
TOP("a", None, "y"),
|
|
]
|
|
for mob in upper_mobs + lower_mobs:
|
|
if isinstance(mob, TOP):
|
|
mob.scale(0.5)
|
|
for group in upper_mobs, lower_mobs:
|
|
for m1, m2 in zip(group, group[1:]):
|
|
m2.next_to(m1)
|
|
for top in upper_mobs[0], upper_mobs[2]:
|
|
top.set_value(2, None)
|
|
upper_mobs = VMobject(*upper_mobs).center().shift(2*UP)
|
|
lower_mobs = VMobject(*lower_mobs).center().shift(2*DOWN)
|
|
return upper_mobs, lower_mobs
|
|
|
|
def put_in_circles(self, tops):
|
|
anims = []
|
|
for top in tops:
|
|
for i, value in enumerate(top.values):
|
|
if isinstance(value, VectorizedPoint):
|
|
index = i
|
|
circle = top.put_on_vertex(index, Circle(color = WHITE))
|
|
anims.append(
|
|
Transform(top.copy().set_color(YELLOW), circle)
|
|
)
|
|
self.add(*[anim.mobject for anim in anims])
|
|
self.wait()
|
|
self.play(*anims)
|
|
self.wait()
|
|
|
|
def set_color_appropriate_parts(self, t_exp_rule, t_log_rule):
|
|
#Horribly hacky
|
|
circle1 = t_exp_rule.split()[0].put_on_vertex(
|
|
2, Circle()
|
|
)
|
|
top_dot = t_exp_rule.split()[1]
|
|
circle2 = t_exp_rule.split()[2].put_on_vertex(
|
|
2, Circle()
|
|
)
|
|
top_plus = t_exp_rule.split()[4].values[1]
|
|
|
|
bottom_times = t_log_rule.split()[0].values[2]
|
|
circle3 = t_log_rule.split()[2].put_on_vertex(
|
|
1, Circle()
|
|
)
|
|
bottom_plus = t_log_rule.split()[3]
|
|
circle4 = t_log_rule.split()[4].put_on_vertex(
|
|
1, Circle()
|
|
)
|
|
|
|
mob_lists = [
|
|
[circle1, top_dot, circle2],
|
|
[top_plus],
|
|
[bottom_times],
|
|
[circle3, bottom_plus, circle4]
|
|
]
|
|
for mobs in mob_lists:
|
|
copies = VMobject(*mobs).copy()
|
|
self.play(ApplyMethod(
|
|
copies.set_color, YELLOW,
|
|
run_time = 0.5
|
|
))
|
|
self.play(ApplyMethod(
|
|
copies.scale_in_place, 1.2,
|
|
rate_func = there_and_back
|
|
))
|
|
self.wait()
|
|
self.remove(copies)
|
|
|
|
|
|
class DrawInsideTriangle(Scene):
|
|
def construct(self):
|
|
top = TOP()
|
|
top.scale(2)
|
|
dot = top.put_in_vertex(0, Dot())
|
|
plus = top.put_in_vertex(1, TexMobject("+"))
|
|
times = top.put_in_vertex(2, TexMobject("\\times"))
|
|
plus.set_color(GREEN)
|
|
times.set_color(YELLOW)
|
|
|
|
self.add(top)
|
|
self.wait()
|
|
for mob in dot, plus, times:
|
|
self.play(Write(mob, run_time = 1))
|
|
self.wait()
|
|
|
|
class ConstantOnTop(Scene):
|
|
def construct(self):
|
|
top = TOP()
|
|
dot = top.put_in_vertex(1, Dot())
|
|
times1 = top.put_in_vertex(0, TexMobject("\\times"))
|
|
times2 = top.put_in_vertex(2, TexMobject("\\times"))
|
|
times1.set_color(YELLOW)
|
|
times2.set_color(YELLOW)
|
|
three = top.put_on_vertex(1, "3")
|
|
lower_left_x = top.put_on_vertex(0, "x")
|
|
lower_right_x = top.put_on_vertex(2, "x")
|
|
x_cubed = TexMobject("x^3").to_edge(UP)
|
|
x_cubed.submobjects.reverse() #To align better
|
|
cube_root_x = TexMobject("\\sqrt[3]{x}").to_edge(UP)
|
|
|
|
self.add(top)
|
|
self.play(ShowCreation(three))
|
|
self.play(
|
|
FadeIn(lower_left_x),
|
|
Write(x_cubed),
|
|
run_time = 1
|
|
)
|
|
self.wait()
|
|
self.play(*[
|
|
Transform(*pair, path_arc = np.pi)
|
|
for pair in [
|
|
(lower_left_x, lower_right_x),
|
|
(x_cubed, cube_root_x),
|
|
]
|
|
])
|
|
self.wait(2)
|
|
for mob in dot, times1, times2:
|
|
self.play(ShowCreation(mob))
|
|
self.wait()
|
|
|
|
def get_const_top_TOP(*args):
|
|
top = TOP(*args)
|
|
dot = top.put_in_vertex(1, Dot())
|
|
times1 = top.put_in_vertex(0, TexMobject("\\times"))
|
|
times2 = top.put_in_vertex(2, TexMobject("\\times"))
|
|
times1.set_color(YELLOW)
|
|
times2.set_color(YELLOW)
|
|
top.add(dot, times1, times2)
|
|
return top
|
|
|
|
|
|
class MultiplyWithConstantTop(Scene):
|
|
def construct(self):
|
|
top1 = get_const_top_TOP("x", "3")
|
|
top2 = get_const_top_TOP("y", "3")
|
|
top3 = get_const_top_TOP("xy", "3")
|
|
times = TexMobject("\\times")
|
|
equals = TexMobject("=")
|
|
top_exp_equation = VMobject(
|
|
top1, times, top2, equals, top3
|
|
)
|
|
top_exp_equation.arrange()
|
|
old_style_exp = TexMobject("(x^3)(y^3) = (xy)^3")
|
|
old_style_exp.to_edge(UP)
|
|
old_style_exp.set_color(GREEN)
|
|
old_style_rad = TexMobject("\\sqrt[3]{x} \\sqrt[3]{y} = \\sqrt[3]{xy}")
|
|
old_style_rad.to_edge(UP)
|
|
old_style_rad.set_color(RED)
|
|
|
|
self.add(top_exp_equation, old_style_exp)
|
|
self.wait(3)
|
|
|
|
old_tops = [top1, top2, top3]
|
|
new_tops = []
|
|
for top in old_tops:
|
|
new_top = top.copy()
|
|
new_top.put_on_vertex(2, new_top.values[0])
|
|
new_top.shift(0.5*LEFT)
|
|
new_tops.append(new_top)
|
|
self.play(
|
|
Transform(old_style_exp, old_style_rad),
|
|
Transform(
|
|
VMobject(*old_tops),
|
|
VMobject(*new_tops),
|
|
path_arc = np.pi/2
|
|
)
|
|
)
|
|
self.wait(3)
|
|
|
|
class RightStaysConstantQ(Scene):
|
|
def construct(self):
|
|
top1, top2, top3 = old_tops = [
|
|
TOP(None, s, "8")
|
|
for s in ("x", "y", TexMobject("x?y"))
|
|
]
|
|
q_mark = TexMobject("?").scale(2)
|
|
equation = VMobject(
|
|
top1, q_mark, top2, TexMobject("="), top3
|
|
)
|
|
equation.arrange(buff = 0.7)
|
|
symbols_at_top = VMobject(*[
|
|
top.values[1]
|
|
for top in (top1, top2, top3)
|
|
])
|
|
symbols_at_lower_right = VMobject(*[
|
|
top.put_on_vertex(0, top.values[1].copy())
|
|
for top in (top1, top2, top3)
|
|
])
|
|
old_style_eq1 = TexMobject("\\sqrt[x]{8} ? \\sqrt[y]{8} = \\sqrt[x?y]{8}")
|
|
old_style_eq1.set_color(BLUE)
|
|
old_style_eq2 = TexMobject("\\log_x(8) ? \\log_y(8) = \\log_{x?y}(8)")
|
|
old_style_eq2.set_color(YELLOW)
|
|
for eq in old_style_eq1, old_style_eq2:
|
|
eq.to_edge(UP)
|
|
|
|
randy = Randolph()
|
|
randy.to_corner()
|
|
bubble = ThoughtBubble().pin_to(randy)
|
|
bubble.add_content(TOP(None, None, "8"))
|
|
|
|
self.add(randy, bubble)
|
|
self.play(ApplyMethod(randy.change_mode, "pondering"))
|
|
self.wait(3)
|
|
triangle = bubble.content.triangle
|
|
eight = bubble.content.values[2]
|
|
bubble.clear()
|
|
self.play(
|
|
Transform(triangle, equation),
|
|
FadeOut(eight),
|
|
ApplyPointwiseFunction(
|
|
lambda p : (p+2*DOWN)*15/get_norm(p+2*DOWN),
|
|
bubble
|
|
),
|
|
FadeIn(old_style_eq1),
|
|
ApplyMethod(randy.shift, 3*DOWN + 3*LEFT),
|
|
run_time = 2
|
|
)
|
|
self.remove(triangle)
|
|
self.add(equation)
|
|
self.wait(4)
|
|
self.play(
|
|
Transform(
|
|
symbols_at_top, symbols_at_lower_right,
|
|
path_arc = np.pi/2
|
|
),
|
|
Transform(old_style_eq1, old_style_eq2)
|
|
)
|
|
self.wait(2)
|
|
|
|
|
|
class AOplusB(Scene):
|
|
def construct(self):
|
|
self.add(TexMobject(
|
|
"a \\oplus b = \\dfrac{1}{\\frac{1}{a} + \\frac{1}{b}}"
|
|
).scale(2))
|
|
self.wait()
|
|
|
|
|
|
class ConstantLowerRight(Scene):
|
|
def construct(self):
|
|
top = TOP()
|
|
times = top.put_in_vertex(0, TexMobject("\\times"))
|
|
times.set_color(YELLOW)
|
|
oplus = top.put_in_vertex(1, TexMobject("\\oplus"))
|
|
oplus.set_color(BLUE)
|
|
dot = top.put_in_vertex(2, Dot())
|
|
eight = top.put_on_vertex(2, TexMobject("8"))
|
|
|
|
self.add(top)
|
|
self.play(ShowCreation(eight))
|
|
for mob in dot, oplus, times:
|
|
self.play(ShowCreation(mob))
|
|
self.wait()
|
|
|
|
top.add(eight)
|
|
top.add(times, oplus, dot)
|
|
top1, top2, top3 = tops = [
|
|
top.copy() for i in range(3)
|
|
]
|
|
big_oplus = TexMobject("\\oplus").scale(2).set_color(BLUE)
|
|
equals = TexMobject("=")
|
|
equation = VMobject(
|
|
top1, big_oplus, top2, equals, top3
|
|
)
|
|
equation.arrange()
|
|
top3.shift(0.5*RIGHT)
|
|
x, y, xy = [
|
|
t.put_on_vertex(0, s)
|
|
for t, s in zip(tops, ["x", "y", "xy"])
|
|
]
|
|
old_style_eq = TexMobject(
|
|
"\\dfrac{1}{\\frac{1}{\\log_x(8)} + \\frac{1}{\\log_y(8)}} = \\log_{xy}(8)"
|
|
)
|
|
old_style_eq.to_edge(UP).set_color(RED)
|
|
|
|
triple_top_copy = VMobject(*[
|
|
top.copy() for i in range(3)
|
|
])
|
|
self.clear()
|
|
self.play(
|
|
Transform(triple_top_copy, VMobject(*tops)),
|
|
FadeIn(VMobject(x, y, xy, big_oplus, equals))
|
|
)
|
|
self.remove(triple_top_copy)
|
|
self.add(*tops)
|
|
self.play(Write(old_style_eq))
|
|
self.wait(3)
|
|
|
|
syms = VMobject(x, y, xy)
|
|
new_syms = VMobject(*[
|
|
t.put_on_vertex(1, s)
|
|
for t, s in zip(tops, ["x", "y", "x \\oplus y"])
|
|
])
|
|
new_old_style_eq = TexMobject(
|
|
"\\sqrt[x]{8} \\sqrt[y]{8} = \\sqrt[X]{8}"
|
|
)
|
|
X = new_old_style_eq.split()[-4]
|
|
frac = TexMobject("\\frac{1}{\\frac{1}{x} + \\frac{1}{y}}")
|
|
frac.replace(X)
|
|
frac_lower_right = frac.get_corner(DOWN+RIGHT)
|
|
frac.scale(2)
|
|
frac.shift(frac_lower_right - frac.get_corner(DOWN+RIGHT))
|
|
new_old_style_eq.submobjects[-4] = frac
|
|
new_old_style_eq.to_edge(UP)
|
|
new_old_style_eq.set_color(RED)
|
|
big_times = TexMobject("\\times").set_color(YELLOW)
|
|
big_times.shift(big_oplus.get_center())
|
|
self.play(
|
|
Transform(old_style_eq, new_old_style_eq),
|
|
Transform(syms, new_syms, path_arc = np.pi/2),
|
|
Transform(big_oplus, big_times)
|
|
)
|
|
self.wait(4)
|
|
|
|
|
|
class TowerExponentFrame(Scene):
|
|
def construct(self):
|
|
words = TextMobject("""
|
|
Consider an expression like $3^{3^3}$. It's
|
|
ambiguous whether this means $27^3$ or $3^{27}$,
|
|
which is the difference between $19{,}683$ and
|
|
$7{,}625{,}597{,}484{,}987$. But with the triangle
|
|
of power, the difference is crystal clear:
|
|
""")
|
|
words.set_width(FRAME_WIDTH-1)
|
|
words.to_edge(UP)
|
|
top1 = TOP(TOP(3, 3), 3)
|
|
top2 = TOP(3, (TOP(3, 3)))
|
|
for top in top1, top2:
|
|
top.next_to(words, DOWN)
|
|
top1.shift(3*LEFT)
|
|
top2.shift(3*RIGHT)
|
|
|
|
self.add(words, top1, top2)
|
|
self.wait()
|
|
|
|
|
|
class ExponentialGrowth(Scene):
|
|
def construct(self):
|
|
words = TextMobject("""
|
|
Let's say you are studying a certain growth rate,
|
|
and you come across an expression like $T^a$. It
|
|
matters a lot whether you consider $T$ or $a$
|
|
to be the variable, since exponential growth and
|
|
polynomial growth have very different flavors. The
|
|
nice thing about having a triangle that you can write
|
|
inside is that you can clarify this kind of ambiguity
|
|
by writing a little dot next to the constant and
|
|
a ``$\\sim$'' next to the variable.
|
|
""")
|
|
words.scale(0.75)
|
|
words.to_edge(UP)
|
|
top = TOP("T", "a")
|
|
top.next_to(words, DOWN)
|
|
dot = top.put_in_vertex(0, TexMobject("\\cdot"))
|
|
sim = top.put_in_vertex(1, TexMobject("\\sim"))
|
|
|
|
self.add(words, top, dot, sim)
|
|
self.show_frame()
|
|
self.wait()
|
|
|
|
|
|
|
|
|
|
class GoExplore(Scene):
|
|
def construct(self):
|
|
explore = TextMobject("Go explore!")
|
|
by_the_way = TextMobject("by the way \\dots")
|
|
by_the_way.shift(20*RIGHT)
|
|
|
|
self.play(Write(explore))
|
|
self.wait(4)
|
|
self.play(
|
|
ApplyMethod(
|
|
VMobject(explore, by_the_way).shift,
|
|
20*LEFT
|
|
)
|
|
)
|
|
self.wait(3)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|