Mecánica
Dibujo técnico y mecanismos con scene.mechanics: cotas, barras, muelles, fuerzas, apoyos y engranajes reactivos
scene.mechanics dibuja la notación de la mecánica y el dibujo técnico: cotas, ángulos, barras, muelles, vectores de fuerza, apoyos y engranajes. Casi todo es reactivo: los extremos aceptan objetos, AnchorPoint y PointRef (ver Geometría), y la geometría se regenera en el mismo fotograma cuando se mueven. Las magnitudes aceptan números o valores reactivos (Parameter, Variable, Computed).
from gaanim import Anchor, BLACK, Direction, WHITE, Scene
scene = Scene(frame=(16, 9), background=WHITE)
wall = scene.mechanics.fixed_support((-4, 0), direction=Direction.RIGHT, size=1.2, ground_length=2.4)
block = scene.geometry.rect(2, 1.6).fill("#cbd5e1").stroke(BLACK, 0.04).move_to(0, 0)
spring = scene.mechanics.spring_between((-4, 0), block.anchor_point(Anchor.LEFT), coils=7, amplitude=0.3).no_fill().stroke(BLACK, 0.05)
gap = scene.mechanics.dimension_between((-4, -0.8), block.anchor_point(Anchor.BOTTOM_LEFT), 0.6, side="below", show_value=True, unit="m", color=BLACK)
scene.play([spring.animate.fade_in(), gap.animate.fade_in()], duration=0.3)
scene.play([block.animate.shift_by(2, 0).duration(1.2)])
scene.render()
Los textos de las anotaciones (etiqueta, valor y unidad) miden 0.48 unidades por defecto, legibles en 1080p; font_size lo cambia. color pinta la geometría y la anotación completa, incluido el número cuando cambia o al hacer seek.
Cotas y ángulos
Medidas técnicas estáticas o que siguen a sus extremos.
(x1, y1) y (x2, y2), desplazada offset en perpendicular, con líneas de extensión de 0.02 unidades y las puntas de double_arrow. Para una cota que siga a objetos móviles, usa dimension_between.dim = scene.mechanics.dimension(-3, 0, 3, 0, 0.6).fill(WHITE)fade_in, create) espera oculta hasta que esta empieza. value solo controla el número: cambiarlo nunca cambia la longitud. Tipos, métricas, estilos u orientaciones inválidos lanzan TypeError o ValueError; un resultado reactivo no finito muestra el marcador de valor inválido.| Nombre | Tipo | Predeterminado | Descripción |
|---|---|---|---|
from_ / to | Endpoint | obligatorio | Extremos medidos. |
offset | float | obligatorio | Desplazamiento perpendicular. Sin side, positivo es a la izquierda de from_ → to; con side, solo cuenta su magnitud. |
side | str | None | None | left, right, above o below: mantiene la cota en ese lado de la escena aunque los extremos se crucen. |
label | str | None | None | Texto simbólico o matemática en línea. |
show_value | bool | False | Muestra la distancia actual multiplicada por scale. |
value | float | Parameter | Variable | Computed | None | None | Lectura numérica propia; implica show_value y sustituye la distancia medida y scale. |
format / unit / scale | str / str | None / float | ".2f" / None / 1 | Formato, unidad y factor de escena a unidades mostradas. |
label_gap / label_orientation | float / str | 0.1 / "upright" | Separación entre la línea y el borde más cercano de la anotación, y orientación upright o aligned (nunca boca abajo). |
line_width | float | 0.03 | Ancho de la línea; las puntas miden seis anchos, limitadas en cotas cortas. |
extension_style / dash_length / gap_length | str / float / float | "solid" / 0.12 / 0.08 | Líneas de extensión solid o dashed. |
font / weight / label_style | str / int / TextStyle | None | None | Tipografía de la anotación; font, weight y font_size sustituyen a label_style. |
from gaanim import Anchor, BLACK, WHITE, Scene
scene = Scene(frame=(16, 9), background=WHITE)
frame = scene.geometry.rect(4.5, 2).move_to(0, 0)
physical_width = scene.viz.parameter(2.5)
dim = scene.mechanics.dimension_between(
frame.anchor_point(Anchor.TOP_LEFT),
frame.anchor_point(Anchor.TOP_RIGHT),
0.6, label="$W_f$", value=physical_width, unit="m", color=BLACK,
extension_style="dashed", line_width=0.03, dash_length=0.12, gap_length=0.08,
)
height = scene.mechanics.dimension_between(
frame.anchor_point(Anchor.TOP_RIGHT),
frame.anchor_point(Anchor.BOTTOM_RIGHT),
0.6, side="right", show_value=True, unit="m", color=BLACK,
font="DejaVu Sans Mono", weight=700,
)
scene.play([
dim.animate.fade_in().duration(0.3),
height.animate.fade_in().duration(0.3),
physical_width.animate.set(4.0).duration(0.9),
])
scene.render()
dimension_between. Sus partes son Drawable que se estilizan por separado; label, number y unit valen None si no se pidieron.dim = scene.mechanics.dimension_between((-2, 0), (2, 0), 0.5, label="$L$", show_value=True)
dim.extensions.opacity(0.4)
dim.number.fill(GOLD)| Nombre | Tipo | Predeterminado | Descripción |
|---|---|---|---|
vertex | Endpoint | obligatorio | Vértice del ángulo. |
from_ / to | Direction | Endpoint | obligatorio | Rayos como direcciones fijas o extremos. |
radius | float | 0.64 | Radio del arco. |
sweep | str | "minor" | minor, major, cw o ccw. |
arrowheads | str | "both" | none, start, end o both. |
unit | str | "deg" | deg o rad para el valor mostrado. |
show_extensions | bool | True | Dibuja los rayos. |
from gaanim import Direction, GOLD, Scene
scene = Scene(frame=(16, 9))
bob = scene.geometry.dot(0.125).move_to(1, -1.125)
theta = scene.mechanics.angle_between((0,0), Direction.DOWN, bob, label="$theta$", show_value=True, color=GOLD)
scene.play([theta.animate.fade_in(), bob.animate.shift_by(1.125, 0.44).duration(1.2)])
scene.render()
angle_between, con partes que se estilizan por separado.Barras y muelles
Elementos que unen dos extremos y cambian de longitud con ellos.
width en unidades de escena. Dibuja las articulaciones aparte si las necesitas.from gaanim import Anchor, BLACK, Scene
scene = Scene(frame=(16, 9))
body = scene.geometry.rect(1.875, 0.875).move_to(0.5, -0.25)
bar = scene.mechanics.bar_between((-1.875, 1.25), body.anchor_point(Anchor.TOP_LEFT), width=0.11).stroke(BLACK, 0.11)
scene.play([bar.animate.fade_in(), body.animate.shift_by(1, 0).duration(1.0)])
scene.render()
ValueError.| Nombre | Tipo | Predeterminado | Descripción |
|---|---|---|---|
from_ / to | Endpoint | obligatorio | Extremos. |
coils | int | 8 | Número de vueltas. |
amplitude | float | 0.12 | Radio de la hélice, perpendicular al eje. |
crossing | float | 0 | De 0 a 1: pliega cada vuelta para crear cruces en forma de «e». |
start_straight / end_straight | float | 0.12 / 0.12 | Tramos rectos no negativos en cada extremo; se acortan si los extremos están muy cerca. |
from gaanim import GOLD, WHITE, Scene
scene = Scene(frame=(16, 9), background="#0f172a")
mass = scene.geometry.dot(0.125).fill(GOLD).move_to(0.875, 0)
spring = scene.mechanics.spring_between(( -0.875, 0), mass, coils=6, amplitude=0.175, crossing=1.0, start_straight=0.225, end_straight=0.225).no_fill().stroke(WHITE, 0.04)
scene.play([spring.animate.fade_in().duration(0.3), mass.animate.shift_by(0.5, 0).duration(1.0)])
scene.render()
Fuerzas y momentos
Vectores con punta sólida y lectura opcional, en unidades físicas.
show_value, format, unit, scale).force = scene.mechanics.vector_between((-1.25, 0), (0.875, 0.56), label="$F$", color=GOLD)| Nombre | Tipo | Predeterminado | Descripción |
|---|---|---|---|
origin | Endpoint | obligatorio | Punto de aplicación. |
magnitude | float | fuente reactiva | obligatorio | Magnitud física. |
direction | float | fuente reactiva | 0.0 | Dirección en radianes. |
visual_scale | float | 1.0 | Unidades de escena por unidad física; positiva. |
unit | str | "N" | Unidad de la lectura. |
from gaanim import GREEN, Scene
scene = Scene(frame=(16, 9))
body = scene.geometry.circle(0.3)
magnitude = scene.viz.parameter(30)
force = scene.mechanics.force_at(
body.anchor_point(), magnitude, direction=0.5, visual_scale=0.025,
label="$F$", show_value=True, unit="N", color=GREEN,
)
scene.play([body.animate.fade_in(), force.animate.fade_in(), magnitude.animate.set(80).duration(1.5)])
scene.render()
Parameter.add_updater_fn para simulaciones: las simulaciones de paso fijo se reconstruyen antes, así que una fuerza derivada del cuerpo simulado ve el estado del mismo fotograma.body = scene.geometry.circle(0.3)
fx = scene.viz.parameter(20)
weight = scene.mechanics.force_from_components(body, fx, -40, visual_scale=0.02, label="$W$")vector_between, force_at y force_from_components, con partes que se estilizan por separado.direction es "cw" o "ccw".moment = scene.mechanics.moment_about((1.5, -0.5), radius=0.525, label="$M$")x_direction y etiquetas opcionales.from gaanim import Direction, GOLD, Scene
scene = Scene(frame=(16, 9))
force = scene.mechanics.vector_between((-1.25, 0), (0.875, 0.56), label="$F$", color=GOLD)
moment = scene.mechanics.moment_about((1.5, -0.5), radius=0.525, label="$M$")
frame = scene.mechanics.coordinate_frame_at((0, -1), Direction.RIGHT, labels=("$e_1$", "$e_2$"))
scene.play([force.animate.fade_in(), moment.animate.fade_in(), frame.animate.fade_in()])
scene.render()
Parameter o Variable de 0 a 1.t = scene.viz.parameter(0.1)
track = scene.geometry.polyline([(x / 10, 0.5 * math.sin(x / 10)) for x in range(-40, 41)]).no_fill()
contact = scene.mechanics.contact_on_curve(track, t, tangent_length=1.0, normal_length=0.6)
scene.play([contact.animate.fade_in(), t.animate.set(0.9).duration(1.5)])Apoyos y articulaciones
Símbolos de apoyo con base rayada y juntas, que siguen a su punto de conexión.
fixed_support, pin_support, roller_support y guided_support fijan kind.| Nombre | Tipo | Predeterminado | Descripción |
|---|---|---|---|
point | Endpoint | obligatorio | Punto de conexión. |
kind | str | "pin" | fixed, pin, roller, simple, guided, prismatic, cable o spring. |
direction | Direction | None | None | De la base hacia la conexión: UP pone el suelo debajo y DOWN crea un apoyo de techo. |
size / ground_length | float | 0.48 / 0.70 | Medidas en unidades de escena. |
anchor = scene.mechanics.support_at((0, 2), kind="cable", direction=Direction.DOWN)from gaanim import Direction, Scene
scene = Scene(frame=(16, 9))
pin = scene.mechanics.pin_support((-1.25, 0), direction=Direction.UP)
roller = scene.mechanics.roller_support((1.25, 0), direction=Direction.UP)
scene.play([pin.animate.fade_in(), roller.animate.fade_in()])
scene.render()
wall = scene.mechanics.fixed_support((-4, 0), direction=Direction.RIGHT)direction.base = scene.mechanics.pin_support((0, 0))
base.hatching.opacity(0.5)kind="revolute") o prismática ("prismatic", alineada con axis) que sigue a su punto.hinge = scene.mechanics.joint_at((0, 0))
slider = scene.mechanics.joint_at((2, 0), kind="prismatic", axis=Direction.RIGHT)Transmisiones
Engranajes, cremalleras y levas editoriales. Son relaciones visuales, no un solver cinemático ni de contacto; los dientes son esquemáticos.
radius con teeth dientes y agujero central. Acóplalos con Drawable.bind_rotation_from(source, ratio=..., phase=...).from gaanim import Direction, Scene
scene = Scene(frame=(16, 9))
driver = scene.mechanics.gear(0.69, 20).move_to(-0.69, 0.25)
driven = scene.mechanics.gear(0.41, 12).move_to(0.41, 0.25).bind_rotation_from(driver, ratio=-5/3)
rack = scene.mechanics.rack(2.75, 18).move_to(0, -0.81).bind_translation_from_rotation(
driver, axis=Direction.RIGHT, scale=0.69,
)
scene.play([driver.animate.fade_in(), driven.animate.fade_in(), rack.animate.fade_in(), driver.animate.rotate_by(2.0)])
scene.render()
length con teeth dientes equiespaciados. Muévela con Drawable.bind_translation_from_rotation.(ángulo_en_radianes, radio).samples = [(2 * math.pi * i / 64, 1 + 0.3 * math.cos(2 * math.pi * i / 64)) for i in range(64)]
cam = scene.mechanics.cam_profile(samples)
scene.play([cam.animate.rotate_by(math.tau).duration(2)])