aboutsummaryrefslogtreecommitdiffstats
path: root/README.es.md
blob: d5c8837b4b253fbcc9f8df82df58e6a8540c60aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# 24 videos verticales para TikTok — 100% generados localmente

[English](README.md)

Todo corre offline: **Manim** para la animación 2D, **Blender** (EEVEE, headless) para
las escenas 3D, **Godot** (motor de física Jolt, headless) para la física de
rígidos, **qwentts.cpp** (voz clonada rioplatense) para la locución y las
muestras de piano de `keysound` para la música.

## Los videos (`out/`)

| Archivo | Tema | Duración | Gancho |
|---|---|---|---|
| `montyhall.mp4` | Problema de Monty Hall | 39,5 s | genera debate en comentarios |
| `nueves.mp4` | 0,999… = 1 | 38,8 s | genera debate en comentarios |
| `cumple.mp4` | Paradoja del cumpleaños | 38,6 s | dato que sorprende |
| `luna.mp4` | Doblar un papel hasta la Luna | 38,4 s | dato que sorprende |
| `japones.mp4` | Multiplicar con líneas | 38,8 s | útil, se guarda y se comparte |
| `benford.mp4` | Ley de Benford (detectar fraude) | 42,8 s | dato que sorprende + útil |
| `hilbert.mp4` | El hotel infinito | 37,1 s | genera debate en comentarios |
| `reuleaux.mp4` | Por qué las tapas son redondas | 36,0 s | satisfactorio de mirar |
| `regla72.mp4` | La regla del 72 | 35,5 s | útil, se guarda |
| `buffon.mp4` | Sacar π tirando agujas | 36,7 s | belleza matemática |
| `collatz.mp4` | Conjetura de Collatz (3n+1) | 46,2 s | misterio sin resolver |
| `simpson.mp4` | Paradoja de Simpson | 53,0 s | genera debate + útil |
| `cuerda.mp4` | Una cuerda alrededor de la Tierra | 45,4 s | dato que sorprende |
| `gabriel.mp4` | El cuerno de Gabriel | 40,5 s | belleza matemática |
| `mobius.mp4` | Cinta de Möbius | 46,3 s | para hacer en casa |
| `bayes.mp4` | Falso positivo (test médico) | 46,9 s | genera debate + útil |
| `cicloide.mp4` | Braquistócrona: la curva más rápida | 47,6 s | satisfactorio de mirar |
| `dados.mp4` | Dados no transitivos | 50,6 s | genera debate en comentarios |
| `bolapeluda.mp4` | Teorema de la bola peluda (3D) | 41,9 s | satisfactorio de mirar |
| `caos.mp4` | Tres péndulos dobles y 1 mm (3D) | 40,6 s | hipnótico, se mira entero |
| `diferencial.mp4` | Cómo funciona el diferencial (3D) | 44,1 s | útil, se guarda y se comparte |
| `galton.mp4` | Tablero de Galton: 500 bolitas forman la campana (Blender + Godot) | 45,7 s | satisfactorio + pregunta en comentarios |
| `bloques.mp4` | Pila armónica: bloques que sobresalen de la mesa (Blender + Godot) | 48,0 s | para hacer en casa + dato que sorprende |
| `domino.mp4` | Dominó ×1,5: de 5 mm al Aconcagua (Blender + Godot) | 66,7 s | exponencial + pregunta en comentarios (más de 1 min) |

Formato: 1080×1920, 30 fps, H.264 + AAC 48 kHz estéreo, **−13,8 a −15,3 LUFS**
medidos (el objetivo de TikTok es −14, así no te lo bajan ni te lo suben),
pico real por debajo de −1 dBTP.
Subtítulos quemados: el 80 % mira sin audio.

## Textos sugeridos para publicar

**montyhall** — ¿Te quedás o cambiás? El 87% se equivoca 🚪
`#matematica #montyhall #datocurioso #probabilidad #argentina`

**nueves** — 0,999… es EXACTAMENTE 1. Peleame en los comentarios 🧮
`#matematica #mindblown #datocurioso #numeros #viral`

**cumple** — Con 23 personas ya hay 50% de que dos cumplan el mismo día 🎂
`#paradoja #datocurioso #matematica #probabilidad #sabiasque`

**luna** — Doblá un papel 42 veces y llegás a la Luna 🌙
`#exponencial #datocurioso #ciencia #mindblown #sabiasque`

