# -*- coding: utf-8 -*- import sys, os, math sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from manim import * from tiktok import * W = 2.9 # constant width of the shapes FLOOR = -0.6 # floor of the rolling animation def reuleaux(w, bulge=1, **kw): """Reuleaux triangle: 3 arcs of radius w centred on the opposite vertex.""" R = w / math.sqrt(3) V = [R * np.array([math.cos(a), math.sin(a), 0]) for a in (PI / 2, PI / 2 + TAU / 3, PI / 2 + 2 * TAU / 3)] arcs = [ArcBetweenPoints(V[i], V[(i + 1) % 3], angle=bulge * PI / 3) for i in range(3)] m = VMobject(**kw) m.set_points(arcs[0].points) for a in arcs[1:]: m.append_points(a.points) return m class Reuleaux(TikTok): NAME = "reuleaux" def construct(self): self.prepare() chip = self.chip("Geometría de la calle", AMBER) with self.beat(0) as b: lid = VGroup( Circle(radius=1.6, stroke_color="#7A879F", stroke_width=12, fill_color="#1B2438", fill_opacity=1), Circle(radius=1.25, stroke_color="#4A5878", stroke_width=5), VGroup(*[Line(ORIGIN, RIGHT * 1.1, stroke_width=5, color="#4A5878") .rotate(a, about_point=ORIGIN) for a in np.arange(0, TAU, TAU / 8)]), ).move_to(UP * 3.4) q = self.fit(Text("¿por qué son redondas?", font=FONT, font_size=52, weight=BOLD, color=WHITE), 8.0).move_to(UP * 0.7) b.play(FadeIn(chip, shift=DOWN * 0.3), run_time=0.45) b.play(FadeIn(lid, scale=0.7), run_time=0.8) b.play(Write(q), run_time=0.9) with self.beat(1) as b: ans = self.fit(Text("no se pueden caer adentro", font=FONT, font_size=46, weight=BOLD, color=C_GREEN), 8.2).move_to(DOWN * 0.7) b.play(FadeIn(ans, shift=UP * 0.15), run_time=0.7) # ---- the square one falls in --- with self.beat(2) as b: b.play(FadeOut(VGroup(lid, q, ans)), run_time=0.4) hole = Square(side_length=2.6, stroke_color="#4A5878", stroke_width=6, fill_color="#05070D", fill_opacity=1).move_to(UP * 3.2) cover = Square(side_length=2.5, stroke_color=C_PINK, stroke_width=8, fill_color="#2A1220", fill_opacity=1).move_to(UP * 3.2) b.play(FadeIn(hole), FadeIn(cover), run_time=0.5) b.play(Rotate(cover, PI / 4), run_time=0.7) b.play(cover.animate.shift(DOWN * 2.3).scale(0.82).set_opacity(0.35), run_time=0.9) falls = Text("se cae", font=FONT, font_size=44, weight=BOLD, color=C_PINK) falls.move_to(UP * 0.35) b.play(FadeIn(falls), run_time=0.4) with self.beat(3) as b: b.play(FadeOut(VGroup(cover, falls)), run_time=0.3) side = Line(hole.get_corner(DL), hole.get_corner(DR), stroke_width=8, color=C_SKY).shift(DOWN * 0.35) diag = Line(hole.get_corner(DL), hole.get_corner(UR), stroke_width=8, color=C_PINK) l1 = Text("lado", font=FONT, font_size=34, weight=BOLD, color=C_SKY).next_to(side, DOWN, buff=0.12) l2 = Text("diagonal = 1,41 × lado", font=FONT, font_size=36, weight=BOLD, color=C_PINK).move_to(UP * 0.9) self.fit(l2, 8.0) b.play(Create(side), FadeIn(l1), run_time=0.6) b.play(Create(diag), run_time=0.6) b.play(FadeIn(l2, shift=UP * 0.15), run_time=0.6) # ---- the circle measures the same in every direction --- with self.beat(4) as b: b.play(FadeOut(VGroup(hole, side, diag, l1, l2)), run_time=0.4) circ = Circle(radius=W / 2, stroke_color=C_SKY, stroke_width=9, fill_color="#0E2233", fill_opacity=1).move_to(UP * 3.0) tr = ValueTracker(0) caliper = always_redraw(lambda: DoubleArrow( circ.get_center() + rotate_vector(RIGHT * W / 2, tr.get_value()), circ.get_center() - rotate_vector(RIGHT * W / 2, tr.get_value()), buff=0, color=AMBER, stroke_width=7, tip_length=0.22)) b.play(FadeIn(circ, scale=0.8), run_time=0.5) b.play(FadeIn(caliper), run_time=0.3) b.play(tr.animate.set_value(PI), run_time=max(1.0, b.floor - 1.1), rate_func=linear) same = Text("mide igual en toda dirección", font=FONT, font_size=38, weight=BOLD, color=AMBER).move_to(UP * 0.55) self.fit(same, 8.2) b.play(FadeIn(same), run_time=0.4) with self.beat(5) as b: no = self.fit(Text("y no es la única", font=FONT, font_size=50, weight=BOLD, color=WHITE), 8.0).move_to(DOWN * 0.7) b.play(FadeIn(no, shift=UP * 0.15), run_time=0.7) # ---- the Reuleaux appears --- tri = reuleaux(W, stroke_color=C_GREEN, stroke_width=9, fill_color="#0C2A22", fill_opacity=1).move_to(UP * 3.0) with self.beat(6) as b: b.play(FadeOut(VGroup(caliper, same, no)), run_time=0.3) b.play(Transform(circ, tri), run_time=1.2) with self.beat(7) as b: nm_key = self.fit(Text("TRIÁNGULO DE REULEAUX", font=FONT, font_size=46, weight=BOLD, color=C_GREEN), 8.2).move_to(UP * 0.75) b.play(Write(nm_key), run_time=1.0) # ---- it rolls between two walls --- with self.beat(8) as b: b.play(FadeOut(VGroup(circ, nm_key)), run_time=0.4) base = reuleaux(W, stroke_color=C_GREEN, stroke_width=9, fill_color="#0C2A22", fill_opacity=1) spin = ValueTracker(0) def rolling_t(): s = base.copy().rotate(spin.get_value()) s.shift(UP * (FLOOR - s.get_bottom()[1])) return s rolling = always_redraw(rolling_t) ground = Line([-4.2, FLOOR, 0], [4.2, FLOOR, 0], stroke_width=7, color="#4A5878") ceiling = Line([-4.2, FLOOR + W, 0], [4.2, FLOOR + W, 0], stroke_width=7, color=AMBER) b.play(Create(ground), Create(ceiling), run_time=0.5) b.play(FadeIn(rolling), run_time=0.3) b.play(spin.animate.set_value(TAU), run_time=max(1.4, b.floor - 0.8), rate_func=linear) with self.beat(9) as b: load_amt = RoundedRectangle(corner_radius=0.12, width=3.4, height=0.55, stroke_width=0, fill_color=AMBER, fill_opacity=1) load_amt.move_to([0, FLOOR + W + 0.32, 0]) level = self.fit(Text("la carga no sube ni baja", font=FONT, font_size=42, weight=BOLD, color=AMBER), 8.0).move_to(UP * 4.3) b.play(FadeIn(load_amt, shift=DOWN * 0.15), FadeIn(level), run_time=0.6) b.play(spin.animate.set_value(TAU * 2), run_time=max(1.2, b.floor - 0.7), rate_func=linear) with self.beat(10) as b: b.play(FadeOut(VGroup(rolling, ground, ceiling, load_amt, level)), run_time=0.5) count_txt = VGroup( self.fit(Text("ahora ya sabés", font=FONT, font_size=50, weight=BOLD, color=WHITE), 8.0), self.fit(Text("por qué son redondas", font=FONT, font_size=54, weight=BOLD, color=AMBER), 8.2), ).arrange(DOWN, buff=0.32).move_to(UP * 1.6) b.play(FadeIn(count_txt[0], shift=UP * 0.15), run_time=0.5) b.play(Write(count_txt[1]), run_time=0.9) b.play(Circumscribe(count_txt[1], color=AMBER, buff=0.28, stroke_width=7), run_time=0.9) self.finish()