Fábricas de scene.geometry: primitivas, líneas, flechas, trayectorias, grupos, booleanas, geometría reactiva, dibujos axonométricos y 3D
scene.geometry crea las formas vectoriales de la escena. Cada fábrica devuelve un Drawable centrado en el origen, listo para encadenar estilo, posición y animación. Las medidas están en unidades de escena: el marco lógico mide 16 × 9.
from gaanim import BLUE, GOLD, WHITE, Scene
scene =Scene(frame=(16, 9), background="#0f172a")
disk = scene.geometry.circle(1).fill(BLUE).move_to(-3, 0)
card = scene.geometry.rounded_rect(2.4, 1.4, 0.2).fill(GOLD).move_to(0, 0)
star = scene.geometry.star(5, 1, 0.45).no_fill().stroke(WHITE, 0.05).move_to(3, 0)
scene.play([disk.animate.grow_from_center(), card.animate.create(), star.animate.create()], duration=1.2)
scene.render()
Primitivas
Círculos, rectángulos, elipses y puntos: la base de casi cualquier diagrama.
Círculo centrado en el origen. Sirve para nodos, marcadores y diagramas radiales; combínalo con annulus para anillos.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
radius
float
obligatorio
Radio en unidades de escena.
Ejemplo
from gaanim import BLUE, Scene
scene =Scene(frame=(16, 9), background="#0f172a")
node = scene.geometry.circle(0.625).fill(BLUE).move_to(0, 0)
scene.play([node.animate.create().duration(1.0)])
scene.render()
Nube de puntos sin Cartesian2D: todos los círculos comparten un camino, así que miles de puntos (una sección de Poincaré, una muestra) siguen siendo un único objeto para fill, opacity, el layout y la animación. Listas vacías, posiciones no finitas o un radio no positivo lanzan ValueError.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
positions
Sequence[tuple[float, float]]
obligatorio
Centros en coordenadas de escena; al menos uno y todos finitos.
radius
float
0.06
Radio de cada punto, mayor que cero.
Ejemplo
import math
from gaanim import GOLD, Scene
scene =Scene(frame=(16, 9), background="#0f172a")
spiral = [(0.02* i * math.cos(0.2* i), 0.02* i * math.sin(0.2* i)) for i inrange(200)]
cloud = scene.geometry.points(spiral, radius=0.04).fill(GOLD)
scene.play([cloud.animate.fade_in().duration(0.5)])
scene.render()
Líneas y flechas
Segmentos, flechas rectas y curvas, arcos y conectores. Los extremos reactivos (Endpoint) aceptan tuplas, objetos, AnchorPoint y PointRef. Las líneas, arcos, polilíneas, curvas y trazos seguidos nacen sin relleno: solo dibujan su trazo aunque ganen área.
Dos tuplas crean una línea normal que se mueve con su grupo. Los extremos que son referencias se resuelven en el espacio del mundo en cada fotograma, así que la línea sigue a los objetos. Con length, next_to coloca la línea sin cambiar su longitud ni su orientación. Mezclar length con extremos lanza TypeError; longitudes o direcciones inválidas lanzan ValueError.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
p1 / p2
Endpoint
obligatorio
Extremos fijos o reactivos.
x1, y1, x2, y2
float
obligatorio
Forma compatible con cuatro coordenadas fijas.
length
float
obligatorio
Longitud positiva de una línea centrada en el origen.
direction
Direction
Direction.RIGHT
Orientación con length; admite diagonales y vectores 2D no nulos.
Ejemplo
from gaanim import Anchor, GOLD, WHITE, Scene
scene =Scene(frame=(16, 9), background="#0f172a")
left = scene.geometry.dot(0.11).fill(GOLD).move_to(-1.5, -0.375)
card = scene.geometry.rect(1.5, 0.875).move_to(1.125, 0.44)
connector = scene.geometry.line(
left.anchor_point(Anchor.RIGHT),
card.anchor_point(Anchor.LEFT),
).stroke(WHITE, 0.04)
scene.play([left.animate.shift_by(0.375, 0.75).duration(0.7)])
scene.render()
Con length, la línea es independiente de las coordenadas:
from gaanim import Direction, GOLD, WHITE, Scene
scene =Scene(frame=(16, 9), background="#0f172a")
title = scene.text("Una línea bajo el título", size=0.5).fill(WHITE)
rule = scene.geometry.line(length=5).stroke(GOLD, 0.035).next_to(
title, Direction.DOWN, spacing=0.25,
)
scene.play([title.animate.fade_in(), rule.animate.create()], duration=1)
scene.render()
Flecha sólida de (x1, y1) a (x2, y2). fill la colorea entera: salvo que stroke fije otro, su contorno sigue al relleno (igual en double_arrow, curved_arrow y curved_arrow_arc). Para flechas cortas añade max_head_ratio=0.3, y .no_stroke() para una silueta limpia. Extremos no finitos o medidas no positivas lanzan ValueError; extremos coincidentes dan un camino vacío.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
x1, y1
float
obligatorio
Cola.
x2, y2
float
obligatorio
Punta.
head_length / head_width / body_width
float | None
None
Medidas en unidades de escena; por defecto 0.18, 0.15 y 0.036.
max_head_ratio
float | None
None
En (0, 1], limita la punta respecto a la longitud total y escala su ancho en proporción.
Ejemplo
from gaanim import GOLD, Scene
scene =Scene(frame=(16, 9), background="#0f172a")
arrow = scene.geometry.arrow(-3, 0, 3, 0).fill(GOLD).no_stroke()
scene.play([arrow.animate.create().duration(0.8)])
scene.render()
Flecha con punta en ambos extremos, para rangos y relaciones bidireccionales. Las puntas se acortan para no solaparse; un body_width fino sirve para cotas. Para una cota completa, con líneas de extensión, usa scene.mechanics.dimension. Medidas no positivas lanzan ValueError.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
x1, y1, x2, y2
float
obligatorio
Extremos.
head_length / head_width / body_width
float | None
None
Igual que en arrow: por defecto 0.18, 0.15 y 0.036.
Flecha curva para ciclos y rotaciones. Comparte medidas y validación con arrow, así que flechas rectas y curvas pueden compartir estilo. La punta se acorta en arcos cortos y se estrecha en radios pequeños.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
x1, y1, x2, y2
float
obligatorio
Inicio y final.
angle
float
obligatorio
Desviación en radianes; el signo elige el lado de la curva.
Flecha a lo largo de un arco circular con centro (cx, cy), radio y ángulos en radianes. Acepta las mismas medidas que curved_arrow; max_head_ratio se mide sobre radius * abs(sweep_angle).
Arco de circunferencia con centro (cx, cy); start_angle y sweep_angle en radianes. Nace sin relleno, como las demás líneas; fill() rellena el segmento entre la cuerda y el arco.
Ejemplo
from gaanim import GOLD, Scene
scene =Scene(frame=(16, 9), background="#0f172a")
arc = scene.geometry.arc(0, 0, 0.75, 0.0, 2.0).no_fill().stroke(GOLD, 0.05)
scene.play([arc.animate.create().duration(0.8)])
scene.render()
Arco de elipse, para el borde de un cilindro, un cono o una probeta en perspectiva: media elipse es sweep_angle=math.pi. Nace sin relleno y admite tip, recortes y create(), como arc. Radios no positivos o valores no finitos lanzan ValueError.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
cx, cy
float
obligatorio
Centro.
rx, ry
float
obligatorio
Radios positivos en x y en y.
start_angle, sweep_angle
float
obligatorio
Radianes, en sentido antihorario desde +x, como en arc.
Ejemplo
import math
from gaanim import GOLD, WHITE, Scene
scene =Scene(frame=(16, 9), background="#0f172a")
front = scene.geometry.ellipse_arc(0, -1.2, 1.6, 0.45, math.pi, math.pi).stroke(WHITE, 0.05)
back = scene.geometry.ellipse_arc(0, -1.2, 1.6, 0.45, 0, math.pi).stroke(WHITE, 0.03).opacity(0.4)
top = scene.geometry.ellipse_arc(0, 1.2, 1.6, 0.45, 0, 2* math.pi).stroke(GOLD, 0.05)
left = scene.geometry.line(-1.6, -1.2, -1.6, 1.2).stroke(WHITE, 0.05)
right = scene.geometry.line(1.6, -1.2, 1.6, 1.2).stroke(WHITE, 0.05)
scene.play([shape.animate.create().duration(0.8) for shape in (front, back, top, left, right)])
scene.render()
Línea discontinua para guías y aristas ocultas. Acepta los mismos extremos que line: dos puntos, objetos, PointRef o AnchorPoint, o cuatro coordenadas; entre objetos, los sigue en cada cuadro. Los guiones miden dash_length y los separa gap_length (0.16 y 0.10 por defecto). create() los dibuja uno tras otro desde el inicio.
Flecha rellena y reactiva que sigue a sus extremos durante la animación y el seek. Usa via para los codos: no esquiva obstáculos. La punta se limita al último segmento no nulo. Referencias de otra escena lanzan ValueError. Anima el conector completo con create().
Parámetros
Nombre
Tipo
Predeterminado
Descripción
start / end
Endpoint
obligatorio
Cola y punta, fijas o reactivas.
via
Sequence[Endpoint] | None
None
Puntos intermedios en orden.
head_length / head_width / body_width
float
0.18 / 0.15 / 0.036
Medidas positivas en unidades de escena.
Ejemplo
from gaanim import Anchor, Scene
scene =Scene(frame=(16, 9))
left = scene.geometry.rect(2, 1).move_to(-3, 0)
right = scene.geometry.rect(2, 1).move_to(3, 0)
link = scene.geometry.connector(left.anchor_point(Anchor.RIGHT), right.anchor_point(Anchor.LEFT))
scene.play(link.animate.create())
scene.play(right.animate.shift_by(0, 1))
scene.render()
Polígonos y símbolos
Polígonos arbitrarios y regulares, sectores y marcas habituales en diagramas y demostraciones.
Estrella de points puntas (al menos 2) con radios exterior e interior.
Ejemplo
from gaanim import GOLD, Scene
scene =Scene(frame=(16, 9), background="#0f172a")
star = scene.geometry.star(5, 0.875, 0.4).fill(GOLD).move_to(0, 0)
scene.play([star.animate.spin_in_from_nothing().duration(1.0)])
scene.render()
Llave entre (x1, y1) y (x2, y2) con profundidad height distinta de cero; el signo elige el lado. Se dibuja como una llave tipográfica: brazos rectos, rizos del tamaño de su profundidad y una punta, con un grosor que se afina hacia los extremos (16 % de height en los brazos, al menos 0.02). Es una figura rellena y sin trazo: fill le da color y un tema la pinta como la familia glyph, con el color del texto. Para una llave bajo una parte de un texto, usa selection.animate.brace(...) (ver Texto).
Ejemplo
from gaanim import WHITE, Scene
scene =Scene(frame=(16, 9), background="#0f172a")
brace = scene.geometry.brace(-1, -0.25, 1, -0.25, 0.3).fill(WHITE)
label = scene.text("intervalo").move_to(0, -0.69)
scene.play([brace.animate.create().duration(0.7), label.animate.fade_in().duration(0.4)])
scene.render()
Marca de ángulo recto con brazos de arm_length. Colócala en la esquina de un triángulo. No tiene relleno y toma el estilo de línea del tema, como line.
Polilínea que une los puntos en orden. Con coordenadas fijas, abierta o cerrada, points cambia sus vértices. Una coordenada puede ser un Parameter, un Computed o scene.time: entonces la polilínea se redibuja en cada fotograma con sus valores, lo que da forma a cualquier línea o contorno a partir de señales (la traza de un audio, un gráfico en vivo). Un punto cuyo valor no se puede leer queda fuera de ese fotograma.
Contorno de varias bolas fundidas como gotas de líquido (efecto gooey). Se recalcula en cada fotograma a partir de dónde están las bolas y de su tamaño, así que animarlas anima la masa; las bolas dejan de dibujarse (opacidad 0). El resultado es un trazado vectorial normal en coordenadas de la escena: nítido a cualquier tamaño, con relleno, trazo y efectos como cualquier otro. Una lista vacía, una bola de otra escena, un threshold no positivo o un smoothness negativo lanzan ValueError.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
balls
Sequence[Drawable]
obligatorio
Los objetos que forman la masa; cada uno cuenta como un círculo con el centro de su caja y la mitad de su lado mayor.
threshold
float
1.0
Divide cada radio: por encima de 1 adelgaza las bolas.
smoothness
float
0.4
Distancia en unidades de escena a la que dos bolas se unen con un cuello; 0 es una unión simple.
Ejemplo
from gaanim import BLUE, Scene
scene =Scene(frame=(16, 9), background="#0b1020")
drops = [scene.geometry.circle(r).move_to(x, 0) for x, r in ((-3, 0.9), (0, 1.1), (3, 0.7))]
blob = scene.geometry.metaballs(drops, smoothness=0.9).fill(BLUE)
scene.play([drops[0].animate.move_to(-1.2, 0.3).duration(1.5), drops[2].animate.move_to(1.3, -0.3).duration(1.5)])
scene.render()
Curva compuesta con comandos de cursor, al estilo de Typst. Los controles de quad y cubic aceptan un punto, None (el control cae sobre su extremo) o "auto": en un quad refleja respecto al cursor el control del quad anterior, y en el primer control de un cubic, el segundo control del cubic anterior. Tras otro tipo de tramo, o como segundo control de un cubic, "auto" equivale a None. close y close_smooth llevan una lista vacía.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
commands
Sequence[CurveCommand]
obligatorio
Pares (nombre, puntos): move, line, quad, cubic, close y close_smooth. Añade _rel a un comando de dibujo para usar puntos relativos al cursor.
Agrupa objetos para moverlos, rotarlos y escalarlos como uno, conservando las coordenadas locales de cada miembro (también las de sus updaters). Los miembros ya visibles no se ocultan al unirse a un grupo que contiene un trazo o una fuerza diferidos. write, create y los fundidos del grupo son entradas explícitas de todo el subárbol. Para distribuir miembros usa Layout.
Ejemplo
from gaanim import BLUE, Direction, Scene
scene =Scene(frame=(16, 9), background="#0f172a")
row = scene.geometry.group([scene.geometry.dot(0.125).fill(BLUE), scene.text("Etiqueta").move_to(0.9, 0)]).move_to(0, 0)
scene.play([row.animate.fade_in_from(Direction.DOWN, distance=0.3).duration(0.6)])
scene.render()
Marco vivo alrededor de los límites de sus objetivos en el mundo: los sigue al moverse, escalarse, rotar o reordenarse en un layout. Usa el trazo del tema y ningún relleno. Su posición y geometría pertenecen al vínculo: anima los objetivos o cambia de objetivo con retarget. Objetivos vacíos, de otra escena o medidas inválidas lanzan TypeError o ValueError.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
targets
Drawable | TextSelection | Sequence
obligatorio
Uno o varios objetos, partes de texto o partes de ecuación.
padding
float | (v, h) | (t, r, b, l)
0.12
Margen no negativo alrededor de la unión.
corner_radius
float
0.08
Radio no negativo, limitado al tamaño del marco.
Ejemplo
from gaanim import GOLD, Scene, part
scene =Scene(frame=(16, 9), background="#0f172a")
eq = scene.text.equation("E =", part("mass", "m"), part("light", "c^2"))
frame = scene.geometry.surrounding_rect(eq["mass"]).stroke(GOLD, 0.04)
scene.play([eq.animate.fade_in(), frame.animate.create()])
scene.play([frame.retarget(eq["light"]).duration(0.8)])
scene.render()
Devuelve un Anim que interpola los cuatro bordes del marco hacia nuevos objetivos y después los sigue. Origen y destino pueden seguir moviéndose durante la transición; los seeks reproducen la misma geometría. Configura el tiempo con duration y easing.
Copias de una figura colocadas por una transformación acumulada o por una distribución, como el Repeater de After Effects o el Duplicator de Cavalry. El resultado es un grupo cuyos miembros son las copias, en orden: connect las recorre y el estilo del grupo llega a todas. La figura original es la copia 0; los grupos y textos se copian con sus miembros. count y animate.count muestran solo las primeras copias y funden la siguiente con la parte fraccionaria, para construir el patrón copia a copia.
count copias (1 a 10 000): la copia i se desplaza i * offset, gira i * rotate radianes y escala scale ** i, alrededor de about si se da (así los pétalos orbitan el centro de la flor) o de su propio pivote. Su opacidad va de opacity[0] en la primera a opacity[1] en la última. Valores no finitos, una escala no positiva, opacidades fuera de [0, 1] o una figura de otra escena lanzan ValueError.
Una copia de shape centrada en cada punto de distribution; la figura original va al primero. Con una distribución orientada, cada copia gira para seguirla. Una figura de otra escena lanza ValueError.
Ejemplo
from gaanim import GOLD, Distribution, Scene
scene =Scene(frame=(16, 9), background="#0d1322")
arrow = scene.geometry.polygon([(-0.3, -0.2), (0.35, 0.0), (-0.3, 0.2)]).fill(GOLD)
ring = scene.geometry.duplicate(arrow, Distribution.circle(16, 2.6, orient=True))
scene.play([ring.animate.rotate_by(1.2).duration(1.5)])
scene.render()
Dónde van las copias de Geometry.duplicate. grid: columns × rows puntos separados spacing (un número o (x, y)), fila a fila desde arriba a la izquierda. circle: puntos en una circunferencia desde start radianes en sentido antihorario; con orient cada copia mira hacia fuera. along: puntos repartidos por longitud a lo largo de una polilínea, extremos incluidos; con orient siguen su dirección. random: puntos uniformes dentro de (xmin, ymin, xmax, ymax), iguales para la misma seed. phyllotaxis: espiral de girasol, el punto i a spacing * sqrt(i) girado por el ángulo áureo. points devuelve las posiciones. Menos de 1 o más de 10 000 puntos, o valores no finitos, lanzan ValueError.
Líneas vivas entre puntos, como el Connect Shape de Cavalry (efecto plexus). points es un grupo (sus miembros) o una lista de objetos; cada punto es el centro de sus límites y las líneas los siguen al moverse, en cada fotograma y búsqueda. mode elige los pares no más largos que max_distance: "range" une todos, "nearest" cada punto con sus neighbors más cercanos y "sequential" cada punto con el siguiente. Los puntos ocultos se saltan y los pares salen en un orden fijo. Con fade_by_distance las conexiones largas son más tenues. Por defecto son líneas blancas finas (o el estilo de línea del tema); cámbialas con stroke. Menos de dos puntos, un max_distance no positivo o neighbors menor que 1 lanzan ValueError.
Ejemplo
from gaanim import CYAN, Distribution, Scene
scene =Scene(frame=(16, 9), background="#060b16")
cloud = scene.geometry.duplicate(scene.geometry.dot(0.06).fill(CYAN), Distribution.random(40, (-6.5, -3.0, 6.5, 3.0), seed=7))
links = scene.geometry.connect(cloud, max_distance=2.2).stroke(CYAN, 0.025)
scene.play([cloud.animate.rotate_by(0.6).duration(2.0)])
scene.render()
Operaciones booleanas
Unión, intersección, diferencia y diferencia simétrica de áreas vectoriales cerradas.
Los operandos se conservan como objetos independientes y el resultado copia el estilo que tiene el primero al crearlo (relleno, trazo, opacidad y efectos); los cambios de estilo posteriores de los operandos no lo cambian, ni siquiera con live=True. Con live=True, el resultado se reconstruye cuando cambian los caminos o las transformaciones de sus fuentes. tolerance es el error máximo, en unidades de escena, al aproximar las curvas; el valor predeterminado (0.0025, unos 0.3 px a 1080p) mantiene suaves los círculos. Súbelo solo para acelerar resultados live=True muy complejos. Debe ser finita y positiva. rule acepta "nonzero" o "evenodd"; operandos de otra escena o valores inválidos lanzan ValueError.
from gaanim import BLUE, Scene
scene =Scene(frame=(16, 9), background="#0f172a")
left = scene.geometry.circle(1).move_to(-0.5, 0)
right = scene.geometry.circle(1).move_to(0.5, 0)
merged = scene.geometry.union(left, right).fill(BLUE)
left.opacity(0)
right.opacity(0)
scene.play([merged.animate.create().duration(1.0)])
scene.render()
Genera un interior vectorial que se intersecta con la silueta en cada fotograma. Oculta la máscara original si su relleno no debe tapar el nivel. Con una fuente reactiva, las muestras finitas se limitan a [0, 1]; una muestra no finita genera un diagnóstico y usa el valor de respaldo. Anima el nivel con animate.fill_level(...); los saltos en la línea de tiempo restauran vínculos, valores y cortes.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
mask
Drawable
obligatorio
Silueta que recorta el relleno; conserva su visibilidad propia.
Hacia dónde crece el relleno: up (desde abajo), down, left (desde la derecha) o right (desde la izquierda).
keep_outline
bool
True
Añade una copia reactiva que solo usa el trazo de la máscara.
Ejemplo
drop = scene.media.svg("drop.svg").scale_to(2.5).no_fill().stroke("#dbeafe", 0.05).opacity(0)
water = scene.geometry.fill_level(drop, "#38bdf8", 0.0)
scene.play([water.animate.fill_level(0.72).duration(1.4)])
Con una fuente reactiva, el nivel sigue a un parámetro que también puede mostrarse:
Cambia el nivel de un objeto creado con fill_level desde el cursor. Otra fuente reactiva reemplaza el vínculo; un número en [0, 1] lo termina con un corte reversible. Mientras esté vinculado, anima la fuente: animate.fill_level(...) se rechaza. Valores fijos inválidos, fuentes de otra escena u otros tipos de objeto lanzan ValueError.
Ejemplo
water.set_fill_level(0.4)
Geometría reactiva
Relaciones que se recalculan en el mismo fotograma: «este extremo es la esquina del rectángulo» o «este punto está al 35 % de la curva».
En lugar de recalcular una línea desde Python, declaras la relación y el motor regenera solo la geometría afectada después de aplicar las transformaciones de sus fuentes. La relación se evalúa para cualquier instante, así que el seek es exacto. Los objetos reactivos suelen empezar ocultos: anima su entrada (fade_in, create o write) junto con lo que los conduce. Las magnitudes reactivas (Parameter, Variable, Computed) están en Visualización y la guía Reactividad enseña a combinarlas.
Puntos de referencia
Puntos no dibujados que se aceptan en cualquier lugar donde se admite un Endpoint. Sus coordenadas aceptan números o fuentes reactivas.
Punto afín entre dos extremos (alpha=0 en el primero, 1 en el segundo) más un desplazamiento en el mundo. alpha puede ser un número, un Parameter, una Variable o un Computed.
Ejemplo
a = scene.geometry.dot(0.1).move_to(-3, 0)
b = scene.geometry.dot(0.1).move_to(3, 1)
middle = scene.geometry.point_between(a, b, alpha=0.5, offset=(0, 0.3))
label = scene.text("M", role="label").follow(middle)
Punto a distancia radius y ángulo angle (radianes) de un origen; ambos pueden ser reactivos.
Ejemplo
theta = scene.viz.parameter(0.0)
tip = scene.geometry.polar_point((0, 0), 1.5, theta)
hand = scene.geometry.tracking_line((0, 0), tip).stroke(WHITE, 0.05)
scene.play([hand.animate.create(), theta.animate.set(3.14).duration(1.5)])
Líneas y marcas que siguen a otros objetos
FÁBRICAGeometry.tracking_linetracking_line(from_: Endpoint, to: Endpoint) -> Drawable
Línea oculta cuyos extremos siguen a sus fuentes en el mismo fotograma y que conserva el progreso de create o write activos. Revélala con una animación de entrada.
Ejemplo
from gaanim import GOLD, WHITE, Scene
scene =Scene(frame=(16, 9), background="#0f172a")
tip = scene.geometry.dot(0.11).fill(GOLD).move_to(1.25, 0.375)
rod = scene.geometry.tracking_line((-1.25, -0.375), tip).no_fill().stroke(WHITE, 0.05)
scene.play([rod.animate.create().duration(0.8), tip.animate.shift_by(-0.5, 1).duration(0.8)])
scene.play([rod.animate.write().duration(0.8), tip.animate.shift_by(1, -0.625).duration(0.8)])
scene.render()
Punto oculto que recorre la curva por longitud de arco, sin callbacks de Python durante la reproducción. Lee la curva tal como se dibuja: si se mueve, gira, escala o anima, o está dentro de un grupo como unos ejes, el punto sigue sobre ella. Lo mismo vale para tangent_on_curve, normal_on_curve y curvature_on_curve.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
curve
Drawable
obligatorio
Polilínea, Bézier, arco, círculo o polígono.
tracker
float | Parameter | Variable | Computed
obligatorio
Posición de 0 a 1 por longitud de arco; se limita a ese rango.
Ejemplo
from gaanim import GOLD, WHITE, Scene
from math import cos, sin, pi
scene =Scene(frame=(16, 9), background="#0f172a")
t = scene.viz.parameter(0.0)
curve = scene.geometry.polyline([(1.375*cos(u), 0.75*sin(2*u)) for u in (2*pi*i/240for i inrange(241))]).no_fill().stroke(WHITE, 0.025)
dot = scene.geometry.point_on_curve(curve, t).fill(GOLD)
scene.play([dot.animate.fade_in().duration(0.3), t.animate.set(1.0).duration(1.6)])
scene.render()
Segmento oculto de longitud length, centrado en el punto de la curva y orientado según la tangente. Usa el mismo muestreo por longitud de arco que point_on_curve.
Ejemplo
from gaanim import GOLD, WHITE, Scene
from math import cos, sin, pi
scene =Scene(frame=(16, 9), background="#0f172a")
t = scene.viz.parameter(0.35)
curve = scene.geometry.polyline([(1.375*cos(u), 0.75*sin(u)) for u in (2*pi*i/240for i inrange(241))]).no_fill().stroke(WHITE, 0.025)
tangent = scene.geometry.tangent_on_curve(curve, t, length=0.875).stroke(GOLD, 0.04)
scene.play([tangent.animate.fade_in().duration(0.3), t.animate.set(0.9).duration(1.4)])
scene.render()
Como tangent_on_curve, girado 90° en sentido antihorario.
Ejemplo
t = scene.viz.parameter(0.2)
curve = scene.geometry.polyline([(math.cos(u /40), math.sin(u /40)) for u inrange(252)])
normal = scene.geometry.normal_on_curve(curve, t, length=0.6).stroke(RED, 0.04)
Círculo osculador oculto, estimado a partir de tres muestras separadas window (una fracción de la longitud de arco). En los extremos de una curva abierta las muestras se desplazan hacia dentro, y en una curva cerrada pasan por la costura, así que el círculo existe también en 0 y en 1. En un tramo recto no se dibuja. Dale estilo con no_fill().stroke(...).
Ejemplo
from gaanim import RED, WHITE, Scene
from math import cos, sin, pi
scene =Scene(frame=(16, 9), background="#0f172a")
t = scene.viz.parameter(0.25)
curve = scene.geometry.polyline([(1.375*cos(u), 0.75*sin(u)) for u in (2*pi*i/240for i inrange(241))]).no_fill().stroke(WHITE, 0.025)
circle = scene.geometry.curvature_on_curve(curve, t).no_fill().stroke(RED, 0.025)
scene.play([circle.animate.fade_in().duration(0.3), t.animate.set(0.7).duration(1.4)])
scene.render()
Flecha en arco oculta que se regenera en cada fotograma a partir de un escalar, sin callbacks de Python. Ideal para marcar un ángulo que cambia. Como toda flecha sólida, fill la colorea entera; stroke fija su contorno.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
tracker
float | Parameter | Variable | Computed
obligatorio
Conduce el ángulo de barrido.
cx, cy, radius, start_angle
float
obligatorio
Centro, radio y ángulo inicial en radianes.
sweep_scale / sweep_offset
float
1.0 / 0.0
El barrido es tracker * sweep_scale + sweep_offset.
Ejemplo
from gaanim import WHITE, Scene
scene =Scene(frame=(16, 9), background="#0f172a")
theta = scene.viz.parameter(0.3)
rot = scene.geometry.always_redraw_arc(theta, 0, 0, 0.69, 0.0).fill(WHITE)
scene.play([rot.animate.fade_in().duration(0.3), theta.animate.set(5.0).duration(1.6)])
scene.render()
Estela 2D que dibuja el recorrido de un objeto. El muestreo empieza en el cursor donde se declara, así que los segmentos anteriores y los seeks no la rellenan de antemano. Revélala con trail.animate.fade_in(). Con dissipating_time las muestras caducan por la cola en la vista previa, los seeks, las capturas y la exportación. Tras un seek, la estela se reconstruye si la fuente la mueven updaters, drive_from_samples, vínculos reactivos o follow; una fuente movida solo con .animate empieza una estela nueva.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
source
Drawable
obligatorio
Objeto cuya posición se muestrea.
dissipating_time
float | None
None
Segundos que dura cada muestra en la estela; positivo.
Para planos técnicos en isométrica o dimétrica, con el estilo plano del resto de la escena. scene.geometry.axonometric(...) devuelve una proyección que dibuja puntos de un modelo 3D como polígonos y polilíneas 2D normales: se rellenan, se trazan y se animan como cualquier otro Drawable. No usa el 3D experimental de la sección siguiente.
El modelo tiene z hacia arriba y su planta en x/y, como un modelo estructural. Un metro del eje menos acortado mide scale unidades de escena, así que la isométrica conserva la longitud de los tres ejes, como un dibujo isométrico, y el origen del modelo se dibuja en origin. Los trazos de estos dibujos van centrados en el contorno, de modo que una cara vista de canto (un muro en planta) se sigue viendo como línea.
depth_sort ordena las caras como un pintor: donde dos dibujos se solapan en pantalla, el más cercano queda encima. Llámalo antes de mostrarlos, como z_index, con la vista en la que importa el orden (view= toma la de otra proyección, como la vista final de una animación): el orden no se recalcula al cambiar de vista. Tres caras que se tapan en ciclo, o dos que se atraviesan, no tienen un orden exacto; parte esas caras en piezas. animate_to lleva todos los dibujos de la proyección a otra vista, vértice a vértice, y la proyección dibuja desde entonces con esa vista.
from gaanim import Scene
scene =Scene(frame=(16, 9), background="#f5f1ea")
plan = scene.geometry.axonometric("plan", origin=(-2.5, -2), scale=0.9)
iso = scene.geometry.axonometric("isometric", origin=(-2.5, -2), scale=0.9)
H =2.5
faces = [
plan.polygon([(0, 0, 0), (5, 0, 0), (5, 4, 0), (0, 4, 0)]).fill("#d8d2c6"),
plan.polygon([(0, 4, 0), (5, 4, 0), (5, 4, H), (0, 4, H)]).fill("#c46a45"),
plan.polygon([(0, 0, 0), (0, 4, 0), (0, 4, H), (0, 0, H)]).fill("#9c4f32"),
plan.polygon([(0, 0, H), (5, 0, H), (5, 2, H), (0, 2, H)]).fill("#9aa3ad"),
]
for face in faces:
face.stroke("#2b2b2b", 0.03)
columns = [plan.line((x, y, 0), (x, y, H)).stroke("#4e535c", 0.06) for x, y in [(5, 2), (5, 4)]]
plan.depth_sort(faces + columns, z_index=10, view=iso)
scene.wait(0.4)
scene.play(plan.animate_to(iso).defaults(duration=1.6))
scene.wait(0.6)
scene.render()
Asigna z_index de atrás hacia delante en la vista actual o en la de view. Donde dos dibujos se solapan en pantalla, el más cercano allí queda encima; el resto sigue su profundidad media, aunque solo se toquen en un vértice o una arista. Un dibujo de otra proyección, uno repetido o un z_index sin sitio para todos lanzan ValueError.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
drawables
Sequence[Drawable]
obligatorio
Dibujos hechos con esta proyección.
z_index
int
0
z_index del dibujo más lejano; cada siguiente suma uno.
view
Axonometric | None
None
Ordena según la vista de esta proyección en lugar de la actual.
Lleva cada vértice en línea recta a donde lo dibuja la vista de other; fija la duración con defaults(duration=...). Desde entonces esta proyección dibuja con esa vista, y other no se lleva los dibujos. Una proyección que no ha dibujado nada lanza ValueError.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
other
Axonometric
obligatorio
Proyección cuya vista se adopta.
Devuelve: Composition — Un animate.points por dibujo, en paralelo.
Ejemplo
plan = scene.geometry.axonometric("plan")
scene.play(iso.animate_to(plan).defaults(duration=1.5))
PROPIEDADAxonometric.azimuthazimuth: float
Devuelve: float — Giro de la vista actual alrededor de z, en radianes. elevation, origin y scale dan el resto de la vista.
Geometría 3D
Primitivas iluminadas con Material3D, líneas en el espacio y estelas 3D. Gaanim las dibuja con Vello: proyecta cada triángulo con la cámara, los ordena de atrás hacia delante y calcula la luz en la CPU. No hay sombras ni búfer de profundidad, así que las superficies que se cruzan pueden mostrar errores de orden, y todo el 3D queda debajo del contenido 2D. Gaanim usa un mundo dextrógiro con Y hacia arriba: cilindros y conos crecen a lo largo de Y y los planos yacen en XZ. Los puntos 3D son tuplas (x, y, z). La guía Cámara y 3D explica la cámara en perspectiva.
Primitive3D extiende Drawable, así que posición, rotación, escala, opacidad y material se animan y son deterministas al hacer seek. Los parámetros de geometría quedan fijos tras la construcción. create() hace crecer la malla desde su centro mientras aparece; write(), que es solo vectorial, se rechaza.
Configura el único equipo automático de luces 3D de la escena. "studio" ilumina las primitivas con material al estilo de Gaanim (luz del cielo y del suelo, una luz principal suave desde arriba a la izquierda y un brillo leve en los bordes), escalada por intensity; "none" deja solo la emisión. shadows se acepta por compatibilidad, pero el contenido 3D no proyecta sombras. Las superficies, los gráficos 3D y las líneas no reciben luz. Las luces de light_3d sustituyen a la luz principal.
Una luz para las primitivas con material, en el origen. No dibuja nada; es un dibujable, así que move_to_3d la coloca y animate, los grupos y follow la mueven. Una luz "point" alumbra desde su posición en todas direcciones; una "directional", con rayos paralelos desde la dirección de su posición vista desde el origen, como el sol. Mientras la escena muestra luces, sustituyen a la luz principal fija de lighting_3d, cuya luz del cielo y del suelo se mantiene, y cada una alumbra en su color (blanco por defecto) con su intensity, atenuada por su opacidad: fade_in y fade_out la encienden y la apagan. Con lighting_3d("none") solo alumbran las luces. Las superficies, los gráficos 3D y las líneas no reciben luz. Una intensity negativa o no finita, u otro kind, lanzan ValueError.
CLASEMaterial3D.matte / metal / emissive / color / roughness / metallic / emissive_color / emissive_strengthMaterial3D(color=WHITE, roughness=0.55, metallic=0.0, emissive=None, emissive_strength=0.0)
Material de una primitiva 3D iluminada, sombreado por Gaanim: la rugosidad y la metalicidad dan forma al brillo y la emisión se suma a la luz. Sus propiedades numéricas se interpolan en espacio lineal y cuyos rangos se validan. Los atajos Material3D.matte(color), Material3D.metal(color) y Material3D.emissive(color, strength=1.0) cubren los aspectos habituales. Las propiedades color, roughness, metallic, emissive_color y emissive_strength son de solo lectura.
El contorno se lee al compilar la escena, así que funcionan textos, ecuaciones, grupos y SVG importados, y se rellena como se dibuja (regla nonzero): las letras conservan sus huecos. La malla queda centrada donde está el origen en el plano z = 0, con la tapa frontal hacia la cámara predeterminada. Los trazos de menos del doble del chaflán conservan aristas rectas. El origen sigue siendo un drawable propio, oculto desde el cursor (el 2D se dibuja encima del 3D) salvo con keep_source; cambiarlo después no altera la malla. Gira la cámara para ver la profundidad. Un origen de otra escena, 3D, imagen, video o Lottie, una depth o tolerance no positiva o un bevel negativo o de al menos la mitad de depth lanzan ValueError; un contorno sin área avisa y deja la malla vacía.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
source
Drawable
obligatorio
Drawable 2D cuyo relleno se extruye: forma, texto, ecuación, grupo o SVG.
depth
float
0.2
Profundidad de la malla, centrada en z = 0.
bevel
float
0.0
Ancho del chaflán a 45° en ambas tapas; menor que la mitad de depth.
material
Material3D | None
None
Material; sin él, mate del color de relleno del origen.
tolerance
float
0.01
Tolerancia al aplanar las curvas, en unidades de escena; menor es más suave y pesado.
keep_source
bool
False
Mantiene visible el drawable de origen.
Devuelve: Primitive3D — Malla cerrada con tapas, paredes y chaflán.
Ejemplo
from gaanim import*
scene =Scene(frame=(16, 9))
word = scene.geometry.extrude(scene.text("HOLA", size=1.6), depth=0.4, bevel=0.04,
material=Material3D.metal(GOLD))
star = scene.geometry.extrude(scene.geometry.star(5, 1.2, 0.5).fill(BLUE).move_to(4.5, 0), depth=0.3)
scene.camera.perspective(fov_y=0.785)
scene.camera.look_at(eye=(2, 3, 12), target=(1, 0, 0))
scene.play([word.animate.create(), star.animate.create()])
scene.play([scene.camera.animate.orbit(delta_yaw=0.6, delta_pitch=0.0).duration(2)])
scene.render()
Estela 3D de la posición de un objeto en el mundo. Funciona con Updater y add_updater_fn; el muestreo empieza donde se declara. Revélala con fade_in o create. dissipating_time hace caducar la cola, max_points limita la memoria y min_distance (0.1 por defecto) filtra muestras casi iguales. Admite los mapas "inferno", "viridis" y "plasma".