**japones** — Así multiplican en Japón, sin tablas ✏️
`#trucosmatematicos #japon #escuela #tarea #aprendeentiktok`

**benford** — Los números inventados se notan. Así agarran fraudes 🕵️
`#benford #fraude #estadistica #datocurioso #sabiasque`

**hilbert** — Un hotel lleno con infinitas habitaciones… y entran infinitos más 🏨
`#infinito #matematica #paradoja #mindblown #hilbert`

**reuleaux** — Por qué las tapas de alcantarilla son redondas 🛞
`#datocurioso #geometria #ingenieria #sabiasque #reuleaux`

**regla72** — La cuenta que te dice cuándo se duplica tu plata 💸
`#finanzaspersonales #inversiones #interescompuesto #inflacion #argentina`

**buffon** — Tiro agujas al piso y sale π. Sin ningún círculo 🎲
`#pi #matematica #azar #datocurioso #mindblown`

**collatz** — Elegí cualquier número. Nadie pudo demostrar por qué siempre cae al 1 🌀
`#collatz #matematica #misterio #datocurioso #mindblown`

**simpson** — Gana en los dos grupos y pierde en el total. Así te miente un promedio 📊
`#estadistica #paradoja #datos #sabiasque #matematica`

**cuerda** — Le agrego 1 metro a una cuerda que rodea la Tierra. ¿Cuánto se levanta? 🌍
`#datocurioso #matematica #mindblown #sabiasque #ciencia`

**gabriel** — Lo podés llenar de pintura, pero no lo podés pintar 🎺
`#infinito #matematica #paradoja #calculo #mindblown`

**mobius** — Cortá esta cinta al medio y NO van a salir dos ✂️📄
`#mobius #topologia #experimento #hacelovos #datocurioso`

**bayes** — El test acierta el 99% y te dio positivo. La chance de estar enfermo es 9% 🧪
`#probabilidad #estadistica #salud #sabiasque #bayes`

**cicloide** — La recta es el camino más corto… y pierde la carrera 🎢
`#fisica #matematica #satisfying #mindblown #cicloide`

**dados** — Elegí el dado que quieras. Elijo después y te gano igual 🎲
`#probabilidad #dados #juegos #datocurioso #matematica`

**bolapeluda** — No podés peinar una esfera sin dejar un remolino. Es un teorema 🌀
`#topologia #matematica #3d #datocurioso #mindblown`

**caos** — Tres péndulos idénticos salvo 1 milímetro. A los 5 segundos no se parecen en nada 🎲
`#caos #fisica #simulacion #mindblown #efectomariposa`

**diferencial** — Por qué tu auto puede doblar sin que una rueda patine ⚙️
`#mecanica #autos #ingenieria #sabiasque #comofunciona`

**galton** — 500 bolitas, cada una al azar… y todas juntas dibujan una campana 🔔
`#probabilidad #estadistica #galton #satisfying #matematica`

**bloques** — Apilé bloques hasta que el de arriba quedó en el aire. ¿Hasta dónde llegás vos? 📚
`#fisica #experimento #matematica #paraHacerEnCasa #sabiasque`

**domino** — Una ficha de 5 mm puede tirar un edificio. ¿En qué ficha se cae tu casa? 🏠
`#domino #fisica #exponencial #satisfying #sabiasque`

Subí uno por día, mismo horario, y alterná los tres tipos:

- **debate** (comentarios, que es lo que más empuja el alcance): montyhall, nueves,
  hilbert, simpson, cuerda, dados
- **útil** (se guarda y se comparte): japones, regla72, benford, bayes, diferencial, bloques
- **espectáculo** (se mira hasta el final): reuleaux, buffon, mobius, gabriel, collatz,
  luna, cumple, cicloide, bolapeluda, caos, galton, domino

Los tres últimos (`bolapeluda`, `caos`, `diferencial`) están hechos en Blender: sirven
para cortar la monotonía visual de Manim sin cambiar el estilo del canal, porque la
capa de interfaz (chip, barra y subtítulos) es la misma.

`mobius` es el único con llamado a la acción físico ("hacelo con una hoja"):
esos suelen traer videos-respuesta, que valen doble.

### Blender + Godot (`galton`, `bloques`)

Blender diseña y Godot calcula la física:

