diff options
| author | Elvis Claros Castro <elvis@claros.ar> | 2026-09-26 20:21:47 -0300 |
|---|---|---|
| committer | Elvis Claros Castro <elvis@claros.ar> | 2026-09-26 20:21:47 -0300 |
| commit | 59355909f2de9236af8168a26c70bcf6caa3b285 (patch) | |
| tree | 686186e2086f81aa22ad25e78eb29fca3cbadc9a /scenes/v17_cicloide.py | |
| download | 100cia-videos-59355909f2de9236af8168a26c70bcf6caa3b285.tar.gz 100cia-videos-59355909f2de9236af8168a26c70bcf6caa3b285.zip | |
Import video pipeline as it was
Diffstat (limited to 'scenes/v17_cicloide.py')
| -rw-r--r-- | scenes/v17_cicloide.py | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/scenes/v17_cicloide.py b/scenes/v17_cicloide.py new file mode 100644 index 0000000..5e6d4b9 --- /dev/null +++ b/scenes/v17_cicloide.py @@ -0,0 +1,275 @@ +# -*- coding: utf-8 -*- +"""La braquistocrona: la recta pierde contra la cicloide. +Todo el movimiento sale de la fisica real (caida libre sin rozamiento).""" +import sys, os, math +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from manim import * +from tiktok import * + +TH1 = 4.0553 # angulo final: da D/H = 3, la curva pasa por debajo +DX = 7.0 +RR = DX / (TH1 - math.sin(TH1)) +HH = RR * (1 - math.cos(TH1)) +X0, Y0 = -3.5, 3.15 +T_C = 1.55 # lo que tarda la cicloide en pantalla +G = RR * (TH1 / T_C) ** 2 # gravedad ajustada a ese tiempo +LL = math.hypot(DX, HH) +AA = G * HH / LL +T_L = math.sqrt(2 * LL / AA) # 2,18 s: la recta llega 40% mas tarde +P0 = np.array([X0, Y0, 0.0]) +P1 = np.array([X0 + DX, Y0 - HH, 0.0]) +UU = (P1 - P0) / LL +VMAX = math.sqrt(4 * G * RR) + +RW = 1.12 # rueda de la bici +XW = -PI * RW +YW = 0.55 +R2, BX, BY = 1.22, 0.0, 0.30 # cuenco de la tautocrona +T_TAU = 2.0 +WW = PI / (2 * T_TAU) + + +def cyc(th): + return np.array([X0 + RR * (th - math.sin(th)), Y0 - RR * (1 - math.cos(th)), 0.0]) + + +def marca(th): + return np.array([XW + RW * (th - math.sin(th)), YW + RW * (1 - math.cos(th)), 0.0]) + + +def cuenco(th): + return np.array([BX + R2 * (th - math.sin(th)) - R2 * PI, + BY + 2 * R2 - R2 * (1 - math.cos(th)), 0.0]) + + +def poli(f, a, b, col, w=8, n=200): + m = VMobject(stroke_color=col, stroke_width=w) + m.set_points_as_corners([f(t) for t in np.linspace(a, b, n)]) + return m + + +class Cicloide(TikTok): + NAME = "cicloide" + + def construct(self): + self.prepare() + chip = self.chip("¿Cuál gana?", AMBAR) + + plata = DashedLine([X0 - 0.8, Y0 + 0.66, 0], [X0 + 1.7, Y0 + 0.66, 0], + stroke_width=3, color=GRIS, dash_length=0.1) + pc = Dot(radius=0.19, color=AMBAR).move_to([X0 - 0.4, Y0 + 0.66, 0]) + pr = Dot(radius=0.19, color=CELESTE).move_to([X0 + 0.08, Y0 + 0.66, 0]) + meta = DashedLine(P1 + UP * 1.35, P1 + DOWN * 0.95, stroke_width=6, + color=VERDE, dash_length=0.14) + rotm = Text("META", font=FONT, font_size=32, weight=BOLD, color=VERDE) + rotm.next_to(meta, UP, buff=0.14) + + leyenda = VGroup( + VGroup(Dot(radius=0.15, color=AMBAR), + Text("curva", font=FONT, font_size=38, weight=BOLD, + color=AMBAR)).arrange(RIGHT, buff=0.22), + VGroup(Dot(radius=0.15, color=CELESTE), + Text("recta", font=FONT, font_size=38, weight=BOLD, + color=CELESTE)).arrange(RIGHT, buff=0.22), + ).arrange(RIGHT, buff=1.3).move_to([0, -1.3, 0]) + + with self.beat(0) as b: + b.play(FadeIn(chip, shift=DOWN * 0.3), run_time=0.4) + b.play(Create(plata), run_time=0.4) + b.play(FadeIn(pc, scale=0.4), FadeIn(pr, scale=0.4), run_time=0.5) + b.play(Create(meta), FadeIn(rotm), FadeIn(leyenda), run_time=0.7) + + recta = Line(P0, P1, stroke_width=8, color=CELESTE) + curva = poli(cyc, 0, TH1, AMBAR) + + with self.beat(1) as b: + b.play(Create(recta), run_time=0.9) + b.play(Create(curva), run_time=max(0.9, b.floor - 1.4)) + + inter = Text("?", font=FONT, font_size=115, weight=BOLD, + color=WHITE).move_to([0.0, 1.62, 0]) + + with self.beat(2) as b: + b.play(FadeIn(inter, scale=0.5), run_time=0.5) + b.play(Indicate(inter, scale_factor=1.25, color=AMBAR), run_time=0.9) + + rot = Text("el más corto", font=FONT, font_size=40, weight=BOLD, + color=CELESTE).move_to([-0.15, 3.02, 0]) + + with self.beat(3) as b: + b.play(FadeOut(inter), run_time=0.3) + b.play(recta.animate.set_stroke(width=12), FadeIn(rot, shift=UP * 0.2), + run_time=0.55) + b.play(ShowPassingFlash(recta.copy().set_stroke(WHITE, 13), time_width=0.5), + run_time=1.0) + b.play(recta.animate.set_stroke(width=8), run_time=0.3) + + # --- la carrera -------------------------------------------------------- + T = ValueTracker(0.0) + + def up_c(m): + t = min(T.get_value(), T_C) + m.move_to(cyc(min(t * math.sqrt(G / RR), TH1))) + + def up_r(m): + t = min(T.get_value(), T_L) + m.move_to(P0 + UU * (0.5 * AA * t * t)) + + med = VGroup(Circle(radius=0.34, stroke_width=0, fill_color=AMBAR, fill_opacity=1), + Text("1º", font=FONT, font_size=34, weight=BOLD, color="#0B0F1A")) + med.move_to(P1 + UP * 0.72).set_opacity(0) + med.add_updater(lambda m: m.set_opacity(1.0 if T.get_value() >= T_C else 0.0)) + + with self.beat(4) as b: + b.play(FadeOut(rot), FadeOut(plata), pc.animate.move_to(P0), + pr.animate.move_to(P0), run_time=0.5) + ya = Text("¡LARGA!", font=FONT, font_size=66, weight=BOLD, + color=VERDE).move_to([0, 1.7, 0]) + b.play(FadeIn(ya, scale=1.7), run_time=0.3) + b.play(FadeOut(ya), run_time=0.22) + pc.add_updater(up_c) + pr.add_updater(up_r) + b.add(med) + b.play(T.animate.set_value(T_L), run_time=T_L, rate_func=linear) + pc.clear_updaters(); pr.clear_updaters(); med.clear_updaters() + + nivel = DashedLine([X0 - 0.2, Y0 - HH, 0], [X0 + DX + 0.2, Y0 - HH, 0], + stroke_width=4, color=GRIS, dash_length=0.12) + fondo = cyc(PI) + nota = Text("pasa por debajo de la meta", font=FONT, font_size=36, weight=BOLD, + color=ROSA).move_to([0.3, -0.95, 0]) + flecha = Arrow(nota.get_top() + RIGHT * 0.9, fondo + DOWN * 0.22, buff=0.06, + color=ROSA, stroke_width=6, max_tip_length_to_length_ratio=0.2) + gap = VGroup( + Text("la recta tardó", font=FONT, font_size=38, weight=BOLD, color=WHITE), + Text("40% más", font=FONT, font_size=64, weight=BOLD, color=CELESTE), + ).arrange(RIGHT, buff=0.3).move_to([0.0, -2.15, 0]) + + with self.beat(5) as b: + b.play(FadeOut(leyenda), Create(nivel), run_time=0.45) + b.play(FadeIn(nota, shift=UP * 0.2), GrowArrow(flecha), run_time=0.6) + b.play(FadeIn(gap, shift=UP * 0.2), run_time=0.55) + + # --- por que gana: velocidad temprana ---------------------------------- + riel_c = Rectangle(width=6.2, height=0.32, stroke_width=0, fill_color="#1B2233", + fill_opacity=1).move_to([0.0, -1.30, 0]) + riel_r = riel_c.copy().move_to([0.0, -2.20, 0]) + bar_c = Rectangle(width=0.02, height=0.32, stroke_width=0, fill_color=AMBAR, + fill_opacity=1).move_to(riel_c.get_left(), LEFT) + bar_r = Rectangle(width=0.02, height=0.32, stroke_width=0, fill_color=CELESTE, + fill_opacity=1).move_to(riel_r.get_left(), LEFT) + lab_c = Text("curva", font=FONT, font_size=30, weight=BOLD, color=AMBAR) + lab_r = Text("recta", font=FONT, font_size=30, weight=BOLD, color=CELESTE) + lab_c.next_to(riel_c, LEFT, buff=0.18) + lab_r.next_to(riel_r, LEFT, buff=0.18) + izq_c, izq_r = riel_c.get_left()[0], riel_r.get_left()[0] + + def barra(m, v, x0, y): + w = max(0.02, 6.2 * v / VMAX) + m.stretch_to_fit_width(w) + m.move_to([x0 + w / 2, y, 0]) + + def ub_c(m): + t = min(T.get_value(), T_C) + th = t * math.sqrt(G / RR) + barra(m, math.sqrt(max(0.0, 2 * G * RR * (1 - math.cos(th)))), izq_c, -1.30) + + def ub_r(m): + barra(m, AA * min(T.get_value(), T_L), izq_r, -2.20) + + with self.beat(6) as b: + b.play(FadeOut(VGroup(nivel, flecha, nota, gap, med)), run_time=0.35) + T.set_value(0.0) + b.play(pc.animate.move_to(P0), pr.animate.move_to(P0), + FadeIn(VGroup(riel_c, riel_r, lab_c, lab_r, bar_c, bar_r)), run_time=0.45) + pc.add_updater(up_c); pr.add_updater(up_r) + bar_c.add_updater(ub_c); bar_r.add_updater(ub_r) + b.play(T.animate.set_value(T_L), run_time=max(1.8, b.floor - 1.1), + rate_func=linear) + for m in (pc, pr, bar_c, bar_r): + m.clear_updaters() + + # --- la rueda de la bici ------------------------------------------------ + viejo = VGroup(recta, curva, meta, rotm, pc, pr, + riel_c, riel_r, lab_c, lab_r, bar_c, bar_r) + piso = Line([-3.7, YW, 0], [3.7, YW, 0], stroke_width=6, color="#3A5487") + TH = ValueTracker(0.0) + + def dibujar_rueda(): + th = TH.get_value() + c = np.array([XW + RW * th, YW + RW, 0.0]) + g = VGroup(Circle(radius=RW, stroke_width=6, color=CELESTE).move_to(c)) + for k in range(3): + a = -th - PI / 2 + k * TAU / 3 + g.add(Line(c, c + RW * np.array([math.cos(a), math.sin(a), 0.0]), + stroke_width=4, color="#3A5487")) + return g + + rueda = always_redraw(dibujar_rueda) + clavo = always_redraw(lambda: Dot(radius=0.14, color=AMBAR).move_to(marca(TH.get_value()))) + rastro = TracedPath(lambda: marca(TH.get_value()), stroke_color=AMBAR, stroke_width=8) + nom = Text("CICLOIDE", font=FONT, font_size=66, weight=BOLD, + color=AMBAR).move_to([0, -1.15, 0]) + + with self.beat(7) as b: + b.play(FadeOut(viejo), run_time=0.4) + b.play(Create(piso), run_time=0.3) + b.add(rastro, rueda, clavo) + b.play(TH.animate.set_value(TAU), run_time=max(1.6, b.floor - 1.5), + rate_func=linear) + b.remove(rueda, clavo, rastro) + arco = poli(marca, 0, TAU, AMBAR) + b.add(arco) + b.play(FadeIn(nom, shift=UP * 0.2), run_time=0.45) + + # --- la tautocrona ------------------------------------------------------ + tazon = poli(cuenco, 0, TAU, AMBAR) + TH0 = [0.30 * PI, 0.56 * PI, 0.82 * PI] + cols = [ROSA, CELESTE, VERDE] + bolas = VGroup(*[Dot(radius=r, color=c).move_to(cuenco(t)) + for t, c, r in zip(TH0, cols, (0.22, 0.175, 0.13))]) + guias = VGroup(*[DashedLine([-4.1, cuenco(t)[1], 0], cuenco(t), stroke_width=3, + color=c, dash_length=0.1) + for t, c in zip(TH0, cols)]) + + with self.beat(8) as b: + b.play(FadeOut(VGroup(nom, piso)), run_time=0.3) + b.play(Transform(arco, tazon), run_time=0.9) + b.play(LaggedStart(*[FadeIn(d, scale=0.5) for d in bolas], lag_ratio=0.25), + run_time=0.8) + b.play(Create(guias), run_time=0.5) + + TT = ValueTracker(0.0) + + def soltar(th0): + def u(m): + c = math.cos(th0 / 2) * math.cos(WW * TT.get_value()) + m.move_to(cuenco(2 * math.acos(max(-1.0, min(1.0, c))))) + return u + + with self.beat(9) as b: + b.play(FadeOut(guias), run_time=0.25) + for d, t0 in zip(bolas, TH0): + d.add_updater(soltar(t0)) + b.play(TT.animate.set_value(T_TAU), run_time=T_TAU, rate_func=linear) + for d in bolas: + d.clear_updaters() + juntas = Text("las tres, juntas", font=FONT, font_size=58, weight=BOLD, + color=VERDE).move_to([0, -1.5, 0]) + b.play(Flash(bolas[0], color=WHITE, flash_radius=0.9, num_lines=18), + FadeIn(juntas, shift=UP * 0.25), run_time=0.7) + + cierre = VGroup( + Text("1696", font=FONT, font_size=76, weight=BOLD, color=AMBAR), + Text("Bernoulli desafía", font=FONT, font_size=44, weight=BOLD, color=WHITE), + Text("a los matemáticos", font=FONT, font_size=44, weight=BOLD, color=WHITE), + Text("Newton lo resuelve", font=FONT, font_size=46, weight=BOLD, color=VERDE), + Text("esa misma noche", font=FONT, font_size=46, weight=BOLD, color=VERDE), + ).arrange(DOWN, buff=0.26).move_to(UP * 0.75) + + with self.beat(10) as b: + b.play(FadeOut(VGroup(arco, bolas, juntas)), run_time=0.4) + b.play(FadeIn(cierre[:3], shift=UP * 0.25), run_time=0.7) + b.play(FadeIn(cierre[3:], shift=UP * 0.25), run_time=0.7) + + self.finish() |