mirror of
https://github.com/3b1b/manim.git
synced 2025-07-30 21:44:19 +08:00
906
active_projects/moduli.py
Normal file
906
active_projects/moduli.py
Normal file
@ -0,0 +1,906 @@
|
|||||||
|
from manimlib.imports import *
|
||||||
|
|
||||||
|
|
||||||
|
class TriangleModuliSpace(Scene):
|
||||||
|
CONFIG = {
|
||||||
|
"camera_config": {
|
||||||
|
"background_image": "chalkboard",
|
||||||
|
},
|
||||||
|
"degen_color": GREEN_D,
|
||||||
|
"x1_color": GREEN_B,
|
||||||
|
"y1_color": RED,
|
||||||
|
"x_eq_y_color": YELLOW,
|
||||||
|
"right_color": TEAL,
|
||||||
|
"obtuse_color": PINK,
|
||||||
|
"acute_color": GREEN,
|
||||||
|
"triangle_fill_opacity": 0.5,
|
||||||
|
"random_seed": 0,
|
||||||
|
"example_triangle_width": 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
self.plane = NumberPlane(
|
||||||
|
axis_config={
|
||||||
|
"unit_size": 2,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
# self.show_meaning_of_similar()
|
||||||
|
self.show_xy_rule()
|
||||||
|
|
||||||
|
def show_meaning_of_similar(self):
|
||||||
|
# Setup titles
|
||||||
|
title = TextMobject("Space", " of all ", "trianlges")
|
||||||
|
title.scale(1.5)
|
||||||
|
title.to_edge(UP)
|
||||||
|
|
||||||
|
subtitle = TextMobject("up to similarity.")
|
||||||
|
subtitle.scale(1.5)
|
||||||
|
subtitle.next_to(title, DOWN, MED_SMALL_BUFF)
|
||||||
|
|
||||||
|
question = TextMobject("What ", "is ", "a\\\\", "moduli ", "space", "?")
|
||||||
|
question.scale(2)
|
||||||
|
|
||||||
|
# Setup all triangles
|
||||||
|
all_triangles, tri_classes = self.get_triangles_and_classes()
|
||||||
|
tri_classes[2][1].scale(0.5)
|
||||||
|
tri_classes[2][1].scalar *= 0.5
|
||||||
|
|
||||||
|
all_triangles.to_edge(DOWN)
|
||||||
|
|
||||||
|
# Triangles pop up...
|
||||||
|
self.play(
|
||||||
|
LaggedStartMap(FadeInFromDown, question),
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
ReplacementTransform(
|
||||||
|
question.get_part_by_tex("space"),
|
||||||
|
title.get_part_by_tex("Space"),
|
||||||
|
),
|
||||||
|
FadeOut(question[:-2]),
|
||||||
|
FadeOut(question[-1]),
|
||||||
|
FadeIn(title[1:]),
|
||||||
|
LaggedStartMap(
|
||||||
|
DrawBorderThenFill, all_triangles,
|
||||||
|
rate_func=bezier([0, 0, 1.5, 1, 1]),
|
||||||
|
run_time=5,
|
||||||
|
lag_ratio=0.05,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# ...Then divide into classes
|
||||||
|
tri_classes.generate_target()
|
||||||
|
colors = Color(BLUE).range_to(Color(RED), len(tri_classes))
|
||||||
|
for group, color in zip(tri_classes.target, colors):
|
||||||
|
group.arrange(DOWN)
|
||||||
|
group.set_color(color)
|
||||||
|
tri_classes.target.arrange(RIGHT, buff=1.25, aligned_edge=UP)
|
||||||
|
tri_classes.target.scale(0.85)
|
||||||
|
tri_classes.target.to_corner(DL)
|
||||||
|
max_width = max([tc.get_width() for tc in tri_classes.target])
|
||||||
|
height = tri_classes.target.get_height() + 0.5
|
||||||
|
rects = VGroup(*[
|
||||||
|
Rectangle(
|
||||||
|
height=height,
|
||||||
|
width=max_width + 0.25,
|
||||||
|
stroke_width=2,
|
||||||
|
).move_to(tri_class, UP)
|
||||||
|
for tri_class in tri_classes.target
|
||||||
|
])
|
||||||
|
rects.shift(MED_SMALL_BUFF * UP)
|
||||||
|
|
||||||
|
# Dumb shifts
|
||||||
|
# tri_classes.target[1][2].shift(0.25 * UP)
|
||||||
|
tri_classes.target[2].scale(0.9, about_edge=UP)
|
||||||
|
tri_classes.target[2][2].shift(0.2 * UP)
|
||||||
|
tri_classes.target[3][0].shift(0.5 * DOWN)
|
||||||
|
tri_classes.target[3][1].shift(1.0 * DOWN)
|
||||||
|
tri_classes.target[3][2].shift(1.2 * DOWN)
|
||||||
|
tri_classes.target[4][1:].shift(0.7 * UP)
|
||||||
|
|
||||||
|
# Dots
|
||||||
|
per_class_dots = VGroup(*[
|
||||||
|
TexMobject("\\vdots").move_to(
|
||||||
|
tri_class
|
||||||
|
).set_y(rects.get_bottom()[1] + 0.4)
|
||||||
|
for tri_class in tri_classes.target
|
||||||
|
])
|
||||||
|
all_class_dots = TexMobject("\\dots").next_to(
|
||||||
|
rects, RIGHT, MED_SMALL_BUFF,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
FadeInFromDown(subtitle),
|
||||||
|
MoveToTarget(tri_classes),
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
LaggedStartMap(FadeIn, rects),
|
||||||
|
Write(per_class_dots),
|
||||||
|
Write(all_class_dots),
|
||||||
|
)
|
||||||
|
self.wait(2)
|
||||||
|
|
||||||
|
# Similar
|
||||||
|
|
||||||
|
tri1 = tri_classes[2][1]
|
||||||
|
tri2 = tri_classes[2][2]
|
||||||
|
tri1.save_state()
|
||||||
|
tri2.save_state()
|
||||||
|
|
||||||
|
sim_sign = TexMobject("\\sim")
|
||||||
|
sim_sign.set_width(1)
|
||||||
|
sim_sign.move_to(midpoint(rects.get_top(), TOP))
|
||||||
|
sim_sign.shift(0.25 * DOWN)
|
||||||
|
|
||||||
|
similar_word = TextMobject("Similar")
|
||||||
|
similar_word.scale(1.5)
|
||||||
|
similar_word.move_to(sim_sign)
|
||||||
|
similar_word.to_edge(UP)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
FadeOutAndShift(VGroup(title, subtitle), UP),
|
||||||
|
tri1.next_to, sim_sign, LEFT, 0.75,
|
||||||
|
tri2.next_to, sim_sign, RIGHT, 0.75,
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
FadeInFromDown(sim_sign),
|
||||||
|
Write(similar_word, run_time=1)
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# Move into place
|
||||||
|
tri1_copy = tri1.copy()
|
||||||
|
self.play(
|
||||||
|
tri1_copy.next_to, tri2,
|
||||||
|
RIGHT, LARGE_BUFF,
|
||||||
|
path_arc=90 * DEGREES,
|
||||||
|
)
|
||||||
|
self.play(Rotate(tri1_copy, tri2.angle - tri1.angle))
|
||||||
|
self.play(tri1_copy.scale, tri2.scalar / tri1.scalar)
|
||||||
|
self.play(
|
||||||
|
tri1_copy.move_to, tri2,
|
||||||
|
)
|
||||||
|
tri1_copy.set_color(YELLOW)
|
||||||
|
self.play(
|
||||||
|
FadeOut(tri1_copy),
|
||||||
|
rate_func=rush_from,
|
||||||
|
)
|
||||||
|
self.wait(2)
|
||||||
|
|
||||||
|
# Show non-similar example
|
||||||
|
not_similar_word = TextMobject("Not ", "Similar")
|
||||||
|
not_similar_word.scale(1.5)
|
||||||
|
not_similar_word.move_to(similar_word)
|
||||||
|
not_similar_word.set_color(RED)
|
||||||
|
|
||||||
|
sim_cross = Line(DL, UR)
|
||||||
|
sim_cross.set_color(RED)
|
||||||
|
sim_cross.match_width(sim_sign)
|
||||||
|
sim_cross.move_to(sim_sign)
|
||||||
|
sim_cross.set_stroke(BLACK, 5, background=True)
|
||||||
|
|
||||||
|
tri3 = tri_classes[1][2]
|
||||||
|
tri3.save_state()
|
||||||
|
tri3.generate_target()
|
||||||
|
tri3.target.move_to(tri2, LEFT)
|
||||||
|
|
||||||
|
tri1_copy = tri1.copy()
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
Restore(tri2),
|
||||||
|
MoveToTarget(tri3),
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
ReplacementTransform(
|
||||||
|
similar_word[0],
|
||||||
|
not_similar_word[1],
|
||||||
|
),
|
||||||
|
GrowFromCenter(not_similar_word[0]),
|
||||||
|
ShowCreation(sim_cross),
|
||||||
|
)
|
||||||
|
self.play(tri1_copy.move_to, tri3)
|
||||||
|
self.play(Rotate(tri1_copy, 90 * DEGREES))
|
||||||
|
self.play(
|
||||||
|
tri1_copy.match_height, tri3,
|
||||||
|
tri1_copy.move_to, tri3, RIGHT,
|
||||||
|
)
|
||||||
|
self.play(WiggleOutThenIn(tri1_copy, n_wiggles=10))
|
||||||
|
self.play(FadeOut(tri1_copy))
|
||||||
|
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# Back to classes
|
||||||
|
new_title = TextMobject("Space of all\\\\", "Similarity classes")
|
||||||
|
new_title.scale(1.5)
|
||||||
|
new_title[1].set_color(YELLOW)
|
||||||
|
new_title.to_edge(UP)
|
||||||
|
new_title_underline = Line(LEFT, RIGHT)
|
||||||
|
new_title_underline.match_width(new_title[1])
|
||||||
|
new_title_underline.match_color(new_title[1])
|
||||||
|
new_title_underline.next_to(new_title, DOWN, buff=0.05)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
Restore(tri1),
|
||||||
|
Restore(tri2),
|
||||||
|
Restore(tri3),
|
||||||
|
FadeOut(not_similar_word),
|
||||||
|
FadeOut(sim_sign),
|
||||||
|
FadeOut(sim_cross),
|
||||||
|
FadeInFrom(new_title[1], UP),
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
ShowCreationThenDestruction(new_title_underline),
|
||||||
|
LaggedStartMap(
|
||||||
|
ApplyMethod, rects,
|
||||||
|
lambda m: (m.set_stroke, YELLOW, 5),
|
||||||
|
rate_func=there_and_back,
|
||||||
|
run_time=1,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.play(Write(new_title[0]))
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# Show abstract space
|
||||||
|
blob = ThoughtBubble()[-1]
|
||||||
|
blob.set_height(2)
|
||||||
|
blob.to_corner(UR)
|
||||||
|
|
||||||
|
dots = VGroup(*[
|
||||||
|
Dot(color=tri.get_color()).move_to(
|
||||||
|
self.get_triangle_x(tri) * RIGHT +
|
||||||
|
self.get_triangle_y(tri) * UP,
|
||||||
|
)
|
||||||
|
for tri_class in tri_classes
|
||||||
|
for tri in tri_class[0]
|
||||||
|
])
|
||||||
|
dots.space_out_submobjects(2)
|
||||||
|
dots.move_to(blob)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
DrawBorderThenFill(blob),
|
||||||
|
new_title.shift, LEFT,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.play(LaggedStart(
|
||||||
|
*[
|
||||||
|
ReplacementTransform(
|
||||||
|
tri_class.copy().set_fill(opacity=0),
|
||||||
|
dot
|
||||||
|
)
|
||||||
|
for tri_class, dot in zip(tri_classes, dots)
|
||||||
|
],
|
||||||
|
run_time=3,
|
||||||
|
lag_ratio=0.3,
|
||||||
|
))
|
||||||
|
|
||||||
|
# Isolate one triangle
|
||||||
|
|
||||||
|
tri = tri_classes[0][0]
|
||||||
|
verts = tri.get_vertices()
|
||||||
|
angle = PI + angle_of_vector(verts[1] - verts[2])
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
tri.rotate, -angle,
|
||||||
|
tri.set_width, self.example_triangle_width,
|
||||||
|
tri.center,
|
||||||
|
FadeOut(tri_classes[0][1:]),
|
||||||
|
FadeOut(tri_classes[1:]),
|
||||||
|
FadeOut(rects),
|
||||||
|
FadeOut(per_class_dots),
|
||||||
|
FadeOut(all_class_dots),
|
||||||
|
FadeOut(blob),
|
||||||
|
FadeOut(dots),
|
||||||
|
FadeOut(new_title),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.triangle = tri
|
||||||
|
|
||||||
|
def show_xy_rule(self):
|
||||||
|
unit_factor = 4.0
|
||||||
|
|
||||||
|
if hasattr(self, "triangle"):
|
||||||
|
triangle = self.triangle
|
||||||
|
else:
|
||||||
|
triangle = self.get_triangles_and_classes()[0][0]
|
||||||
|
verts = triangle.get_vertices()
|
||||||
|
angle = PI + angle_of_vector(verts[1] - verts[2])
|
||||||
|
triangle.rotate(-angle)
|
||||||
|
triangle.set_width(self.example_triangle_width)
|
||||||
|
triangle.center()
|
||||||
|
self.add(triangle)
|
||||||
|
|
||||||
|
side_trackers = VGroup(*[Line() for x in range(3)])
|
||||||
|
side_trackers.set_stroke(width=0, opacity=0)
|
||||||
|
side_trackers.triangle = triangle
|
||||||
|
|
||||||
|
def update_side_trackers(st):
|
||||||
|
verts = st.triangle.get_vertices()
|
||||||
|
st[0].put_start_and_end_on(verts[0], verts[1])
|
||||||
|
st[1].put_start_and_end_on(verts[1], verts[2])
|
||||||
|
st[2].put_start_and_end_on(verts[2], verts[0])
|
||||||
|
|
||||||
|
side_trackers.add_updater(update_side_trackers)
|
||||||
|
|
||||||
|
def get_length_labels():
|
||||||
|
result = VGroup()
|
||||||
|
for line in side_trackers:
|
||||||
|
vect = normalize(line.get_vector())
|
||||||
|
perp_vect = rotate_vector(vect, -90 * DEGREES)
|
||||||
|
perp_vect = np.round(perp_vect, 1)
|
||||||
|
label = DecimalNumber(line.get_length() / unit_factor)
|
||||||
|
label.move_to(line.get_center())
|
||||||
|
label.next_to(line.get_center(), perp_vect, buff=0.15)
|
||||||
|
result.add(label)
|
||||||
|
return result
|
||||||
|
|
||||||
|
side_labels = always_redraw(get_length_labels)
|
||||||
|
|
||||||
|
b_label, c_label, a_label = side_labels
|
||||||
|
b_side, c_side, a_side = side_trackers
|
||||||
|
|
||||||
|
# Rescale
|
||||||
|
self.add(side_trackers)
|
||||||
|
self.play(LaggedStartMap(FadeIn, side_labels, lag_ratio=0.3, run_time=1))
|
||||||
|
self.add(side_labels)
|
||||||
|
self.wait()
|
||||||
|
self.play(triangle.set_width, unit_factor)
|
||||||
|
self.play(ShowCreationThenFadeAround(c_label))
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# Label x and y
|
||||||
|
x_label = TexMobject("x")
|
||||||
|
y_label = TexMobject("y")
|
||||||
|
xy_labels = VGroup(x_label, y_label)
|
||||||
|
xy_labels.scale(1.5)
|
||||||
|
|
||||||
|
x_color = self.x1_color
|
||||||
|
y_color = self.y1_color
|
||||||
|
|
||||||
|
x_label[0].set_color(x_color)
|
||||||
|
y_label[0].set_color(y_color)
|
||||||
|
|
||||||
|
# side_labels.clear_updaters()
|
||||||
|
for var, num, vect in zip(xy_labels, [b_label, a_label], [DR, DL]):
|
||||||
|
buff = 0.15
|
||||||
|
var.move_to(num, vect)
|
||||||
|
var.brace = Brace(num, UP)
|
||||||
|
var.brace.num = num
|
||||||
|
var.brace.add_updater(
|
||||||
|
lambda m: m.next_to(m.num, UP, buff=buff)
|
||||||
|
)
|
||||||
|
var.add_updater(
|
||||||
|
lambda m: m.next_to(m.brace, UP, buff=buff)
|
||||||
|
)
|
||||||
|
|
||||||
|
var.suspend_updating()
|
||||||
|
var.brace.suspend_updating()
|
||||||
|
self.play(
|
||||||
|
FadeInFrom(var, DOWN),
|
||||||
|
Write(var.brace, run_time=1),
|
||||||
|
# MoveToTarget(num)
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# Show plane
|
||||||
|
to_move = VGroup(
|
||||||
|
triangle,
|
||||||
|
side_labels,
|
||||||
|
x_label,
|
||||||
|
x_label.brace,
|
||||||
|
y_label,
|
||||||
|
y_label.brace,
|
||||||
|
)
|
||||||
|
|
||||||
|
axes = Axes(
|
||||||
|
x_min=-0.25,
|
||||||
|
x_max=1.5,
|
||||||
|
y_min=-0.25,
|
||||||
|
y_max=1.5,
|
||||||
|
number_line_config={
|
||||||
|
"tick_frequency": 0.25,
|
||||||
|
"unit_size": 3,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
x_axis = axes.x_axis
|
||||||
|
y_axis = axes.y_axis
|
||||||
|
|
||||||
|
x_axis.add(TexMobject("x", color=x_color).next_to(x_axis, RIGHT))
|
||||||
|
y_axis.add(TexMobject("y", color=y_color).next_to(y_axis, UP))
|
||||||
|
|
||||||
|
for axis, vect in [(x_axis, DOWN), (y_axis, LEFT)]:
|
||||||
|
axis.add_numbers(
|
||||||
|
0.5, 1.0,
|
||||||
|
number_config={"num_decimal_places": 1},
|
||||||
|
direction=vect,
|
||||||
|
)
|
||||||
|
|
||||||
|
axes.to_corner(DR, buff=LARGE_BUFF)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
to_move.to_corner, UL, {"buff": LARGE_BUFF},
|
||||||
|
to_move.shift, MED_LARGE_BUFF * DOWN,
|
||||||
|
Write(axes),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Show coordinates
|
||||||
|
coords = VGroup(b_label.copy(), a_label.copy())
|
||||||
|
|
||||||
|
x_coord, y_coord = coords
|
||||||
|
x_coord.add_updater(lambda m: m.set_value(b_side.get_length() / unit_factor))
|
||||||
|
y_coord.add_updater(lambda m: m.set_value(a_side.get_length() / unit_factor))
|
||||||
|
|
||||||
|
def get_coord_values():
|
||||||
|
return [c.get_value() for c in coords]
|
||||||
|
|
||||||
|
def get_ms_point():
|
||||||
|
return axes.c2p(*get_coord_values())
|
||||||
|
|
||||||
|
dot = always_redraw(
|
||||||
|
lambda: triangle.copy().set_width(0.1).move_to(get_ms_point())
|
||||||
|
)
|
||||||
|
|
||||||
|
y_line = always_redraw(
|
||||||
|
lambda: DashedLine(
|
||||||
|
x_axis.n2p(x_coord.get_value()),
|
||||||
|
get_ms_point(),
|
||||||
|
color=y_color,
|
||||||
|
stroke_width=1,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
x_line = always_redraw(
|
||||||
|
lambda: DashedLine(
|
||||||
|
y_axis.n2p(y_coord.get_value()),
|
||||||
|
get_ms_point(),
|
||||||
|
color=x_color,
|
||||||
|
stroke_width=1,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
coord_label = TexMobject("(", "0.00", ",", "0.00", ")")
|
||||||
|
cl_buff = 0
|
||||||
|
coord_label.next_to(dot, UR, buff=cl_buff)
|
||||||
|
for i, coord in zip([1, 3], coords):
|
||||||
|
coord.generate_target()
|
||||||
|
coord.target.replace(coord_label[i], dim_to_match=0)
|
||||||
|
coord_label[i].set_opacity(0)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
MoveToTarget(x_coord),
|
||||||
|
MoveToTarget(y_coord),
|
||||||
|
FadeIn(coord_label),
|
||||||
|
ReplacementTransform(triangle.copy().set_fill(opacity=0), dot),
|
||||||
|
)
|
||||||
|
coord_label.add(*coords)
|
||||||
|
coord_label.add_updater(lambda m: m.next_to(dot, UR, buff=cl_buff))
|
||||||
|
self.add(x_label, y_label, dot)
|
||||||
|
self.play(
|
||||||
|
ShowCreation(x_line),
|
||||||
|
ShowCreation(y_line),
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# Adjust triangle
|
||||||
|
tip_tracker = VectorizedPoint(triangle.points[0])
|
||||||
|
|
||||||
|
def update_triangle(tri):
|
||||||
|
point = tip_tracker.get_location()
|
||||||
|
tri.points[0] = point
|
||||||
|
tri.points[-1] = point
|
||||||
|
tri.make_jagged()
|
||||||
|
|
||||||
|
triangle.add_updater(update_triangle)
|
||||||
|
|
||||||
|
self.add(tip_tracker)
|
||||||
|
self.play(tip_tracker.shift, 0.5 * LEFT + 1.0 * UP)
|
||||||
|
self.play(tip_tracker.shift, 2.0 * DOWN)
|
||||||
|
self.play(tip_tracker.shift, 1.5 * RIGHT)
|
||||||
|
self.play(tip_tracker.shift, 1.0 * LEFT + 1.0 * UP)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# Show box
|
||||||
|
t2c = {"x": x_color, "y": y_color}
|
||||||
|
ineq1 = TexMobject("0", "\\le ", "x", "\\le", "1", tex_to_color_map=t2c)
|
||||||
|
ineq2 = TexMobject("0", "\\le ", "y", "\\le", "1", tex_to_color_map=t2c)
|
||||||
|
|
||||||
|
ineqs = VGroup(ineq1, ineq2)
|
||||||
|
ineqs.scale(1.5)
|
||||||
|
ineqs.arrange(DOWN, buff=MED_LARGE_BUFF)
|
||||||
|
ineqs.next_to(triangle, DOWN, buff=1.5)
|
||||||
|
|
||||||
|
box = Square(
|
||||||
|
fill_color=DARK_GREY,
|
||||||
|
fill_opacity=0.75,
|
||||||
|
stroke_color=LIGHT_GREY,
|
||||||
|
stroke_width=2,
|
||||||
|
)
|
||||||
|
box.replace(Line(axes.c2p(0, 0), axes.c2p(1, 1)))
|
||||||
|
box_outline = box.copy()
|
||||||
|
box_outline.set_fill(opacity=0)
|
||||||
|
box_outline.set_stroke(YELLOW, 3)
|
||||||
|
|
||||||
|
self.add(box, axes, x_line, y_line, coord_label, dot)
|
||||||
|
self.play(
|
||||||
|
FadeIn(box),
|
||||||
|
LaggedStartMap(FadeInFromDown, ineqs)
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
ShowCreationThenFadeOut(box_outline)
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# x >= y slice
|
||||||
|
region = Polygon(
|
||||||
|
axes.c2p(0, 0),
|
||||||
|
axes.c2p(1, 0),
|
||||||
|
axes.c2p(1, 1),
|
||||||
|
fill_color=GREY_BROWN,
|
||||||
|
fill_opacity=0.75,
|
||||||
|
stroke_color=GREY_BROWN,
|
||||||
|
stroke_width=2,
|
||||||
|
)
|
||||||
|
region_outline = region.copy()
|
||||||
|
region_outline.set_fill(opacity=0)
|
||||||
|
region_outline.set_stroke(YELLOW, 3)
|
||||||
|
|
||||||
|
x_eq_y_line = Line(axes.c2p(0, 0), axes.c2p(1, 1))
|
||||||
|
x_eq_y_line.set_stroke(self.x_eq_y_color, 2)
|
||||||
|
x_eq_y_label = TexMobject("x=y", tex_to_color_map=t2c)
|
||||||
|
x_eq_y_label.next_to(x_eq_y_line.get_end(), LEFT, MED_LARGE_BUFF)
|
||||||
|
x_eq_y_label.shift(0.75 * DL)
|
||||||
|
|
||||||
|
ineq = TexMobject("0", "\\le", "y", "\\le", "x", "\\le", "1")
|
||||||
|
ineq.set_color_by_tex("x", x_color)
|
||||||
|
ineq.set_color_by_tex("y", y_color)
|
||||||
|
ineq.scale(1.5)
|
||||||
|
ineq.move_to(ineqs, LEFT)
|
||||||
|
|
||||||
|
self.add(region, axes, x_line, y_line, coord_label, dot)
|
||||||
|
self.play(
|
||||||
|
FadeIn(region),
|
||||||
|
ShowCreation(x_eq_y_line),
|
||||||
|
# FadeInFromDown(x_eq_y_label),
|
||||||
|
Transform(ineq1[:2], ineq[:2], remover=True),
|
||||||
|
Transform(ineq1[2:], ineq[4:], remover=True),
|
||||||
|
Transform(ineq2[:4], ineq[:4], remover=True),
|
||||||
|
Transform(ineq2[4:], ineq[6:], remover=True),
|
||||||
|
)
|
||||||
|
self.add(ineq)
|
||||||
|
self.play(ShowCreationThenFadeOut(region_outline))
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# x + y <= 1 slice
|
||||||
|
xpy1_line = Line(axes.c2p(0, 1), axes.c2p(1, 0))
|
||||||
|
xpy1_line.set_stroke(GREEN, 2)
|
||||||
|
xpy1_label = TexMobject("x+y=1", tex_to_color_map=t2c)
|
||||||
|
xpy1_label.next_to(xpy1_line.get_start(), RIGHT, MED_LARGE_BUFF)
|
||||||
|
xpy1_label.shift(0.75 * DR)
|
||||||
|
|
||||||
|
xpy1_ineq = TexMobject("1 \\le x + y", tex_to_color_map=t2c)
|
||||||
|
xpy1_ineq.scale(1.5)
|
||||||
|
xpy1_ineq.next_to(ineq, DOWN, buff=MED_LARGE_BUFF)
|
||||||
|
|
||||||
|
ms_region = Polygon(
|
||||||
|
axes.c2p(1, 0),
|
||||||
|
axes.c2p(0.5, 0.5),
|
||||||
|
axes.c2p(1, 1),
|
||||||
|
fill_color=BLUE_E,
|
||||||
|
fill_opacity=0.75,
|
||||||
|
stroke_width=0,
|
||||||
|
)
|
||||||
|
ms_outline = ms_region.copy()
|
||||||
|
ms_outline.set_fill(opacity=0)
|
||||||
|
ms_outline.set_stroke(YELLOW, 2)
|
||||||
|
|
||||||
|
tt_line = Line(DOWN, UP, color=WHITE)
|
||||||
|
tt_line.set_height(0.25)
|
||||||
|
tt_line.add_updater(lambda m: m.move_to(tip_tracker))
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
ShowCreation(xpy1_line),
|
||||||
|
# FadeInFrom(xpy1_label, DOWN),
|
||||||
|
FadeInFrom(xpy1_ineq, UP)
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.play(
|
||||||
|
tip_tracker.set_y, triangle.get_bottom()[1] + 0.01,
|
||||||
|
FadeIn(tt_line),
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
self.add(ms_region, axes, x_line, y_line, coord_label, dot)
|
||||||
|
self.play(
|
||||||
|
FadeIn(ms_region),
|
||||||
|
region.set_fill, DARK_GREY,
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# Move tip around
|
||||||
|
self.play(
|
||||||
|
tip_tracker.shift, UP + RIGHT,
|
||||||
|
FadeOut(tt_line),
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.play(tip_tracker.shift, 0.5 * DOWN + LEFT, run_time=2)
|
||||||
|
self.wait()
|
||||||
|
self.play(tip_tracker.shift, UP + 0.7 * LEFT, run_time=2)
|
||||||
|
self.wait()
|
||||||
|
equilateral_point = triangle.get_bottom() + unit_factor * 0.5 * np.sqrt(3) * UP
|
||||||
|
self.play(
|
||||||
|
tip_tracker.move_to,
|
||||||
|
equilateral_point,
|
||||||
|
run_time=2,
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# Label as moduli space
|
||||||
|
ms_words = TextMobject("Moduli\\\\", "Space")
|
||||||
|
ms_words.scale(1.5)
|
||||||
|
ms_words.next_to(ms_region, RIGHT, buff=0.35)
|
||||||
|
ms_arrow = Arrow(
|
||||||
|
ms_words[1].get_corner(DL),
|
||||||
|
ms_region.get_center(),
|
||||||
|
path_arc=-90 * DEGREES,
|
||||||
|
buff=0.1,
|
||||||
|
)
|
||||||
|
# ms_arrow.rotate(-10 * DEGREES)
|
||||||
|
ms_arrow.shift(0.1 * RIGHT)
|
||||||
|
ms_arrow.scale(0.95)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
FadeInFrom(ms_words, LEFT),
|
||||||
|
)
|
||||||
|
self.play(ShowCreation(ms_arrow))
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# Show right triangles
|
||||||
|
alpha = np.arcsin(0.8)
|
||||||
|
vect = rotate_vector(0.6 * unit_factor * LEFT, -alpha)
|
||||||
|
new_tip = triangle.get_corner(DR) + vect
|
||||||
|
|
||||||
|
elbow = VMobject()
|
||||||
|
elbow.start_new_path(RIGHT)
|
||||||
|
elbow.add_line_to(UR)
|
||||||
|
elbow.add_line_to(UP)
|
||||||
|
|
||||||
|
elbow.rotate(3 * TAU / 4 - alpha, about_point=ORIGIN)
|
||||||
|
elbow.scale(0.2, about_point=ORIGIN)
|
||||||
|
elbow.shift(new_tip)
|
||||||
|
|
||||||
|
elbow_circle = Circle()
|
||||||
|
elbow_circle.replace(elbow)
|
||||||
|
elbow_circle.scale(3)
|
||||||
|
elbow_circle.move_to(new_tip)
|
||||||
|
elbow_circle.set_stroke(self.right_color, 3)
|
||||||
|
|
||||||
|
right_words = TextMobject("Right triangle")
|
||||||
|
right_words.scale(1.5)
|
||||||
|
right_words.set_color(self.right_color)
|
||||||
|
right_words.next_to(triangle, DOWN, buff=1.5)
|
||||||
|
|
||||||
|
ineqs = VGroup(ineq, xpy1_ineq)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
tip_tracker.move_to, new_tip,
|
||||||
|
FadeOut(ms_words),
|
||||||
|
FadeOut(ms_arrow),
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
ShowCreation(elbow),
|
||||||
|
FadeInFrom(right_words, UP),
|
||||||
|
FadeOutAndShift(ineqs, DOWN),
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
ShowCreationThenFadeOut(elbow_circle),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Show circular arc
|
||||||
|
pythag_eq = TexMobject("x^2 + y^2", "=", "1", tex_to_color_map=t2c)
|
||||||
|
pythag_eq.scale(1.5)
|
||||||
|
pythag_eq.next_to(right_words, DOWN, buff=MED_LARGE_BUFF)
|
||||||
|
|
||||||
|
arc = Arc(
|
||||||
|
start_angle=90 * DEGREES,
|
||||||
|
angle=-90 * DEGREES,
|
||||||
|
color=self.right_color,
|
||||||
|
)
|
||||||
|
arc.replace(box)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
FadeInFrom(pythag_eq, UP),
|
||||||
|
)
|
||||||
|
self.add(arc, arc)
|
||||||
|
self.play(ShowCreation(arc))
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# Acute region
|
||||||
|
arc_piece = VMobject()
|
||||||
|
arc_piece.pointwise_become_partial(arc, 0.5, 1.0)
|
||||||
|
|
||||||
|
acute_region = VMobject()
|
||||||
|
acute_region.start_new_path(axes.c2p(1, 1))
|
||||||
|
acute_region.add_line_to(arc_piece.get_start())
|
||||||
|
acute_region.append_vectorized_mobject(arc_piece)
|
||||||
|
acute_region.add_line_to(axes.c2p(1, 1))
|
||||||
|
acute_region.set_fill(self.acute_color, 1)
|
||||||
|
acute_region.set_stroke(width=0)
|
||||||
|
|
||||||
|
obtuse_region = VMobject()
|
||||||
|
obtuse_region.start_new_path(axes.c2p(1, 0))
|
||||||
|
obtuse_region.add_line_to(axes.c2p(0.5, 0.5))
|
||||||
|
obtuse_region.add_line_to(arc_piece.get_start())
|
||||||
|
obtuse_region.append_vectorized_mobject(arc_piece)
|
||||||
|
obtuse_region.set_fill(self.obtuse_color, 1)
|
||||||
|
obtuse_region.set_stroke(width=0)
|
||||||
|
|
||||||
|
acute_words = TextMobject("Acute triangle")
|
||||||
|
acute_words.set_color(self.acute_color)
|
||||||
|
obtuse_words = TextMobject("Obtuse triangle")
|
||||||
|
obtuse_words.set_color(self.obtuse_color)
|
||||||
|
for words in [acute_words, obtuse_words]:
|
||||||
|
words.scale(1.5)
|
||||||
|
words.move_to(right_words)
|
||||||
|
|
||||||
|
eq = pythag_eq[-2]
|
||||||
|
gt = TexMobject(">").replace(eq)
|
||||||
|
gt.set_color(self.acute_color)
|
||||||
|
lt = TexMobject("<").replace(eq)
|
||||||
|
lt.set_color(self.obtuse_color)
|
||||||
|
|
||||||
|
self.add(acute_region, coord_label, x_line, y_line, xpy1_line, x_eq_y_line, dot)
|
||||||
|
self.play(
|
||||||
|
tip_tracker.shift, 0.5 * UP,
|
||||||
|
coord_label.set_opacity, 0,
|
||||||
|
FadeOut(elbow),
|
||||||
|
FadeIn(acute_region),
|
||||||
|
FadeOutAndShift(right_words, UP),
|
||||||
|
FadeOutAndShift(eq, UP),
|
||||||
|
FadeInFrom(acute_words, DOWN),
|
||||||
|
FadeInFrom(gt, DOWN),
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.play(tip_tracker.shift, 0.5 * RIGHT)
|
||||||
|
self.wait()
|
||||||
|
self.add(obtuse_region, coord_label, x_line, y_line, xpy1_line, x_eq_y_line, dot)
|
||||||
|
self.play(
|
||||||
|
tip_tracker.shift, 1.5 * DOWN,
|
||||||
|
FadeIn(obtuse_region),
|
||||||
|
FadeOutAndShift(acute_words, DOWN),
|
||||||
|
FadeOutAndShift(gt, DOWN),
|
||||||
|
FadeInFrom(obtuse_words, UP),
|
||||||
|
FadeInFrom(lt, UP),
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.play(tip_tracker.shift, 0.5 * LEFT)
|
||||||
|
self.play(tip_tracker.shift, 0.5 * DOWN)
|
||||||
|
self.play(tip_tracker.shift, 0.5 * RIGHT)
|
||||||
|
self.play(tip_tracker.shift, 0.5 * UP)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# Ambient changes
|
||||||
|
self.play(
|
||||||
|
FadeOut(obtuse_words),
|
||||||
|
FadeOut(pythag_eq[:-2]),
|
||||||
|
FadeOut(pythag_eq[-1]),
|
||||||
|
FadeOut(lt),
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
tip_tracker.move_to, equilateral_point + 0.25 * DL,
|
||||||
|
path_arc=30 * DEGREES,
|
||||||
|
run_time=8,
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
def get_triangles_and_classes(self):
|
||||||
|
original_triangles = VGroup(*[
|
||||||
|
self.get_random_triangle()
|
||||||
|
for x in range(5)
|
||||||
|
])
|
||||||
|
original_triangles.submobjects[4] = self.get_random_triangle() # Hack
|
||||||
|
all_triangles = VGroup()
|
||||||
|
tri_classes = VGroup()
|
||||||
|
for triangle in original_triangles:
|
||||||
|
all_triangles.add(triangle)
|
||||||
|
tri_class = VGroup()
|
||||||
|
tri_class.add(triangle)
|
||||||
|
for x in range(2):
|
||||||
|
tri_copy = triangle.copy()
|
||||||
|
angle = TAU * np.random.random()
|
||||||
|
scalar = 0.25 + 1.5 * np.random.random()
|
||||||
|
|
||||||
|
tri_copy.rotate(angle - tri_copy.angle)
|
||||||
|
tri_copy.angle = angle
|
||||||
|
tri_copy.scale(scalar / tri_copy.scalar)
|
||||||
|
tri_copy.scalar = scalar
|
||||||
|
|
||||||
|
all_triangles.add(tri_copy)
|
||||||
|
tri_class.add(tri_copy)
|
||||||
|
tri_classes.add(tri_class)
|
||||||
|
|
||||||
|
colors = Color(BLUE).range_to(Color(RED), len(all_triangles))
|
||||||
|
for triangle, color in zip(all_triangles, colors):
|
||||||
|
# triangle.set_color(random_bright_color())
|
||||||
|
triangle.set_color(color)
|
||||||
|
|
||||||
|
all_triangles.shuffle()
|
||||||
|
all_triangles.arrange_in_grid(3, 5, buff=MED_LARGE_BUFF)
|
||||||
|
all_triangles.set_height(6)
|
||||||
|
sf = 1.25
|
||||||
|
all_triangles.stretch(sf, 0)
|
||||||
|
for triangle in all_triangles:
|
||||||
|
triangle.stretch(1 / sf, 0)
|
||||||
|
# all_triangles.next_to(title, DOWN)
|
||||||
|
all_triangles.to_edge(DOWN, LARGE_BUFF)
|
||||||
|
|
||||||
|
return all_triangles, tri_classes
|
||||||
|
|
||||||
|
def get_random_triangle(self, x=None, y=None):
|
||||||
|
y = np.random.random()
|
||||||
|
x = y + np.random.random()
|
||||||
|
if x + y <= 1:
|
||||||
|
diff = 1 - (x + y)
|
||||||
|
x += diff
|
||||||
|
y += diff
|
||||||
|
tri = self.get_triangle(x, y)
|
||||||
|
tri.angle = TAU * np.random.random()
|
||||||
|
tri.scalar = 0.25 + np.random.random() * 1.5
|
||||||
|
|
||||||
|
tri.rotate(tri.angle)
|
||||||
|
tri.scale(tri.scalar)
|
||||||
|
return tri
|
||||||
|
|
||||||
|
def get_triangle(self, x, y):
|
||||||
|
# Enforce assumption that x > y
|
||||||
|
if y > x:
|
||||||
|
raise Exception("Please ensure x >= y. Thank you.")
|
||||||
|
plane = self.plane
|
||||||
|
|
||||||
|
# Heron
|
||||||
|
s = (1 + x + y) / 2.0
|
||||||
|
area = np.sqrt(s * (s - 1.0) * (s - x) * (s - y))
|
||||||
|
beta = np.arcsin(2 * area / x)
|
||||||
|
tip_point = RIGHT + rotate_vector(x * LEFT, -beta)
|
||||||
|
|
||||||
|
color = self.get_triangle_color(x, y)
|
||||||
|
return Polygon(
|
||||||
|
plane.c2p(0, 0),
|
||||||
|
plane.c2p(1, 0),
|
||||||
|
plane.c2p(*tip_point[:2]),
|
||||||
|
color=color,
|
||||||
|
fill_opacity=self.triangle_fill_opacity,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_triangle_color(self, x, y):
|
||||||
|
epsilon = 1e-4
|
||||||
|
if x + y == 1:
|
||||||
|
return self.x_eq_y_color
|
||||||
|
elif x == 1:
|
||||||
|
return self.x1_color
|
||||||
|
elif y == 1:
|
||||||
|
return self.y1_color
|
||||||
|
elif np.abs(x**2 + y**2 - 1) < epsilon:
|
||||||
|
return self.right_color
|
||||||
|
elif x**2 + y**2 < 1:
|
||||||
|
return self.obtuse_color
|
||||||
|
elif x**2 + y**2 > 1:
|
||||||
|
return self.acute_color
|
||||||
|
assert(False) # Should not get here
|
||||||
|
|
||||||
|
def get_triangle_xy(self, triangle):
|
||||||
|
A, B, C = triangle.get_start_anchors()[:3]
|
||||||
|
a = get_norm(B - C)
|
||||||
|
b = get_norm(C - A)
|
||||||
|
c = get_norm(A - B)
|
||||||
|
sides = np.array(sorted([a, b, c]))
|
||||||
|
sides = sides / np.max(sides)
|
||||||
|
return sides[1], sides[0]
|
||||||
|
|
||||||
|
def get_triangle_x(self, triangle):
|
||||||
|
return self.get_triangle_xy(triangle)[0]
|
||||||
|
|
||||||
|
def get_triangle_y(self, triangle):
|
||||||
|
return self.get_triangle_xy(triangle)[1]
|
@ -189,7 +189,6 @@ class PatreonEndScreen(PatreonThanks, PiCreatureScene):
|
|||||||
def scroll_through_patrons(self):
|
def scroll_through_patrons(self):
|
||||||
logo_box = Square(side_length=2.5)
|
logo_box = Square(side_length=2.5)
|
||||||
logo_box.to_corner(DOWN + LEFT, buff=MED_LARGE_BUFF)
|
logo_box.to_corner(DOWN + LEFT, buff=MED_LARGE_BUFF)
|
||||||
total_width = FRAME_X_RADIUS - logo_box.get_right()[0]
|
|
||||||
|
|
||||||
black_rect = Rectangle(
|
black_rect = Rectangle(
|
||||||
fill_color=BLACK,
|
fill_color=BLACK,
|
||||||
@ -213,10 +212,11 @@ class PatreonEndScreen(PatreonThanks, PiCreatureScene):
|
|||||||
underline.next_to(thanks, DOWN, SMALL_BUFF)
|
underline.next_to(thanks, DOWN, SMALL_BUFF)
|
||||||
thanks.add(underline)
|
thanks.add(underline)
|
||||||
|
|
||||||
changed_patron_names = map(
|
changed_patron_names = list(map(
|
||||||
self.modify_patron_name,
|
self.modify_patron_name,
|
||||||
self.specific_patrons,
|
self.specific_patrons,
|
||||||
)
|
))
|
||||||
|
changed_patron_names.sort()
|
||||||
patrons = VGroup(*map(
|
patrons = VGroup(*map(
|
||||||
TextMobject,
|
TextMobject,
|
||||||
changed_patron_names,
|
changed_patron_names,
|
||||||
|
Reference in New Issue
Block a user