1. `MODE=sim blender -b -P blender/<nombre>.py` arma la escena y exporta a glTF solo
   los objetos físicos. El prefijo del nombre dice qué es cada uno para Godot
   (`static_` malla estática, `box_` caja estática, `gate_` estática que se saca
   con un evento, `ball_` esfera rígida, `block_` caja rígida). Después corre
   `godot --headless --fixed-fps 240 -- godot/work/<nombre>.json`.
2. `godot/sim.gd` carga el glb en tiempo de ejecución, crea los cuerpos, aplica los
   eventos del guion (soltar, sacar la compuerta) en el segundo exacto del timeline
   y graba la pose de cada cuerpo a 30 fps en coordenadas de Blender
   (`godot/work/<nombre>.bin`).
3. `blender -b -P blender/<nombre>.py` lee esas poses y renderiza con los materiales
   de verdad (madera procedural, latón, acero).

Nada del movimiento está animado a mano. Algunos números que salen de la simulación:

- **galton**: casilleros `[0 1 7 29 70 104 96 83 65 35 10 0 0]` (desvío de 1,82
  casilleros; el binomial ideal de 12 filas da 1,73). La columna del medio tiene
  **96**: es la respuesta a la pregunta del final.
- **bloques**: la pila de 5 con corrimientos armónicos ×0,95 se sostiene con el de
  arriba entero en el aire (8,5 % de bloque). La torre de 16 no se mueve hasta que
  se le apoya un bloque más en la punta izquierda de arriba: se caen 10 de 17.

Dos ajustes que hicieron falta en Godot:

- **galton**: una amortiguación lineal de 6/s, que hace de roce contra el vidrio.
  Sin ella las bolitas toman demasiada velocidad lateral y el reparto sale plano.
- **bloques**: física a 1920 Hz y mundo ×5 (`"hz"` y `"scale"` en el config). Jolt
  tolera 2 cm de penetración y a 240 Hz la torre de 16 se hundía y se volcaba sola.

Ojo: con la notebook Optimus, EEVEE usa la Intel salvo que se lance con
`__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia`, y así es 3,6 veces
más rápido (1,5 s/frame contra 5,5 en galton).

## Cómo regenerar

```bash
conda run -n manim python scripts/gen_audio.py montyhall     # voz + verificación
conda run -n manim manim render -v ERROR --disable_caching \
    --format=mp4 -o montyhall scenes/v1_montyhall.py MontyHall
conda run -n manim python scripts/build_video.py montyhall   # música + mezcla + mux
conda run -n manim python scripts/verify_final.py montyhall  # QA sobre el MP4 final
```

`DRAFT=1` antes del render baja a 540×960 para iterar rápido.

## Cómo está armado

```
scripts/narration.py   guiones: (texto que se locuta, subtítulo en pantalla)
scripts/gen_audio.py   sintetiza cada frase y la verifica transcribiéndola con Whisper
scripts/numspell.py    pasa "99,9%" a "noventa y nueve coma nueve por ciento"
                       para poder comparar la transcripción contra el guion
scripts/regen.py       regenera una frase probando 5 semillas
scripts/repick.py      reelige la mejor toma ya sintetizada
scripts/verify.py      QA: transcribe todos los WAV y marca los dudosos
scripts/verify_final.py  QA final: saca el audio del MP4 ya mezclado y lo transcribe,
                       así se prueba que la voz se entiende por encima del piano
scripts/music.py       cama de piano (progresión propia) + ducking bajo la voz
scripts/build_video.py arma la pista siguiendo el timeline y la pega al video
scenes/tiktok.py       base: 9:16, subtítulos, barra de progreso, "beats"
scenes/v*.py           una escena por video (Manim)
scenes/overlay.py      capa de interfaz para los videos 3D: la misma barra, el mismo
                       chip y los mismos subtítulos, renderizados con alfa
blender/base.py        base de las escenas 3D: paleta, cámara, primitivas, tiempo
blender/*.py           una escena por video 3D
scripts/build_blender.py  compone fondo + 3D + overlay + audio en una sola pasada
```

### Los videos 3D

Se renderizan headless con Blender 5.2 y EEVEE:

