From fafaebb051907a848a9406f9da19669c81a83a3b Mon Sep 17 00:00:00 2001 From: Elvis Claros Castro Date: Sat, 26 Sep 2026 20:50:41 -0300 Subject: Translate code, comments and logs to English; English README; configurable paths and env vars Identifiers, docstrings, comments and console messages are now in English. Narration, subtitles and on-screen text stay in Spanish (they are the video content). The Blender <-> Godot physics protocol uses English keys and body prefixes chosen to keep the original creation order, so cached simulations and renders stay bit-identical. The old Spanish environment variable names are still accepted. --- scenes/overlay.py | 20 +++--- scenes/tiktok.py | 36 +++++----- scenes/v10_buffon.py | 92 ++++++++++++------------ scenes/v11_collatz.py | 98 +++++++++++++------------- scenes/v12_simpson.py | 144 ++++++++++++++++++------------------- scenes/v13_cuerda.py | 118 +++++++++++++++---------------- scenes/v14_gabriel.py | 82 ++++++++++----------- scenes/v15_mobius.py | 138 ++++++++++++++++++------------------ scenes/v16_bayes.py | 154 ++++++++++++++++++++-------------------- scenes/v17_cicloide.py | 188 ++++++++++++++++++++++++------------------------- scenes/v18_dados.py | 174 ++++++++++++++++++++++----------------------- scenes/v1_montyhall.py | 44 ++++++------ scenes/v2_nueves.py | 74 +++++++++---------- scenes/v3_cumple.py | 52 +++++++------- scenes/v4_luna.py | 64 ++++++++--------- scenes/v5_japones.py | 62 ++++++++-------- scenes/v6_benford.py | 66 ++++++++--------- scenes/v7_hilbert.py | 56 +++++++-------- scenes/v8_reuleaux.py | 108 ++++++++++++++-------------- scenes/v9_regla72.py | 68 +++++++++--------- 20 files changed, 919 insertions(+), 919 deletions(-) (limited to 'scenes') diff --git a/scenes/overlay.py b/scenes/overlay.py index 0a3eb71..d8d735b 100644 --- a/scenes/overlay.py +++ b/scenes/overlay.py @@ -1,31 +1,31 @@ # -*- coding: utf-8 -*- -"""Capa de interfaz del canal (chip, barra de progreso y subtitulos) para los -videos renderizados en Blender. Se renderiza con --transparent y se compone -encima del 3D, asi el estilo sale del mismo codigo que los videos de Manim. +"""Channel UI layer (chip, progress bar and subtitles) for the videos rendered +in Blender. It is rendered with --transparent and composited over the 3D, so +the style comes from the same code as the Manim videos. NAME=bolapeluda CHIP=Topología CHIPCOL=celeste manim --transparent ... -Tambien escribe out/_timeline.json, que es lo que despues leen el script -de Blender (para animar) y build_video.py (para colocar el audio). +It also writes out/_timeline.json, which the Blender script (to animate) +and build_video.py (to place the audio) read afterwards. """ import os, sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from manim import * from tiktok import * -COLORES = {"ambar": AMBAR, "verde": VERDE, "rosa": ROSA, "celeste": CELESTE} +PALETTE = {"ambar": AMBER, "verde": C_GREEN, "rosa": C_PINK, "celeste": C_SKY} class Overlay(TikTok): NAME = os.environ.get("NAME", "") def backdrop(self): - return VGroup() # el fondo punteado va aparte, debajo del 3D + return VGroup() # the dotted background goes separately, under the 3D def construct(self): self.prepare() chip = self.chip(os.environ.get("CHIP", ""), - COLORES[os.environ.get("CHIPCOL", "ambar")]) + PALETTE[os.environ.get("CHIPCOL", "ambar")]) self.add(chip) for i in range(len(self.segs)): with self.beat(i) as b: @@ -33,8 +33,8 @@ class Overlay(TikTok): self.finish() -class Fondo(TikTok): - """Un solo frame con la trama de puntos, para usar de fondo.""" +class Backdrop(TikTok): + """A single frame with the dot pattern, to use as background.""" NAME = "fondo" def construct(self): diff --git a/scenes/tiktok.py b/scenes/tiktok.py index 7e2ad2d..584210e 100644 --- a/scenes/tiktok.py +++ b/scenes/tiktok.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -"""Base comun para los videos verticales: formato 9:16, subtitulos, -barra de progreso y un sistema de 'beats' sincronizados con el audio.""" +"""Common base for the vertical videos: 9:16 format, subtitles, +progress bar and a system of 'beats' synced with the audio.""" import json, os from contextlib import contextmanager from manim import * -# --- formato TikTok 1080x1920 ------------------------------------------------- +# --- TikTok format 1080x1920 -------------------------------------------------- if os.environ.get("DRAFT"): config.pixel_width, config.pixel_height = 540, 960 else: @@ -18,19 +18,19 @@ config.background_color = "#0B0F1A" ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) FONT = "Roboto" -AMBAR = "#FFD166" -VERDE = "#06D6A0" -ROSA = "#EF476F" -CELESTE = "#4CC9F0" -GRIS = "#8D99AE" - -GAP = 0.30 # silencio entre segmentos -SUB_Y = -4.25 # los subtitulos van arriba de la UI de TikTok +AMBER = "#FFD166" +C_GREEN = "#06D6A0" +C_PINK = "#EF476F" +C_SKY = "#4CC9F0" +C_GRAY = "#8D99AE" + +GAP = 0.30 # silence between segments +SUB_Y = -4.25 # subtitles sit above the TikTok UI TOP_Y = 6.45 class _Beat: - """Acumula el tiempo usado dentro de un segmento para poder rellenar el resto.""" + """Accumulates the time used inside a segment so the rest can be filled.""" def __init__(self, scene, floor): self.s, self.floor, self.used = scene, floor, 0.0 @@ -63,7 +63,7 @@ class _Beat: class TikTok(Scene): NAME = "" - # -- infraestructura ------------------------------------------------------ + # -- infrastructure ------------------------------------------------------- def prepare(self): with open(os.path.join(ROOT, "audio", self.NAME, "segments.json"), encoding="utf-8") as f: self.data = json.load(f) @@ -88,7 +88,7 @@ class TikTok(Scene): track = Rectangle(width=w, height=0.075, stroke_width=0, fill_color="#1B2233", fill_opacity=1).move_to(UP * (TOP_Y + 1.15)) bar = Rectangle(width=0.002, height=0.075, stroke_width=0, - fill_color=AMBAR, fill_opacity=1).move_to(track.get_left(), LEFT) + fill_color=AMBER, fill_opacity=1).move_to(track.get_left(), LEFT) self.elapsed = 0.0 total, left, y = self._expected, -w / 2, track.get_y() @@ -103,7 +103,7 @@ class TikTok(Scene): self.add(track, bar) def subtitle(self, text): - """Cambia el subtitulo sin consumir tiempo de animacion.""" + """Changes the subtitle without spending animation time.""" if self._sub: self.remove(self._sub) t = Text(text, font=FONT, font_size=44, weight=BOLD, color=WHITE, @@ -118,7 +118,7 @@ class TikTok(Scene): @contextmanager def beat(self, i, sub=True): - """Bloque sincronizado con el segmento i de audio.""" + """Block synced with audio segment i.""" if sub: self.subtitle(self.segs[i]["sub"]) b = _Beat(self, self.segs[i]["dur"]) @@ -139,8 +139,8 @@ class TikTok(Scene): json.dump({"name": self.NAME, "gap": GAP, "total": round(self.clock, 3), "beats": self.timeline}, f, indent=2) - # -- helpers visuales ----------------------------------------------------- - def chip(self, text, color=AMBAR): + # -- visual helpers ------------------------------------------------------- + def chip(self, text, color=AMBER): t = Text(text, font=FONT, font_size=34, weight=BOLD, color="#0B0F1A") box = RoundedRectangle(corner_radius=0.3, stroke_width=0, fill_color=color, fill_opacity=1, width=t.width + 0.7, height=t.height + 0.45) diff --git a/scenes/v10_buffon.py b/scenes/v10_buffon.py index 35ee4e0..557d927 100644 --- a/scenes/v10_buffon.py +++ b/scenes/v10_buffon.py @@ -4,13 +4,13 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from manim import * from tiktok import * -D = 1.1 # separacion entre rayas = largo de la aguja -Y0 = 0.35 # raya mas baja +D = 1.1 # line spacing = needle length +Y0 = 0.35 # lowest line NLIN = 5 SEED, N1, N2 = 36, 60, 200 -def tirar(n): +def throw(n): rng = np.random.default_rng(SEED) x = rng.uniform(-3.9, 3.9, n) y = rng.uniform(Y0 + 0.1, Y0 + (NLIN - 1) * D - 0.1, n) @@ -18,8 +18,8 @@ def tirar(n): dx, dy = D / 2 * np.cos(th), D / 2 * np.sin(th) p1 = np.stack([x - dx, y - dy, np.zeros(n)], axis=1) p2 = np.stack([x + dx, y + dy, np.zeros(n)], axis=1) - cruza = np.floor((p1[:, 1] - Y0) / D) != np.floor((p2[:, 1] - Y0) / D) - return p1, p2, cruza + crosses = np.floor((p1[:, 1] - Y0) / D) != np.floor((p2[:, 1] - Y0) / D) + return p1, p2, crosses class Buffon(TikTok): @@ -27,10 +27,10 @@ class Buffon(TikTok): def construct(self): self.prepare() - chip = self.chip("π sin círculos", ROSA) - p1, p2, cruza = tirar(N2) + chip = self.chip("π sin círculos", C_PINK) + p1, p2, crosses = throw(N2) - rayas = VGroup(*[Line([-4.2, Y0 + k * D, 0], [4.2, Y0 + k * D, 0], + stripes = VGroup(*[Line([-4.2, Y0 + k * D, 0], [4.2, Y0 + k * D, 0], stroke_width=4, color="#33405C") for k in range(NLIN)]) with self.beat(0) as b: @@ -38,7 +38,7 @@ class Buffon(TikTok): self.fit(Text("tiro agujas al piso", font=FONT, font_size=52, weight=BOLD, color=WHITE), 8.2), self.fit(Text("y sale π", font=FONT, font_size=64, - weight=BOLD, color=AMBAR), 8.2), + weight=BOLD, color=AMBER), 8.2), ).arrange(DOWN, buff=0.32).move_to(UP * 2.4) b.play(FadeIn(chip, shift=DOWN * 0.3), run_time=0.45) b.play(FadeIn(hook[0], shift=UP * 0.15), run_time=0.6) @@ -46,105 +46,105 @@ class Buffon(TikTok): with self.beat(1) as b: b.play(FadeOut(hook), run_time=0.35) - b.play(LaggedStart(*[Create(r) for r in rayas], lag_ratio=0.12), run_time=0.9) + b.play(LaggedStart(*[Create(r) for r in stripes], lag_ratio=0.12), run_time=0.9) demo = Line([-1.0, Y0 + D, 0], [-1.0, Y0 + 2 * D, 0], - stroke_width=9, color=AMBAR) + stroke_width=9, color=AMBER) med = DoubleArrow([0.2, Y0 + D, 0], [0.2, Y0 + 2 * D, 0], buff=0, - color=VERDE, stroke_width=6, tip_length=0.2) - lab = Text("mismo largo", font=FONT, font_size=32, weight=BOLD, color=VERDE) + color=C_GREEN, stroke_width=6, tip_length=0.2) + lab = Text("mismo largo", font=FONT, font_size=32, weight=BOLD, color=C_GREEN) lab.next_to(med, RIGHT, buff=0.25) b.play(Create(demo), run_time=0.5) b.play(GrowArrow(med), FadeIn(lab), run_time=0.6) - agujas = VGroup(*[Line(p1[i], p2[i], stroke_width=4.5, color="#7C8AA8") + needles = VGroup(*[Line(p1[i], p2[i], stroke_width=4.5, color="#7C8AA8") for i in range(N2)]) with self.beat(2) as b: b.play(FadeOut(VGroup(demo, med, lab)), run_time=0.3) - b.play(LaggedStart(*[FadeIn(agujas[i]) for i in range(N1)], + b.play(LaggedStart(*[FadeIn(needles[i]) for i in range(N1)], lag_ratio=0.012), run_time=max(1.1, b.floor - 0.5)) with self.beat(3) as b: - hit = [agujas[i] for i in range(N1) if cruza[i]] - miss = [agujas[i] for i in range(N1) if not cruza[i]] - b.play(*[a.animate.set_stroke(AMBAR, 6) for a in hit], + hit = [needles[i] for i in range(N1) if crosses[i]] + miss = [needles[i] for i in range(N1) if not crosses[i]] + b.play(*[a.animate.set_stroke(AMBER, 6) for a in hit], *[a.animate.set_stroke("#46526E", 3.5) for a in miss], run_time=1.0) - c1 = int(cruza[:N1].sum()) + c1 = int(crosses[:N1].sum()) - def marcador(n, c, y=-0.75): + def marker(n, c, y=-0.75): g = VGroup( - VGroup(Text("tiradas", font=FONT, font_size=30, color=GRIS), + VGroup(Text("tiradas", font=FONT, font_size=30, color=C_GRAY), Text(str(n), font=FONT, font_size=62, weight=BOLD, color=WHITE) ).arrange(DOWN, buff=0.1), - VGroup(Text("cruzan", font=FONT, font_size=30, color=GRIS), - Text(str(c), font=FONT, font_size=62, weight=BOLD, color=AMBAR) + VGroup(Text("cruzan", font=FONT, font_size=30, color=C_GRAY), + Text(str(c), font=FONT, font_size=62, weight=BOLD, color=AMBER) ).arrange(DOWN, buff=0.1), ).arrange(RIGHT, buff=1.5).move_to([0, y, 0]) return g - marc = marcador(N1, c1) + marker_m = marker(N1, c1) with self.beat(4) as b: - b.play(FadeIn(marc, shift=UP * 0.15), run_time=0.8) + b.play(FadeIn(marker_m, shift=UP * 0.15), run_time=0.8) with self.beat(5) as b: f = MathTex(r"\frac{2 \times \text{tiradas}}{\text{cruces}}", - color=CELESTE).scale(1.25).move_to(DOWN * 2.45) + color=C_SKY).scale(1.25).move_to(DOWN * 2.45) b.play(Write(f), run_time=1.1) with self.beat(6) as b: - est = 2 * N1 / c1 - res = MathTex(r"= %s" % f"{est:.4f}".replace(".", "{,}"), - color=VERDE).scale(1.45) + estimate = 2 * N1 / c1 + res = MathTex(r"= %s" % f"{estimate:.4f}".replace(".", "{,}"), + color=C_GREEN).scale(1.45) res.next_to(f, RIGHT, buff=0.4) grp = VGroup(f, res) b.play(Write(res), run_time=0.8) b.play(grp.animate.move_to(DOWN * 2.45), run_time=0.4) - b.play(Flash(res, color=VERDE, line_length=0.5, num_lines=16, + b.play(Flash(res, color=C_GREEN, line_length=0.5, num_lines=16, flash_radius=1.4), run_time=0.6) - c2 = int(cruza.sum()) + c2 = int(crosses.sum()) with self.beat(7) as b: - hit = [agujas[i] for i in range(N1, N2) if cruza[i]] - miss = [agujas[i] for i in range(N1, N2) if not cruza[i]] + hit = [needles[i] for i in range(N1, N2) if crosses[i]] + miss = [needles[i] for i in range(N1, N2) if not crosses[i]] for a in hit: - a.set_stroke(AMBAR, 6) + a.set_stroke(AMBER, 6) for a in miss: a.set_stroke("#46526E", 3.5) - b.play(LaggedStart(*[FadeIn(agujas[i]) for i in range(N1, N2)], + b.play(LaggedStart(*[FadeIn(needles[i]) for i in range(N1, N2)], lag_ratio=0.004), run_time=max(1.0, b.floor - 1.3)) est2 = 2 * N2 / c2 - nm = marcador(N2, c2) + nm = marker(N2, c2) nres = MathTex(r"= %s" % f"{est2:.4f}".replace(".", "{,}"), - color=VERDE).scale(1.45).next_to(f, RIGHT, buff=0.4) - b.play(Transform(marc, nm), Transform(res, nres), run_time=0.9) + color=C_GREEN).scale(1.45).next_to(f, RIGHT, buff=0.4) + b.play(Transform(marker_m, nm), Transform(res, nres), run_time=0.9) with self.beat(8) as b: - pi = MathTex(r"\pi = 3{,}14159\ldots", color=AMBAR).scale(1.5) + pi = MathTex(r"\pi = 3{,}14159\ldots", color=AMBER).scale(1.5) pi.move_to(UP * 5.5) b.play(Write(pi), run_time=1.0) - b.play(Circumscribe(pi, color=AMBAR, buff=0.25, stroke_width=6), run_time=0.8) + b.play(Circumscribe(pi, color=AMBER, buff=0.25, stroke_width=6), run_time=0.8) with self.beat(9) as b: - b.play(FadeOut(VGroup(marc, pi, f, res)), run_time=0.45) + b.play(FadeOut(VGroup(marker_m, pi, f, res)), run_time=0.45) t = VGroup( self.fit(Text("no hay ningún círculo", font=FONT, font_size=48, weight=BOLD, color=WHITE), 8.2), self.fit(Text("y π aparece igual", font=FONT, font_size=48, - weight=BOLD, color=AMBAR), 8.2), + weight=BOLD, color=AMBER), 8.2), ).arrange(DOWN, buff=0.35).move_to(DOWN * 1.5) b.play(FadeIn(t[0], shift=UP * 0.15), run_time=0.6) b.play(FadeIn(t[1], shift=UP * 0.15), run_time=0.7) with self.beat(10) as b: b.play(FadeOut(t), run_time=0.35) - cta = VGroup( + count_txt = VGroup( self.fit(Text("aguja de Buffon", font=FONT, font_size=58, - weight=BOLD, color=AMBAR), 8.0), + weight=BOLD, color=AMBER), 8.0), self.fit(Text("hacelo con fósforos", font=FONT, font_size=44, weight=BOLD, color=WHITE), 8.0), ).arrange(DOWN, buff=0.35).move_to(DOWN * 1.5) - b.play(Write(cta[0]), run_time=0.8) - b.play(FadeIn(cta[1], shift=UP * 0.15), run_time=0.6) + b.play(Write(count_txt[0]), run_time=0.8) + b.play(FadeIn(count_txt[1], shift=UP * 0.15), run_time=0.6) self.finish() diff --git a/scenes/v11_collatz.py b/scenes/v11_collatz.py index 49436b8..20c7ccd 100644 --- a/scenes/v11_collatz.py +++ b/scenes/v11_collatz.py @@ -16,8 +16,8 @@ def seq(n): return s -def curva(sq, color=AMBAR, sw=5, mx=None, nx=None, log=False): - """Trayectoria como poligonal. mx/nx permiten compartir escala entre curvas.""" +def curve_obj(sq, color=AMBER, sw=5, mx=None, nx=None, log=False): + """Trajectory as a polyline. mx/nx allow sharing the scale between curves.""" mx = mx or max(sq) nx = nx or len(sq) if log: @@ -43,29 +43,29 @@ class Collatz(TikTok): def construct(self): self.prepare() - chip = self.chip("Abierto desde 1937", ROSA) + chip = self.chip("Abierto desde 1937", C_PINK) with self.beat(0) as b: - n = self.big("27", 150, AMBAR).move_to(UP * 1.9) + n = self.big("27", 150, AMBER).move_to(UP * 1.9) cap = self.fit(Text("agarrá cualquier número", font=FONT, font_size=50, weight=BOLD, color=WHITE), 8.2).move_to(DOWN * 0.3) b.play(FadeIn(chip, shift=DOWN * 0.3), run_time=0.45) b.play(FadeIn(n, scale=0.6), run_time=0.45) for v in ("6", "19", "41"): - b.play(Transform(n, self.big(v, 150, AMBAR).move_to(UP * 1.9)), run_time=0.26) + b.play(Transform(n, self.big(v, 150, AMBER).move_to(UP * 1.9)), run_time=0.26) b.play(FadeIn(cap, shift=UP * 0.15), run_time=0.5) - def regla(txt, op, col): + def ruler(txt, op, col): a = Text(txt, font=FONT, font_size=46, weight=BOLD, color=col) - fl = Arrow(ORIGIN, RIGHT * 0.85, buff=0, color=GRIS, stroke_width=6, + fl = Arrow(ORIGIN, RIGHT * 0.85, buff=0, color=C_GRAY, stroke_width=6, max_tip_length_to_length_ratio=0.35) c = Text(op, font=FONT, font_size=52, weight=BOLD, color=WHITE) return VGroup(a, fl, c).arrange(RIGHT, buff=0.35) with self.beat(1) as b: b.play(FadeOut(VGroup(n, cap)), run_time=0.3) - r1 = regla("es PAR", "÷ 2", CELESTE) - r2 = regla("es IMPAR", "× 3 + 1", AMBAR) + r1 = ruler("es PAR", "÷ 2", C_SKY) + r2 = ruler("es IMPAR", "× 3 + 1", AMBER) rr = VGroup(r1, r2).arrange(DOWN, buff=0.7).move_to(UP * 1.6) self.fit(rr, 8.2) b.play(FadeIn(r1, shift=UP * 0.2), run_time=0.7) @@ -74,12 +74,12 @@ class Collatz(TikTok): s6 = seq(6) with self.beat(2) as b: b.play(rr.animate.scale(0.72).move_to(UP * 5.2), run_time=0.5) - seis = self.big("6", 140, VERDE).move_to(UP * 1.0) - b.play(FadeIn(seis, scale=0.6), run_time=0.6) + six = self.big("6", 140, C_GREEN).move_to(UP * 1.0) + b.play(FadeIn(six, scale=0.6), run_time=0.6) with self.beat(3) as b: - b.play(FadeOut(seis), run_time=0.25) - tiles = VGroup(*[tile(v, VERDE if v == 1 else (CELESTE if v % 2 == 0 else AMBAR)) + b.play(FadeOut(six), run_time=0.25) + tiles = VGroup(*[tile(v, C_GREEN if v == 1 else (C_SKY if v % 2 == 0 else AMBER)) for v in s6]) fila1 = VGroup(*tiles[:5]).arrange(RIGHT, buff=0.28) fila2 = VGroup(*tiles[5:]).arrange(RIGHT, buff=0.28) @@ -89,83 +89,83 @@ class Collatz(TikTok): lag_ratio=0.5), run_time=max(1.4, b.floor - 0.4)) with self.beat(4) as b: - b.play(Circumscribe(tiles[-1], color=VERDE, buff=0.12, stroke_width=6), + b.play(Circumscribe(tiles[-1], color=C_GREEN, buff=0.12, stroke_width=6), run_time=0.7) b.play(FadeOut(grid), run_time=0.35) - siete = self.big("7", 140, AMBAR).move_to(UP * 1.0) - b.play(FadeIn(siete, scale=0.6), run_time=0.5) + seven = self.big("7", 140, AMBER).move_to(UP * 1.0) + b.play(FadeIn(seven, scale=0.6), run_time=0.5) base = Line([X0 - 0.25, Y0, 0], [X1 + 0.25, Y0, 0], stroke_width=4, color="#33405C") - uno = Text("1", font=FONT, font_size=32, weight=BOLD, color=VERDE) - uno.next_to(base.get_start(), LEFT, buff=0.12) + one_val = Text("1", font=FONT, font_size=32, weight=BOLD, color=C_GREEN) + one_val.next_to(base.get_start(), LEFT, buff=0.12) s7 = seq(7) with self.beat(5) as b: - b.play(FadeOut(siete), run_time=0.25) - c7, p7 = curva(s7, AMBAR, 6) - pico = Text("52", font=FONT, font_size=34, weight=BOLD, color=AMBAR) - pico.next_to([p7[s7.index(max(s7))][0], Y1, 0], UP, buff=0.12) - b.play(FadeIn(base), FadeIn(uno), run_time=0.35) + b.play(FadeOut(seven), run_time=0.25) + c7, p7 = curve_obj(s7, AMBER, 6) + peak_y = Text("52", font=FONT, font_size=34, weight=BOLD, color=AMBER) + peak_y.next_to([p7[s7.index(max(s7))][0], Y1, 0], UP, buff=0.12) + b.play(FadeIn(base), FadeIn(one_val), run_time=0.35) b.play(Create(c7), run_time=max(1.2, b.floor - 1.1)) - b.play(FadeIn(pico), run_time=0.4) + b.play(FadeIn(peak_y), run_time=0.4) s27 = seq(27) with self.beat(6) as b: - b.play(FadeOut(VGroup(c7, pico)), run_time=0.3) - c27, p27 = curva(s27, ROSA, 5) + b.play(FadeOut(VGroup(c7, peak_y)), run_time=0.3) + c27, p27 = curve_obj(s27, C_PINK, 5) k = s27.index(max(s27)) - top = Text("9.232", font=FONT, font_size=40, weight=BOLD, color=ROSA) + top = Text("9.232", font=FONT, font_size=40, weight=BOLD, color=C_PINK) top.next_to([p27[k][0], Y1, 0], UP, buff=0.12) - lab = Text("27", font=FONT, font_size=40, weight=BOLD, color=ROSA) + lab = Text("27", font=FONT, font_size=40, weight=BOLD, color=C_PINK) lab.move_to([X0 - 0.05, Y1 + 0.45, 0]) b.play(FadeIn(lab), run_time=0.3) b.play(Create(c27), run_time=max(1.3, b.floor - 1.0)) b.play(FadeIn(top), run_time=0.4) with self.beat(7) as b: - fin = Dot([p27[-1][0], Y0, 0], radius=0.14, color=VERDE) - pasos = self.fit(Text("111 pasos y cae al 1", font=FONT, font_size=48, - weight=BOLD, color=VERDE), 8.0).move_to(DOWN * 2.6) + fin = Dot([p27[-1][0], Y0, 0], radius=0.14, color=C_GREEN) + steps = self.fit(Text("111 pasos y cae al 1", font=FONT, font_size=48, + weight=BOLD, color=C_GREEN), 8.0).move_to(DOWN * 2.6) b.play(FadeIn(fin, scale=0.4), run_time=0.4) - b.play(Flash(fin, color=VERDE, line_length=0.35, num_lines=14, + b.play(Flash(fin, color=C_GREEN, line_length=0.35, num_lines=14, flash_radius=0.8), run_time=0.6) - b.play(FadeIn(pasos, shift=UP * 0.15), run_time=0.6) + b.play(FadeIn(steps, shift=UP * 0.15), run_time=0.6) with self.beat(8) as b: - b.play(FadeOut(VGroup(c27, top, lab, fin, pasos)), run_time=0.4) - todos = [seq(k) for k in range(2, 80)] - MX = max(max(s) for s in todos) - NX = max(len(s) for s in todos) - cols = [AMBAR, CELESTE, ROSA, VERDE] - gr = VGroup(*[curva(s, cols[i % 4], 2.4, mx=MX, nx=NX, log=True)[0] - for i, s in enumerate(todos)]) + b.play(FadeOut(VGroup(c27, top, lab, fin, steps)), run_time=0.4) + everyone = [seq(k) for k in range(2, 80)] + MX = max(max(s) for s in everyone) + NX = max(len(s) for s in everyone) + cols = [AMBER, C_SKY, C_PINK, C_GREEN] + gr = VGroup(*[curve_obj(s, cols[i % 4], 2.4, mx=MX, nx=NX, log=True)[0] + for i, s in enumerate(everyone)]) gr.set_stroke(opacity=0.65) b.play(LaggedStart(*[Create(c) for c in gr], lag_ratio=0.02), run_time=max(1.6, b.floor - 0.9)) - marca = self.fit(Text("todos terminan en 1", font=FONT, font_size=46, - weight=BOLD, color=VERDE), 8.0).move_to(DOWN * 2.6) - b.play(FadeIn(marca, shift=UP * 0.15), run_time=0.6) + tick_m = self.fit(Text("todos terminan en 1", font=FONT, font_size=46, + weight=BOLD, color=C_GREEN), 8.0).move_to(DOWN * 2.6) + b.play(FadeIn(tick_m, shift=UP * 0.15), run_time=0.6) with self.beat(9) as b: - b.play(FadeOut(VGroup(gr, marca, base, uno, rr)), run_time=0.45) + b.play(FadeOut(VGroup(gr, tick_m, base, one_val, rr)), run_time=0.45) t = VGroup( self.fit(Text("nadie pudo demostrar", font=FONT, font_size=50, weight=BOLD, color=WHITE), 8.2), self.fit(Text("que pasa SIEMPRE", font=FONT, font_size=54, - weight=BOLD, color=ROSA), 8.2), + weight=BOLD, color=C_PINK), 8.2), ).arrange(DOWN, buff=0.35).move_to(UP * 1.2) b.play(FadeIn(t[0], shift=UP * 0.15), run_time=0.6) b.play(FadeIn(t[1], scale=0.8), run_time=0.7) with self.beat(10) as b: b.play(FadeOut(t), run_time=0.35) - cta = VGroup( + count_txt = VGroup( self.fit(Text("conjetura de Collatz", font=FONT, font_size=56, - weight=BOLD, color=AMBAR), 8.0), + weight=BOLD, color=AMBER), 8.0), self.fit(Text("dejá tu número abajo", font=FONT, font_size=46, weight=BOLD, color=WHITE), 8.0), ).arrange(DOWN, buff=0.38).move_to(UP * 0.8) - b.play(Write(cta[0]), run_time=0.9) - b.play(FadeIn(cta[1], shift=UP * 0.15), run_time=0.6) + b.play(Write(count_txt[0]), run_time=0.9) + b.play(FadeIn(count_txt[1], shift=UP * 0.15), run_time=0.6) self.finish() diff --git a/scenes/v12_simpson.py b/scenes/v12_simpson.py index 6092924..885d9e7 100644 --- a/scenes/v12_simpson.py +++ b/scenes/v12_simpson.py @@ -4,23 +4,23 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from manim import * from tiktok import * -# datos reales (Charig et al., 1986): tratamiento A vs B para calculos renales -LEV = (("A", 81, 87), ("B", 234, 270)) -GRA = (("A", 192, 263), ("B", 55, 80)) +# real data (Charig et al., 1986): treatment A vs B for kidney stones +LEVER = (("A", 81, 87), ("B", 234, 270)) +GRAV_FIELD = (("A", 192, 263), ("B", 55, 80)) TOT = (("A", 81 + 192, 87 + 263), ("B", 234 + 55, 270 + 80)) -BARW = 4.05 # ancho de barra al 100% -XL = -1.55 # borde izquierdo de las barras +BARW = 4.05 # bar width at 100% +XL = -1.55 # left edge of the bars class Simpson(TikTok): NAME = "simpson" - def barra(self, letra, num, den, y, gana): + def bar_m(self, letter, num, den, y, wins): pct = 100.0 * num / den - col = VERDE if gana else GRIS - lab = Text(letra, font=FONT, font_size=50, weight=BOLD, - color=CELESTE if letra == "A" else ROSA) + col = C_GREEN if wins else C_GRAY + lab = Text(letter, font=FONT, font_size=50, weight=BOLD, + color=C_SKY if letter == "A" else C_PINK) lab.move_to([-3.65, y, 0]) track = RoundedRectangle(corner_radius=0.12, width=BARW, height=0.62, stroke_width=0, fill_color="#1B2233", fill_opacity=1) @@ -32,139 +32,139 @@ class Simpson(TikTok): val.next_to(track, RIGHT, buff=0.22) return VGroup(lab, track, fill, val) - def bloque(self, titulo, datos, ytop, ganador): - t = Text(titulo, font=FONT, font_size=40, weight=BOLD, color=WHITE) + def block_obj(self, title, dataset, ytop, winner): + t = Text(title, font=FONT, font_size=40, weight=BOLD, color=WHITE) t.move_to([-3.85, ytop, 0], LEFT) - b1 = self.barra(datos[0][0], datos[0][1], datos[0][2], ytop - 0.85, - datos[0][0] == ganador) - b2 = self.barra(datos[1][0], datos[1][1], datos[1][2], ytop - 1.70, - datos[1][0] == ganador) + b1 = self.bar_m(dataset[0][0], dataset[0][1], dataset[0][2], ytop - 0.85, + dataset[0][0] == winner) + b2 = self.bar_m(dataset[1][0], dataset[1][1], dataset[1][2], ytop - 1.70, + dataset[1][0] == winner) return VGroup(t, b1, b2) def construct(self): self.prepare() - chip = self.chip("Estadística que engaña", ROSA) + chip = self.chip("Estadística que engaña", C_PINK) with self.beat(0) as b: b.play(FadeIn(chip, shift=DOWN * 0.3), run_time=0.45) - cab = VGroup( + header = VGroup( self.fit(Text("dos tratamientos", font=FONT, font_size=52, weight=BOLD, color=WHITE), 8.2), VGroup( - Text("A", font=FONT, font_size=90, weight=BOLD, color=CELESTE), - Text("vs", font=FONT, font_size=44, color=GRIS), - Text("B", font=FONT, font_size=90, weight=BOLD, color=ROSA), + Text("A", font=FONT, font_size=90, weight=BOLD, color=C_SKY), + Text("vs", font=FONT, font_size=44, color=C_GRAY), + Text("B", font=FONT, font_size=90, weight=BOLD, color=C_PINK), ).arrange(RIGHT, buff=0.7), ).arrange(DOWN, buff=0.45).move_to(UP * 3.0) - b.play(FadeIn(cab[0], shift=UP * 0.15), run_time=0.6) - b.play(FadeIn(cab[1][0], scale=0.6), run_time=0.35) - b.play(FadeIn(cab[1][1]), run_time=0.2) - b.play(FadeIn(cab[1][2], scale=0.6), run_time=0.35) + b.play(FadeIn(header[0], shift=UP * 0.15), run_time=0.6) + b.play(FadeIn(header[1][0], scale=0.6), run_time=0.35) + b.play(FadeIn(header[1][1]), run_time=0.2) + b.play(FadeIn(header[1][2], scale=0.6), run_time=0.35) - lev = self.bloque("casos LEVES", LEV, 2.55, "A") + lever = self.block_obj("casos LEVES", LEVER, 2.55, "A") with self.beat(1) as b: - b.play(cab.animate.scale(0.5).move_to(UP * 5.4), run_time=0.5) - b.play(FadeIn(lev[0], shift=RIGHT * 0.2), run_time=0.4) - for bb in (lev[1], lev[2]): + b.play(header.animate.scale(0.5).move_to(UP * 5.4), run_time=0.5) + b.play(FadeIn(lever[0], shift=RIGHT * 0.2), run_time=0.4) + for bb in (lever[1], lever[2]): b.play(FadeIn(bb[0]), FadeIn(bb[1]), GrowFromEdge(bb[2], LEFT), FadeIn(bb[3]), run_time=0.55) - gra = self.bloque("casos GRAVES", GRA, -0.35, "A") + grav = self.block_obj("casos GRAVES", GRAV_FIELD, -0.35, "A") with self.beat(2) as b: - b.play(FadeIn(gra[0], shift=RIGHT * 0.2), run_time=0.4) - for bb in (gra[1], gra[2]): + b.play(FadeIn(grav[0], shift=RIGHT * 0.2), run_time=0.4) + for bb in (grav[1], grav[2]): b.play(FadeIn(bb[0]), FadeIn(bb[1]), GrowFromEdge(bb[2], LEFT), FadeIn(bb[3]), run_time=0.55) - def tilde(col=VERDE): + def tilde(col=C_GREEN): m = VMobject(stroke_color=col, stroke_width=9) m.set_points_as_corners([[-0.20, 0.02, 0], [-0.05, -0.18, 0], [0.24, 0.26, 0]]) return m with self.beat(3) as b: tick = VGroup(*[tilde() for _ in range(2)]) - tick[0].next_to(lev[1][3], RIGHT, buff=0.25) - tick[1].next_to(gra[1][3], RIGHT, buff=0.25) + tick[0].next_to(lever[1][3], RIGHT, buff=0.25) + tick[1].next_to(grav[1][3], RIGHT, buff=0.25) b.play(FadeIn(tick[0], scale=0.5), run_time=0.4) b.play(FadeIn(tick[1], scale=0.5), run_time=0.4) - pre = self.fit(Text("A gana 2 de 2", font=FONT, font_size=48, - weight=BOLD, color=CELESTE), 8.0).move_to(DOWN * 2.80) - b.play(FadeIn(pre, shift=UP * 0.15), run_time=0.5) + pre_frames = self.fit(Text("A gana 2 de 2", font=FONT, font_size=48, + weight=BOLD, color=C_SKY), 8.0).move_to(DOWN * 2.80) + b.play(FadeIn(pre_frames, shift=UP * 0.15), run_time=0.5) - tot = self.bloque("TODOS juntos", TOT, 2.55, "B") + tot = self.block_obj("TODOS juntos", TOT, 2.55, "B") with self.beat(4) as b: - b.play(FadeOut(VGroup(lev, gra, tick, pre)), run_time=0.45) + b.play(FadeOut(VGroup(lever, grav, tick, pre_frames)), run_time=0.45) b.play(FadeIn(tot[0], shift=RIGHT * 0.2), run_time=0.4) for bb in (tot[1], tot[2]): b.play(FadeIn(bb[0]), FadeIn(bb[1]), GrowFromEdge(bb[2], LEFT), FadeIn(bb[3]), run_time=0.6) with self.beat(5) as b: - b.play(Circumscribe(tot[2], color=VERDE, buff=0.18, stroke_width=6), + b.play(Circumscribe(tot[2], color=C_GREEN, buff=0.18, stroke_width=6), run_time=0.8) g = self.fit(Text("gana B", font=FONT, font_size=70, weight=BOLD, - color=ROSA), 8.0).move_to(DOWN * 0.75) + color=C_PINK), 8.0).move_to(DOWN * 0.75) ne = self.fit(Text("y no hay ningún error", font=FONT, font_size=44, weight=BOLD, color=WHITE), 8.0).next_to(g, DOWN, buff=0.4) b.play(FadeIn(g, scale=0.7), run_time=0.6) b.play(FadeIn(ne, shift=UP * 0.15), run_time=0.5) with self.beat(6) as b: - b.play(FadeOut(VGroup(tot, g, ne, cab)), run_time=0.4) - nom = VGroup( + b.play(FadeOut(VGroup(tot, g, ne, header)), run_time=0.4) + nm_key = VGroup( self.fit(Text("PARADOJA", font=FONT, font_size=72, weight=BOLD, - color=AMBAR), 8.2), + color=AMBER), 8.2), self.fit(Text("DE SIMPSON", font=FONT, font_size=72, weight=BOLD, - color=AMBAR), 8.2), + color=AMBER), 8.2), ).arrange(DOWN, buff=0.2).move_to(UP * 2.6) - b.play(FadeIn(nom, scale=0.75), run_time=0.8) - b.play(Flash(nom, color=AMBAR, line_length=0.6, num_lines=20, + b.play(FadeIn(nm_key, scale=0.75), run_time=0.8) + b.play(Flash(nm_key, color=AMBER, line_length=0.6, num_lines=20, flash_radius=3.0), run_time=0.7) - def reparto(letra, col, leves, graves, y): - lab = Text(letra, font=FONT, font_size=50, weight=BOLD, color=col) + def split_up(letter, col, minor, graves, y): + lab = Text(letter, font=FONT, font_size=50, weight=BOLD, color=col) lab.move_to([-3.65, y, 0]) - n = leves + graves - wl = leves / n * BARW + n = minor + graves + wl = minor / n * BARW a = Rectangle(width=max(wl, 0.05), height=0.7, stroke_width=0, - fill_color=VERDE, fill_opacity=1).move_to([XL, y, 0], LEFT) + fill_color=C_GREEN, fill_opacity=1).move_to([XL, y, 0], LEFT) c = Rectangle(width=max(BARW - wl, 0.05), height=0.7, stroke_width=0, - fill_color=ROSA, fill_opacity=1).next_to(a, RIGHT, buff=0) - nl = Text(str(leves), font=FONT, font_size=30, weight=BOLD, color="#0B0F1A").move_to(a) + fill_color=C_PINK, fill_opacity=1).next_to(a, RIGHT, buff=0) + nl = Text(str(minor), font=FONT, font_size=30, weight=BOLD, color="#0B0F1A").move_to(a) ng = Text(str(graves), font=FONT, font_size=30, weight=BOLD, color="#0B0F1A").move_to(c) return VGroup(lab, a, c, nl, ng) - leyenda = VGroup( - VGroup(Square(0.34, stroke_width=0, fill_color=VERDE, fill_opacity=1), + legend = VGroup( + VGroup(Square(0.34, stroke_width=0, fill_color=C_GREEN, fill_opacity=1), Text("leves", font=FONT, font_size=32, color=WHITE)).arrange(RIGHT, buff=0.18), - VGroup(Square(0.34, stroke_width=0, fill_color=ROSA, fill_opacity=1), + VGroup(Square(0.34, stroke_width=0, fill_color=C_PINK, fill_opacity=1), Text("graves", font=FONT, font_size=32, color=WHITE)).arrange(RIGHT, buff=0.18), ).arrange(RIGHT, buff=0.8).move_to(UP * 0.55) - rA = reparto("A", CELESTE, 87, 263, -0.65) - rB = reparto("B", ROSA, 270, 80, -1.75) + rA = split_up("A", C_SKY, 87, 263, -0.65) + rB = split_up("B", C_PINK, 270, 80, -1.75) with self.beat(7) as b: - b.play(nom.animate.scale(0.55).move_to(UP * 5.3), run_time=0.5) - b.play(FadeIn(leyenda), run_time=0.4) + b.play(nm_key.animate.scale(0.55).move_to(UP * 5.3), run_time=0.5) + b.play(FadeIn(legend), run_time=0.4) b.play(FadeIn(rA[0]), GrowFromEdge(VGroup(rA[1], rA[2]), LEFT), FadeIn(VGroup(rA[3], rA[4])), run_time=0.8) - b.play(Circumscribe(rA[2], color=ROSA, buff=0.1, stroke_width=5), run_time=0.7) + b.play(Circumscribe(rA[2], color=C_PINK, buff=0.1, stroke_width=5), run_time=0.7) with self.beat(8) as b: b.play(FadeIn(rB[0]), GrowFromEdge(VGroup(rB[1], rB[2]), LEFT), FadeIn(VGroup(rB[3], rB[4])), run_time=0.8) - b.play(Circumscribe(rB[1], color=VERDE, buff=0.1, stroke_width=5), run_time=0.7) + b.play(Circumscribe(rB[1], color=C_GREEN, buff=0.1, stroke_width=5), run_time=0.7) with self.beat(9) as b: - b.play(FadeOut(VGroup(rA, rB, leyenda)), run_time=0.4) + b.play(FadeOut(VGroup(rA, rB, legend)), run_time=0.4) t = VGroup( self.fit(Text("el promedio mezcla", font=FONT, font_size=48, weight=BOLD, color=WHITE), 8.2), self.fit(Text("dos grupos distintos", font=FONT, font_size=48, - weight=BOLD, color=AMBAR), 8.2), + weight=BOLD, color=AMBER), 8.2), self.fit(Text("(datos de un estudio real)", font=FONT, font_size=34, - color=GRIS), 8.2), + color=C_GRAY), 8.2), ).arrange(DOWN, buff=0.35).move_to(UP * 1.6) b.play(FadeIn(t[0], shift=UP * 0.15), run_time=0.55) b.play(FadeIn(t[1], shift=UP * 0.15), run_time=0.55) @@ -172,17 +172,17 @@ class Simpson(TikTok): with self.beat(10) as b: b.play(FadeOut(t), run_time=0.35) - cta = VGroup( + count_txt = VGroup( self.fit(Text("cuando te muestren", font=FONT, font_size=44, weight=BOLD, color=WHITE), 8.0), self.fit(Text("un promedio, preguntá:", font=FONT, font_size=44, weight=BOLD, color=WHITE), 8.0), self.fit(Text("¿de quiénes?", font=FONT, font_size=64, - weight=BOLD, color=AMBAR), 8.0), + weight=BOLD, color=AMBER), 8.0), ).arrange(DOWN, buff=0.3).move_to(UP * 1.4) - b.play(FadeIn(cta[0], shift=UP * 0.12), run_time=0.45) - b.play(FadeIn(cta[1], shift=UP * 0.12), run_time=0.45) - b.play(Write(cta[2]), run_time=0.8) - b.play(Circumscribe(cta[2], color=AMBAR, buff=0.25, stroke_width=6), run_time=0.8) + b.play(FadeIn(count_txt[0], shift=UP * 0.12), run_time=0.45) + b.play(FadeIn(count_txt[1], shift=UP * 0.12), run_time=0.45) + b.play(Write(count_txt[2]), run_time=0.8) + b.play(Circumscribe(count_txt[2], color=AMBER, buff=0.25, stroke_width=6), run_time=0.8) self.finish() diff --git a/scenes/v13_cuerda.py b/scenes/v13_cuerda.py index 5ca224d..c02ea1c 100644 --- a/scenes/v13_cuerda.py +++ b/scenes/v13_cuerda.py @@ -4,13 +4,13 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from manim import * from tiktok import * -CEN = UP * 1.75 -RT = 2.05 # radio de la Tierra en pantalla -HUECO = 0.72 # separacion dibujada (exagerada) +CTR = UP * 1.75 +RT = 2.05 # Earth radius on screen +GAP_W = 0.72 # drawn gap (exaggerated) -def gato(h=0.55, color="#E8EDF7"): - """Silueta simple de gato de perfil, alto total = h.""" +def cat(h=0.55, color="#E8EDF7"): + """Simple cat silhouette in profile, total height = h.""" body = Ellipse(width=0.90, height=0.34, stroke_width=0, fill_color=color, fill_opacity=1).move_to([0, 0.12, 0]) head = Circle(radius=0.17, stroke_width=0, fill_color=color, @@ -27,29 +27,29 @@ def gato(h=0.55, color="#E8EDF7"): return g -class Cuerda(TikTok): +class Rope(TikTok): NAME = "cuerda" - def planeta(self, r, center, col="#1D3B6E"): - p = Circle(radius=r, stroke_width=4, stroke_color=CELESTE, + def planet(self, r, center, col="#1D3B6E"): + p = Circle(radius=r, stroke_width=4, stroke_color=C_SKY, fill_color=col, fill_opacity=1).move_to(center) return p def construct(self): self.prepare() - chip = self.chip("Intuición rota", CELESTE) + chip = self.chip("Intuición rota", C_SKY) - tierra = self.planeta(RT, CEN) - cuerda = Circle(radius=RT + 0.03, stroke_width=7, color=AMBAR).move_to(CEN) + earth = self.planet(RT, CTR) + rope = Circle(radius=RT + 0.03, stroke_width=7, color=AMBER).move_to(CTR) with self.beat(0) as b: b.play(FadeIn(chip, shift=DOWN * 0.3), run_time=0.45) - b.play(GrowFromCenter(tierra), run_time=0.7) - b.play(Create(cuerda), run_time=max(0.8, b.floor - 1.6)) + b.play(GrowFromCenter(earth), run_time=0.7) + b.play(Create(rope), run_time=max(0.8, b.floor - 1.6)) with self.beat(1) as b: - km = Text("40.000 km", font=FONT, font_size=50, weight=BOLD, color=AMBAR) - km.move_to(CEN + DOWN * 0.0) + km = Text("40.000 km", font=FONT, font_size=50, weight=BOLD, color=AMBER) + km.move_to(CTR + DOWN * 0.0) box = RoundedRectangle(corner_radius=0.16, stroke_width=0, fill_color="#0B0F1A", fill_opacity=0.78, width=km.width + 0.5, height=km.height + 0.35).move_to(km) @@ -57,37 +57,37 @@ class Cuerda(TikTok): with self.beat(2) as b: b.play(FadeOut(VGroup(box, km)), run_time=0.3) - seg = Line(LEFT * 0.6, RIGHT * 0.6, stroke_width=9, color=VERDE) - tip = VGroup(*[Line(UP * 0.16, DOWN * 0.16, stroke_width=6, color=VERDE) - .move_to(p) for p in (seg.get_start(), seg.get_end())]) - lab = Text("+ 1 metro", font=FONT, font_size=48, weight=BOLD, color=VERDE) - g = VGroup(VGroup(seg, tip), lab).arrange(DOWN, buff=0.3).move_to(DOWN * 2.15) - b.play(GrowFromCenter(VGroup(seg, tip)), run_time=0.5) + seg_m = Line(LEFT * 0.6, RIGHT * 0.6, stroke_width=9, color=C_GREEN) + tip = VGroup(*[Line(UP * 0.16, DOWN * 0.16, stroke_width=6, color=C_GREEN) + .move_to(p) for p in (seg_m.get_start(), seg_m.get_end())]) + lab = Text("+ 1 metro", font=FONT, font_size=48, weight=BOLD, color=C_GREEN) + g = VGroup(VGroup(seg_m, tip), lab).arrange(DOWN, buff=0.3).move_to(DOWN * 2.15) + b.play(GrowFromCenter(VGroup(seg_m, tip)), run_time=0.5) b.play(FadeIn(lab, shift=UP * 0.15), run_time=0.6) with self.beat(3) as b: - alta = Circle(radius=RT + HUECO, stroke_width=7, color=AMBAR).move_to(CEN) + high_c = Circle(radius=RT + GAP_W, stroke_width=7, color=AMBER).move_to(CTR) radios = VGroup(*[ - DashedLine(CEN + RT * np.array([math.cos(a), math.sin(a), 0]), - CEN + (RT + HUECO) * np.array([math.cos(a), math.sin(a), 0]), + DashedLine(CTR + RT * np.array([math.cos(a), math.sin(a), 0]), + CTR + (RT + GAP_W) * np.array([math.cos(a), math.sin(a), 0]), stroke_width=4, color="#7C8AA8", dash_length=0.07) for a in np.linspace(0, TAU, 16, endpoint=False)]) b.play(FadeOut(g), run_time=0.25) - b.play(Transform(cuerda, alta), run_time=1.0) + b.play(Transform(rope, high_c), run_time=1.0) b.play(FadeIn(radios), run_time=0.5) - esc = Text("(dibujo exagerado)", font=FONT, font_size=28, color=GRIS) - esc.move_to(DOWN * 2.6) - b.play(FadeIn(esc), run_time=0.35) + scale_f = Text("(dibujo exagerado)", font=FONT, font_size=28, color=C_GRAY) + scale_f.move_to(DOWN * 2.6) + b.play(FadeIn(scale_f), run_time=0.35) with self.beat(4) as b: q = VGroup( self.fit(Text("¿cuánto se separa?", font=FONT, font_size=52, weight=BOLD, color=WHITE), 8.0), VGroup( - Text("«nada»", font=FONT, font_size=40, color=GRIS), - Text("«un pelo»", font=FONT, font_size=40, color=GRIS), + Text("«nada»", font=FONT, font_size=40, color=C_GRAY), + Text("«un pelo»", font=FONT, font_size=40, color=C_GRAY), ).arrange(RIGHT, buff=0.7), - ).arrange(DOWN, buff=0.35).move_to(CEN) + ).arrange(DOWN, buff=0.35).move_to(CTR) bx = RoundedRectangle(corner_radius=0.2, stroke_width=0, fill_color="#0B0F1A", fill_opacity=0.82, width=q.width + 0.6, height=q.height + 0.5).move_to(q) @@ -95,67 +95,67 @@ class Cuerda(TikTok): with self.beat(5) as b: b.play(FadeOut(VGroup(bx, q)), run_time=0.3) - gt = gato(HUECO * 0.92).move_to(CEN + UP * (RT + HUECO / 2)) - med = DoubleArrow(CEN + LEFT * RT, CEN + LEFT * (RT + HUECO), buff=0, - color=VERDE, stroke_width=6, tip_length=0.18) - val = Text("15,9 cm", font=FONT, font_size=42, weight=BOLD, color=VERDE) + gt = cat(GAP_W * 0.92).move_to(CTR + UP * (RT + GAP_W / 2)) + med = DoubleArrow(CTR + LEFT * RT, CTR + LEFT * (RT + GAP_W), buff=0, + color=C_GREEN, stroke_width=6, tip_length=0.18) + val = Text("15,9 cm", font=FONT, font_size=42, weight=BOLD, color=C_GREEN) val.next_to(med, UP, buff=0.22) b.play(GrowArrow(med), FadeIn(val), run_time=0.7) b.play(FadeIn(gt, shift=RIGHT * 0.3), run_time=0.7) with self.beat(6) as b: - b.play(FadeOut(VGroup(radios, gt, med, val, esc)), run_time=0.4) - b.play(VGroup(tierra, cuerda).animate.scale(0.52).move_to(UP * 4.6), + b.play(FadeOut(VGroup(radios, gt, med, val, scale_f)), run_time=0.4) + b.play(VGroup(earth, rope).animate.scale(0.52).move_to(UP * 4.6), run_time=0.6) f1 = MathTex(r"r = \frac{P}{2\pi}", color=WHITE).scale(1.6).move_to(UP * 1.5) b.play(Write(f1), run_time=1.0) with self.beat(7) as b: - f2 = MathTex(r"\Delta r = \frac{1\,\text{m}}{2\pi}", color=AMBAR) + f2 = MathTex(r"\Delta r = \frac{1\,\text{m}}{2\pi}", color=AMBER) f2.scale(1.6).move_to(DOWN * 0.5) b.play(Write(f2), run_time=1.1) with self.beat(8) as b: - f3 = MathTex(r"= 0{,}159\ \text{m} = 15{,}9\ \text{cm}", color=VERDE) + f3 = MathTex(r"= 0{,}159\ \text{m} = 15{,}9\ \text{cm}", color=C_GREEN) f3.scale(1.25).move_to(DOWN * 2.15) b.play(Write(f3), run_time=0.9) - nota = self.fit(Text("el tamaño del planeta no aparece", font=FONT, - font_size=40, weight=BOLD, color=ROSA), 8.2) - nota.move_to(DOWN * 3.05) - b.play(FadeIn(nota, shift=UP * 0.15), run_time=0.6) + note_txt = self.fit(Text("el tamaño del planeta no aparece", font=FONT, + font_size=40, weight=BOLD, color=C_PINK), 8.2) + note_txt.move_to(DOWN * 3.05) + b.play(FadeIn(note_txt, shift=UP * 0.15), run_time=0.6) with self.beat(9) as b: - b.play(FadeOut(VGroup(f1, f2, f3, nota, tierra, cuerda)), run_time=0.45) - def par(r, etiqueta, x, col): + b.play(FadeOut(VGroup(f1, f2, f3, note_txt, earth, rope)), run_time=0.45) + def pair_label(r, label, x, col): c0 = [x, 1.75, 0] - p = self.planeta(r, c0, col) - c = Circle(radius=r + 0.30, stroke_width=6, color=AMBAR).move_to(c0) - lab = Text(etiqueta, font=FONT, font_size=36, weight=BOLD, color=WHITE) + p = self.planet(r, c0, col) + c = Circle(radius=r + 0.30, stroke_width=6, color=AMBER).move_to(c0) + lab = Text(label, font=FONT, font_size=36, weight=BOLD, color=WHITE) lab.move_to([x, -0.55, 0]) - v = Text("15,9 cm", font=FONT, font_size=38, weight=BOLD, color=VERDE) + v = Text("15,9 cm", font=FONT, font_size=38, weight=BOLD, color=C_GREEN) v.move_to([x, -1.25, 0]) return VGroup(p, c, lab, v) - iz = par(1.25, "la Tierra", -2.05, "#1D3B6E") - de = par(0.55, "una pelota de tenis", 2.05, "#2F7A3C") + left_lab = pair_label(1.25, "la Tierra", -2.05, "#1D3B6E") + de = pair_label(0.55, "una pelota de tenis", 2.05, "#2F7A3C") self.fit(de[2], 3.9) - b.play(FadeIn(iz), run_time=0.6) + b.play(FadeIn(left_lab), run_time=0.6) b.play(FadeIn(de), run_time=0.6) - ig = Text("=", font=FONT, font_size=64, weight=BOLD, color=AMBAR) + ig = Text("=", font=FONT, font_size=64, weight=BOLD, color=AMBER) ig.move_to([0, 1.75, 0]) b.play(FadeIn(ig, scale=0.6), run_time=0.5) - esc2 = Text("(no está a escala)", font=FONT, font_size=28, color=GRIS) + esc2 = Text("(no está a escala)", font=FONT, font_size=28, color=C_GRAY) esc2.move_to(DOWN * 2.35) b.play(FadeIn(esc2), run_time=0.35) with self.beat(10) as b: - b.play(FadeOut(VGroup(iz, de, ig, esc2)), run_time=0.4) - cta = VGroup( + b.play(FadeOut(VGroup(left_lab, de, ig, esc2)), run_time=0.4) + count_txt = VGroup( self.fit(Text("el tamaño no importa", font=FONT, font_size=56, - weight=BOLD, color=AMBAR), 8.0), + weight=BOLD, color=AMBER), 8.0), self.fit(Text("mandáselo al que dudó", font=FONT, font_size=44, weight=BOLD, color=WHITE), 8.0), ).arrange(DOWN, buff=0.38).move_to(UP * 1.4) - b.play(Write(cta[0]), run_time=0.9) - b.play(FadeIn(cta[1], shift=UP * 0.15), run_time=0.6) + b.play(Write(count_txt[0]), run_time=0.9) + b.play(FadeIn(count_txt[1], shift=UP * 0.15), run_time=0.6) self.finish() diff --git a/scenes/v14_gabriel.py b/scenes/v14_gabriel.py index 24a9621..2a36935 100644 --- a/scenes/v14_gabriel.py +++ b/scenes/v14_gabriel.py @@ -4,8 +4,8 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from manim import * from tiktok import * -XA, XB = 1.0, 7.4 # dominio de 1/x -KX, KY = 1.00, 2.00 # escala a pantalla +XA, XB = 1.0, 7.4 # domain of 1/x +KX, KY = 1.00, 2.00 # scale to screen X0S, CY = -3.75, 1.70 @@ -17,15 +17,15 @@ def sy(x): return KY / x -def rama(sig, n=160, **kw): +def branch(next_one, n=160, **kw): xs = np.linspace(XA, XB, n) - pts = [[sx(x), CY + sig * sy(x), 0] for x in xs] + pts = [[sx(x), CY + next_one * sy(x), 0] for x in xs] m = VMobject(**kw) m.set_points_as_corners(pts) return m -def cuerpo(fill="#1E4E8C", op=0.55): +def body_obj(fill="#1E4E8C", op=0.55): xs = np.linspace(XA, XB, 160) up = [[sx(x), CY + sy(x), 0] for x in xs] dn = [[sx(x), CY - sy(x), 0] for x in reversed(xs)] @@ -34,7 +34,7 @@ def cuerpo(fill="#1E4E8C", op=0.55): return m -def aros(n=9, col=CELESTE, op=0.55): +def hoops(n=9, col=C_SKY, op=0.55): xs = np.linspace(XA + 0.35, XB, n) return VGroup(*[ Ellipse(width=0.34, height=2 * sy(x), stroke_width=3, stroke_opacity=op, @@ -46,7 +46,7 @@ class Gabriel(TikTok): def construct(self): self.prepare() - chip = self.chip("Paradoja del infinito", CELESTE) + chip = self.chip("Paradoja del infinito", C_SKY) with self.beat(0) as b: b.play(FadeIn(chip, shift=DOWN * 0.3), run_time=0.45) @@ -54,37 +54,37 @@ class Gabriel(TikTok): self.fit(Text("el cuerno de", font=FONT, font_size=50, weight=BOLD, color=WHITE), 8.0), self.fit(Text("GABRIEL", font=FONT, font_size=86, - weight=BOLD, color=AMBAR), 8.0), + weight=BOLD, color=AMBER), 8.0), ).arrange(DOWN, buff=0.25).move_to(UP * 2.6) b.play(FadeIn(t[0], shift=UP * 0.15), run_time=0.5) b.play(FadeIn(t[1], scale=0.7), run_time=0.8) - eje = Line([sx(XA) - 0.5, CY, 0], [sx(XB) + 0.35, CY, 0], + axis_obj = Line([sx(XA) - 0.5, CY, 0], [sx(XB) + 0.35, CY, 0], stroke_width=3, color="#33405C") with self.beat(1) as b: b.play(t.animate.scale(0.5).move_to(UP * 5.3), run_time=0.5) - f = MathTex(r"y = \frac{1}{x}", color=AMBAR).scale(1.35).move_to(DOWN * 2.4) - up = rama(+1, stroke_color=AMBAR, stroke_width=6) - b.play(FadeIn(eje), run_time=0.3) + f = MathTex(r"y = \frac{1}{x}", color=AMBER).scale(1.35).move_to(DOWN * 2.4) + up = branch(+1, stroke_color=AMBER, stroke_width=6) + b.play(FadeIn(axis_obj), run_time=0.3) b.play(Create(up), run_time=0.9) b.play(Write(f), run_time=0.7) - body = cuerpo() - low = rama(-1, stroke_color=AMBAR, stroke_width=6) - ar = aros() - boca = Ellipse(width=0.42, height=2 * sy(XA), stroke_width=6, - color=AMBAR).move_to([sx(XA), CY, 0]) + body = body_obj() + low = branch(-1, stroke_color=AMBER, stroke_width=6) + ar = hoops() + mouth = Ellipse(width=0.42, height=2 * sy(XA), stroke_width=6, + color=AMBER).move_to([sx(XA), CY, 0]) with self.beat(2) as b: b.play(Create(low), run_time=0.6) - b.play(FadeIn(body), FadeIn(boca), run_time=0.5) + b.play(FadeIn(body), FadeIn(mouth), run_time=0.5) b.play(LaggedStart(*[Create(a) for a in ar], lag_ratio=0.15), run_time=0.9) - inf = MathTex(r"\cdots \infty", color=GRIS).scale(1.0) + inf = MathTex(r"\cdots \infty", color=C_GRAY).scale(1.0) inf.next_to([sx(XB), CY, 0], RIGHT, buff=0.2) b.play(FadeIn(inf), run_time=0.4) - horn = VGroup(eje, body, up, low, ar, boca, inf) + horn = VGroup(axis_obj, body, up, low, ar, mouth, inf) with self.beat(3) as b: b.play(FadeOut(f), run_time=0.25) @@ -94,58 +94,58 @@ class Gabriel(TikTok): with self.beat(4) as b: b.play(FadeOut(q), run_time=0.25) - lleno = cuerpo(fill=VERDE, op=0.85) - b.play(FadeIn(lleno), run_time=0.7) + filled = body_obj(fill=C_GREEN, op=0.85) + b.play(FadeIn(filled), run_time=0.7) v = MathTex(r"V = \pi\int_1^{\infty}\frac{dx}{x^2} = \pi", - color=VERDE).scale(1.15).move_to(DOWN * 2.05) + color=C_GREEN).scale(1.15).move_to(DOWN * 2.05) b.play(Write(v), run_time=1.1) with self.beat(5) as b: lt = self.fit(Text("≈ 3,14 litros y te sobra", font=FONT, font_size=48, - weight=BOLD, color=VERDE), 8.2).move_to(DOWN * 2.90) + weight=BOLD, color=C_GREEN), 8.2).move_to(DOWN * 2.90) b.play(FadeIn(lt, shift=UP * 0.15), run_time=0.7) with self.beat(6) as b: - b.play(FadeOut(VGroup(v, lt, lleno)), run_time=0.4) + b.play(FadeOut(VGroup(v, lt, filled)), run_time=0.4) q2 = self.fit(Text("¿y pintarlo por fuera?", font=FONT, font_size=52, weight=BOLD, color=WHITE), 8.2).move_to(DOWN * 2.3) - b.play(up.animate.set_stroke(ROSA, 8), low.animate.set_stroke(ROSA, 8), + b.play(up.animate.set_stroke(C_PINK, 8), low.animate.set_stroke(C_PINK, 8), run_time=0.6) b.play(FadeIn(q2, shift=UP * 0.15), run_time=0.6) with self.beat(7) as b: b.play(FadeOut(q2), run_time=0.25) a = MathTex(r"A > 2\pi\int_1^{\infty}\frac{dx}{x} = \infty", - color=ROSA).scale(1.15).move_to(DOWN * 2.15) + color=C_PINK).scale(1.15).move_to(DOWN * 2.15) b.play(Write(a), run_time=1.1) - b.play(Flash(a, color=ROSA, line_length=0.5, num_lines=18, + b.play(Flash(a, color=C_PINK, line_length=0.5, num_lines=18, flash_radius=2.4), run_time=0.7) with self.beat(8) as b: b.play(FadeOut(VGroup(a, horn)), run_time=0.45) - def fila(txt, val, col): + def row(txt, val, col): t1 = Text(txt, font=FONT, font_size=44, weight=BOLD, color=WHITE) t2 = Text(val, font=FONT, font_size=56, weight=BOLD, color=col) return VGroup(t1, t2).arrange(DOWN, buff=0.18) - cmp = VGroup(fila("volumen", "π (finito)", VERDE), - fila("superficie", "∞ (infinita)", ROSA) + cmp = VGroup(row("volumen", "π (finito)", C_GREEN), + row("superficie", "∞ (infinita)", C_PINK) ).arrange(DOWN, buff=0.75).move_to(UP * 2.4) self.fit(cmp, 8.2) b.play(FadeIn(cmp[0], shift=UP * 0.15), run_time=0.55) b.play(FadeIn(cmp[1], shift=UP * 0.15), run_time=0.55) - golpe = VGroup( + impact = VGroup( self.fit(Text("lo llenás de pintura", font=FONT, font_size=48, weight=BOLD, color=WHITE), 8.2), self.fit(Text("pero no lo podés pintar", font=FONT, font_size=48, - weight=BOLD, color=AMBAR), 8.2), + weight=BOLD, color=AMBER), 8.2), ).arrange(DOWN, buff=0.3).move_to(DOWN * 0.6) - b.play(FadeIn(golpe, scale=0.85), run_time=0.8) + b.play(FadeIn(impact, scale=0.85), run_time=0.8) with self.beat(9) as b: - b.play(FadeOut(VGroup(cmp, golpe)), run_time=0.4) + b.play(FadeOut(VGroup(cmp, impact)), run_time=0.4) tr = VGroup( self.fit(Text("la trampa:", font=FONT, font_size=48, - weight=BOLD, color=ROSA), 8.2), + weight=BOLD, color=C_PINK), 8.2), self.fit(Text("la pintura real tiene", font=FONT, font_size=46, weight=BOLD, color=WHITE), 8.2), self.fit(Text("espesor. El cuerno", font=FONT, font_size=46, @@ -158,15 +158,15 @@ class Gabriel(TikTok): with self.beat(10) as b: b.play(FadeOut(tr), run_time=0.35) - cta = VGroup( + count_txt = VGroup( self.fit(Text("con el infinito", font=FONT, font_size=52, weight=BOLD, color=WHITE), 8.0), self.fit(Text("la intuición se rompe", font=FONT, font_size=56, - weight=BOLD, color=AMBAR), 8.0), + weight=BOLD, color=AMBER), 8.0), ).arrange(DOWN, buff=0.35).move_to(UP * 1.4) - b.play(FadeIn(cta[0], shift=UP * 0.15), run_time=0.55) - b.play(Write(cta[1]), run_time=0.9) - b.play(Circumscribe(cta[1], color=AMBAR, buff=0.25, stroke_width=6), + b.play(FadeIn(count_txt[0], shift=UP * 0.15), run_time=0.55) + b.play(Write(count_txt[1]), run_time=0.9) + b.play(Circumscribe(count_txt[1], color=AMBER, buff=0.25, stroke_width=6), run_time=0.8) self.finish() diff --git a/scenes/v15_mobius.py b/scenes/v15_mobius.py index a921c6b..bc00227 100644 --- a/scenes/v15_mobius.py +++ b/scenes/v15_mobius.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -"""Cinta de Mobius renderizada como superficie reglada: se generan quads en 3D, -se proyectan con una camara fija y se dibujan de atras hacia adelante.""" +"""Möbius strip rendered as a ruled surface: quads are generated in 3D, +projected with a fixed camera and drawn back to front.""" import sys, os, math sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from manim import * @@ -11,7 +11,7 @@ CARA_A, CARA_B = "#FFD166", "#2F6FD0" def geom(tau, beta, L=9.4, w=0.62, nu=170, smax=1.0): - """Punto central y direccion transversal de la cinta para s en [0, smax].""" + """Centre point and transverse direction of the strip for s in [0, smax].""" s = np.linspace(0.0, smax, max(int(nu * smax), 2) + 1) th = max(beta * TAU, 1e-3) Rr = L / th @@ -30,11 +30,11 @@ def proj(P): c, sn = math.cos(YAW), math.sin(YAW) x2, y2 = x * c - y * sn, x * sn + y * c ct, st = math.cos(TILT), math.sin(TILT) - return x2, y2 * ct - z * st, y2 * st + z * ct # X, Y, profundidad + return x2, y2 * ct - z * st, y2 * st + z * ct # X, Y, depth def quads(tau, beta, L=9.4, w=0.62, nu=170, scale=1.0, center=ORIGIN): - """Lista de (poligono_screen, profundidad, color) sin ordenar.""" + """Unsorted list of (screen_polygon, depth, color).""" p, d, _ = geom(tau, beta, L, w, nu) A = p - w * d Bv = p + w * d @@ -56,19 +56,19 @@ def quads(tau, beta, L=9.4, w=0.62, nu=170, scale=1.0, cen