Merge pull request #790 from 3b1b/moduli

Moduli
This commit is contained in:
Grant Sanderson
2019-11-11 14:07:33 -08:00
committed by GitHub
2 changed files with 909 additions and 3 deletions

906
active_projects/moduli.py Normal file
View 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]

View File

@ -189,7 +189,6 @@ class PatreonEndScreen(PatreonThanks, PiCreatureScene):
def scroll_through_patrons(self):
logo_box = Square(side_length=2.5)
logo_box.to_corner(DOWN + LEFT, buff=MED_LARGE_BUFF)
total_width = FRAME_X_RADIUS - logo_box.get_right()[0]
black_rect = Rectangle(
fill_color=BLACK,
@ -213,10 +212,11 @@ class PatreonEndScreen(PatreonThanks, PiCreatureScene):
underline.next_to(thanks, DOWN, SMALL_BUFF)
thanks.add(underline)
changed_patron_names = map(
changed_patron_names = list(map(
self.modify_patron_name,
self.specific_patrons,
)
))
changed_patron_names.sort()
patrons = VGroup(*map(
TextMobject,
changed_patron_names,