```bash
conda run -n manim manim -s --disable_caching -o fondo scenes/overlay.py Backdrop  # trama de puntos
NAME=caos CHIP=Caos CHIPCOL=rosa conda run -n manim manim --transparent \
    --disable_caching -o caos scenes/overlay.py Overlay   # capa + timeline
/ruta/a/blender -b --factory-startup --python blender/caos.py  # secuencia PNG con alfa
conda run -n manim python scripts/build_blender.py caos    # composicion + audio
```

Dos decisiones que se pagaron solas:

- **Sin keyframes.** El render recorre los frames uno por uno y antes de cada uno
  llama a `refresh(f)`, que coloca todo. Así la física se calcula exacta en cada
  cuadro, se pueden renderizar frames salteados para revisar el encuadre, y no hay
  que pelear con la API de acciones por capas de Blender 5.x.
- **El fondo no está en la escena 3D.** Blender renderiza con `film_transparent` y el
  fondo punteado se compone abajo con ffmpeg, así es pixel por pixel el mismo que el
  de los 18 videos de Manim.
- **El metal necesita algo que reflejar.** `studio_world()` pone un gradiente de
  horizonte en el mundo: no se ve (el film es transparente) pero es lo único que
  separa el acero del plástico gris. Sin eso, un material metálico queda plano.

`blender/base.py` genera las piezas mecánicas por código:

| Función | Qué hace |
|---|---|
| `bevel_gear` | Engranaje cónico con semiángulo de cono y dientes en espiral. Dos engranan a 90° cuando sus γ suman 90: 45+45 para los del diferencial, 72+18 para corona y piñón (y ahí la relación de dientes queda fijada por sin72/sin18 = 3,07, que es por qué son 43 y 14) |
| `full_wheel` | Neumático con tacos y canales + llanta de aleación con radios + disco de freno |
| `ring_sector` | Pared curva: con dos de estas y dos bridas sale una caja de diferencial con ventanas, que es lo que deja ver los engranajes de adentro |
| `flat_ring`, `screws` | Bridas y coronas de bulones |

Los PNG intermedios quedan en `render/<nombre>/` (unos 4 GB los tres). Sirven para
recomponer con otro audio o subtítulos sin volver a renderizar, que son entre 30 y
55 minutos por video; si no hacen falta, se borran.

`SAMPLES=8` baja las muestras de EEVEE (16 por defecto, 64 de fábrica: eso son
10 s/frame en una GTX 1050 y 1,6 s/frame con 8). `EVERY=55` renderiza un frame de
cada 55 para armar la hoja de contacto sin esperar el render entero.

La sincronía no se estima: la escena escribe `out/<nombre>_timeline.json` con el
tiempo real de cada bloque y el audio se coloca en esas marcas exactas.

## Variables de entorno

| Variable | Para qué |
|---|---|
| `PROJECT` | raíz del proyecto (por defecto, la carpeta del repositorio) |
| `MODE=sim` | en las escenas con Godot: recalcula la física en lugar de renderizar |
| `SAMPLES`, `PCT` | muestras de EEVEE y porcentaje de resolución |
| `FROM_FRAME`, `TO_FRAME`, `EVERY`, `RESUME` | qué frames renderizar |
| `QWENTTS_DIR`, `VOICE_REF` | qwentts.cpp y la voz de referencia para la locución |
| `PIANO_DIR` | muestras de piano de keysound |

Los nombres anteriores (`PROY`, `MODO`, `MUESTRAS`, `DESDE`, `HASTA`, `SALTO`,
`SEGUIR`, `PRUEBA`) se siguen aceptando.

## Voz

El repositorio no trae la voz clonada. `VOICE_REF` es el prefijo de sus tres
archivos (`.spk`, `.rvq` y `.txt`), que se sacan de una grabación con
`qwen-codec --talker`.

## Qué no está en git

Todo lo que se genera: `render/` (PNG intermedios), `media/` (salida de Manim),
`audio/` (locución), `out/` (videos finales y timelines), `tmp/` y
`godot/work/` (simulaciones). Se regeneran con los pasos de arriba.

## Sobre la música

Las progresiones (Am–F–C–G, Em–C–G–D, Am–Em–Dm–E, Cm–G#–A#–Gm, A–E–F#m–D, etc.) son propias y están tocadas con muestras
de piano sueltas, así que el audio es tuyo y no te lo va a marcar el Content ID.
Si preferís un audio en tendencia de TikTok, subilo igual y agregalo desde la app:
la voz ya está mezclada a −14 LUFS y se sigue entendiendo por encima.

## Licencia

MIT