From 13193d209e449bfe81e3545ca201774a732e4eed Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 11 Nov 2019 14:06:04 -0800 Subject: [PATCH 1/2] Changed patron name ordering --- manimlib/for_3b1b_videos/common_scenes.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/manimlib/for_3b1b_videos/common_scenes.py b/manimlib/for_3b1b_videos/common_scenes.py index baa5a2a5..0eba0ab6 100644 --- a/manimlib/for_3b1b_videos/common_scenes.py +++ b/manimlib/for_3b1b_videos/common_scenes.py @@ -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, From eb03a56dfc0ea5c40efccbcf2057e5ee193877c5 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 11 Nov 2019 14:06:17 -0800 Subject: [PATCH 2/2] Finished triangle moduli space scene --- active_projects/moduli.py | 906 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 906 insertions(+) create mode 100644 active_projects/moduli.py diff --git a/active_projects/moduli.py b/active_projects/moduli.py new file mode 100644 index 00000000..d726275e --- /dev/null +++ b/active_projects/moduli.py @@ -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]