Terminar el proyecto
Un cierre para la animación, el archivo final, la revisión y la exportación
Objetivo
La explicación ya funciona. En este capítulo le damos un final: cuando el punto completa la vuelta, la proyección se retira y la escena se queda quieta un momento con el círculo y la onda. Después comprobaremos el proyecto y lo exportaremos a video.
Cambios
Describir el archivo
Añade un docstring al principio de main.py. Es lo primero que leerá quien abra el archivo, incluido tú dentro de unos meses:
"""Del círculo al seno: el proyecto final del tutorial de Gaanim.
Un punto gira sobre un círculo y su altura dibuja una onda seno. Un único
parámetro, ``theta``, gobierna el punto, el radio, la onda y su proyección.
"""Un cierre que respira
Al final de la zona # Línea de tiempo, sustituye scene.wait(1) por:
scene.play([projection.animate.fade_out().duration(0.5)])
scene.wait(1.5)La línea de proyección era un andamio para explicar la relación entre los dos puntos. Al terminar la vuelta queda superpuesta a la recta, así que la retiramos con fade_out. La pausa final de 1.5 segundos da tiempo a mirar el resultado antes de que el video termine: una exportación acaba exactamente en el último instante de la línea de tiempo.
Archivo completo
Este es el programa terminado. Es el mismo archivo que se instala con Gaanim en _docs/tutorial/circulo_al_seno.py, dentro de la carpeta del paquete gaanim:
"""Del círculo al seno: el proyecto final del tutorial de Gaanim.
Un punto gira sobre un círculo y su altura dibuja una onda seno. Un único
parámetro, ``theta``, gobierna el punto, el radio, la onda y su proyección.
"""
import math
from gaanim import WHITE, YELLOW, Axis, Color, Easing, Scene, computed, stagger
# Paleta
BACKGROUND = Color(15, 23, 42)
PRIMARY = Color(96, 165, 250)
ACCENT = YELLOW
MUTED = Color(148, 163, 184)
scene = Scene(frame=(16, 9), background=BACKGROUND, margin=0.6)
CENTER = (-4.5, -1.0)
R = 1.5
# Texto
title = scene.text("Del círculo al seno", role="title")
title.fill(WHITE).move_to(0, 3.4)
caption = scene.text("Un punto que gira a radio constante", role="subtitle")
caption.fill(MUTED).move_to(0, 2.8)
formula = scene.text("$y = r sin(theta)$", role="subtitle").fill(WHITE)
explanation = scene.text("La altura del punto dibuja la onda.", role="body")
explanation.fill(MUTED)
panel = scene.layout.column([formula, explanation], gap=0.2, width=6.5)
panel.move_to(3.5, 1.6)
# Círculo
theta = scene.viz.parameter(0.0)
orbit = scene.geometry.circle(R).stroke(PRIMARY, 0.05).no_fill().move_to(*CENTER)
circle_ref = scene.geometry.polar_point(CENTER, R, theta)
point = scene.geometry.dot(0.125).fill(ACCENT).follow(circle_ref)
radius = scene.geometry.line(CENTER, point).stroke(MUTED, 0.025)
point.z_index(1)
# Recta y onda
axis = (
Axis.linear(0, 2 * math.pi)
.ticks(math.pi)
.numbers("pi", denominator=1)
.style(color=MUTED, number_color=MUTED)
)
number_line = scene.viz.number_line(axis, length=8)
number_line.drawable().move_to(2, CENTER[1])
wave = number_line.function(math.sin, normal_scale=R, reveal=theta)
wave.stroke(PRIMARY, 0.04).no_fill()
height = computed(lambda angle: R * math.sin(angle), inputs=[theta])
wave_dot = scene.geometry.dot(0.1).fill(ACCENT)
wave_dot.follow(number_line.point_ref(theta, normal_offset=height))
projection = scene.geometry.line(point, wave_dot).stroke(ACCENT, 0.025)
# Línea de tiempo
scene.play(stagger(
title.animate.write().duration(0.8),
caption.animate.fade_in().duration(0.5),
each=0.5,
))
scene.play(stagger(
orbit.animate.create().duration(0.8),
radius.animate.create().duration(0.4),
point.animate.grow_from_center().duration(0.5).easing(Easing.SNAPPY),
each=0.25,
))
scene.play(stagger(
formula.animate.write().duration(0.8),
explanation.animate.write().duration(0.8),
each=0.2,
))
scene.play(stagger(
number_line.animate.create().duration(0.8),
projection.animate.create().duration(0.4),
wave_dot.animate.grow_from_center().duration(0.5).easing(Easing.SNAPPY),
each=0.25,
))
scene.play([theta.animate.set(2 * math.pi).duration(4).easing(Easing.LINEAR)])
scene.play([projection.animate.fade_out().duration(0.5)])
scene.wait(1.5)
scene.render()
Comprobar el proyecto
Desde la carpeta del proyecto:
gaanim check .gaanim check ejecuta la escena sin abrir ventana e informa de su duración, en este caso 10 segundos. Si algo falla, muestra el error de Python con su línea. Con --strict también falla ante las advertencias.
Después abre la escena con gaanim . y revísala entera, también a saltos con la barra de reproducción:
- Todo el texto queda dentro del margen de seguridad.
- Ningún objeto aparece antes de su animación de entrada.
- El radio, la proyección y la onda siguen al punto en cualquier instante.
- Las pausas son lo bastante largas para leer la fórmula.
Exportar
Cuando la escena esté lista, expórtala sin tocar el script:
gaanim export . --output exports/del-circulo-al-seno.mp4La extensión elige el formato: .mp4 y .webm para video, .webp y .gif para animaciones ligeras en una web o un documento, y .png para una secuencia de imágenes. Todos salvo PNG necesitan FFmpeg. --quality elige entre draft (rápido), standard (el valor por defecto) y production, y --from y --to exportan solo un tramo:
gaanim export . --output exports/vuelta.webp --from 4 --to 8La referencia completa de estas opciones está en Línea de comandos.
Lo que has aprendido
Scenedefine un lienzo de unidades lógicas, con el origen en el centro y un margen de seguridad.scene.textyscene.geometrycrean objetos y devuelven handles;fill,stroke,no_fillyz_indexles dan estilo y orden..animate,scene.play,stagger,duration,Easingyscene.waitconstruyen la línea de tiempo.scene.layout.columnorganiza contenido que depende de su medida y pasa a ser el dueño de la posición de sus hijos.- Un
Parameteranimado contheta.animate.set(...)gobierna todo lo que depende de él:polar_point,follow, líneas con objetos como extremos y valorescomputed. Axis,scene.viz.number_line,number_line.function(..., reveal=theta)ynumber_line.point_refconvierten el ángulo en una curva sin deriva.gaanim checkygaanim exportvalidan y producen la salida sin cambiar el script.
Cómo continuar
Modifica el proyecto antes de empezar otro. Guarda una copia y prueba, en este orden:
- Cambia
Ra1.2: el círculo, el punto, la onda y la proyección se adaptan. - Da dos vueltas: lleva el eje y la animación de
thetahasta4 * math.pi. - Añade una segunda curva con
number_line.function(math.cos, ...)y otro color. - Anima el panel con otras entradas y easings de Movimiento.
Para ir más lejos, las guías resuelven tareas concretas: Layout para composiciones más ricas, Reactividad para rastros, valores calculados y simulaciones, Presentaciones para convertir la escena en diapositivas y Capturas y comparación para comprobar que un cambio no altera lo que se ve. Consulta la Referencia cuando tengas una pregunta concreta sobre una función.