Objetivo

La izquierda ya contiene el círculo. Al terminar este capítulo, arriba a la derecha aparecerá un panel con la fórmula 𝑦=𝑟sin(𝜃) y una frase que anuncia lo que ocurrirá: la altura del punto dibujará una onda. El panel se escribirá justo después de la entrada del círculo.

El espacio queda repartido con intención: a la izquierda ocurre el fenómeno, arriba a la derecha lo nombramos y debajo del panel queda sitio libre para la onda del capítulo 7.

Cambios

Texto matemático

Añade al final de la zona # Texto:

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)

scene.text compone con Typst lo que va entre $...$: sin se escribe en redonda, theta se convierte en θ y las variables van en cursiva. El resto de la cadena es texto normal.

Estos dos textos no llevan move_to: su posición la decidirá el panel.

Un panel con Layout

Justo debajo, agrupa los dos textos en una columna:

panel = scene.layout.column([formula, explanation], gap=0.2, width=6.5)
panel.move_to(3.5, 1.6)

scene.layout.column apila sus hijos de arriba abajo, separados por gap unidades y alineados a la izquierda. width=6.5 fija el ancho del panel: si la frase no cupiera, se partiría en varias líneas dentro de ese ancho. Después movemos el panel entero; sus hijos lo acompañan.

A partir de aquí, el panel decide dónde descansan formula y explanation. No los coloques con un move_to inmediato: Gaanim lo rechaza con un LayoutOwnershipError. Para recolocarlos, mueve panel; para animarlos, usa .animate sobre ellos, que parte del sitio que les da el panel.

La entrada del panel

En la zona # Línea de tiempo, añade una tercera entrada antes de scene.wait(1):

scene.play(stagger(
    formula.animate.write().duration(0.8),
    explanation.animate.write().duration(0.8),
    each=0.2,
))

El panel ocupa del segundo 2 al 3, cuando el espectador ya conoce el círculo: la explicación llega después del fenómeno y no compite con él.

Archivo completo

from gaanim import WHITE, YELLOW, Color, Easing, Scene, 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
orbit = scene.geometry.circle(R).stroke(PRIMARY, 0.05).no_fill().move_to(*CENTER)
point = scene.geometry.dot(0.125).fill(ACCENT).move_to(CENTER[0] + R, CENTER[1])
radius = scene.geometry.line(CENTER, point).stroke(MUTED, 0.025)
point.z_index(1)

# 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.wait(1)
scene.render()
animation preview

En el siguiente capítulo, Dar vida a la escena, el punto empezará a girar y el radio lo seguirá.