Guía/Referencia avanzada/QF DSL v1

QF DSL v1

Describe construcciones exactas y reproducibles. No es necesario para usar QF, pero es útil para secuencias largas y material reutilizable. Un error de escritura rechaza el script entero indicando la línea: nunca dibuja algo distinto de lo que pediste.

El prefijo /qf-dsl sirve a tres gramáticas. Cuál se usa no depende de lo que escribas, sino del lienzo en el que estés: en geometría se parsea la construcción sintética; en el plano cartesiano, las gráficas; en física, la escena mecánica. Varios constructores se llaman igual en más de una —point, segment, circle, intersection, angle, label— a propósito.

Geometría

Construcción sintética en un lienzo sin ejes: puntos, segmentos, polígonos y circunferencias, con sus puntos y figuras derivadas vivos — un cuadrado apoyado en un lado se rehace cuando ese lado se mueve.

Ejemplo completo
/qf-dsl
T = triangle(A, B, C)
H = foot(B, A, C)
altura = segment(B, H)
M = midpoint(A, C)
mediana = segment(B, M)
G = centroid(T)

Objetos básicos

point(x, y)
Punto libre
segment(A, B)
Segmento
line(A, B)
Recta infinita por dos puntos: llega hasta el borde de la figura y no la agranda (segment(A, B) es el tramo)
circle((0, 0), 3)
Circunferencia de centro fijo
triangle(A, B, C)
Triángulo. Con letras nuevas lo construye el motor; sobre puntos ya definidos usa sus coordenadas, y sobre puntos construidos (on_side, cruces, pies) se mueve con ellos; con angles= o right_at= marca sus ángulos
quadrilateral(A, B, C, D)
Cuadrilátero cualquiera; nombra sus lados AB, BC, CD, DA
polygon(A, B, C, D, E)
Polígono de n vértices
polygon((0, 0), (4, 0), (1, 3))
Polígono por coordenadas, sin letras ni puntos

Puntos derivados

midpoint(A, B)
Punto medio
foot(P, A, B)
Pie perpendicular
intersection(r, s)
Cruce de segmentos, rectas, circunferencias, arcos o rectas construidas; un segmento cuenta solo en su tramo
intersection(k, r, lejos_de=M)
Elige uno de dos cruces sin que salte al arrastrar: cerca_de=P, lejos_de=P, sobre=s, arriba, abajo, izquierda o derecha
on_circle(c, 55)
Punto atado a la circunferencia
on_segment(s, 0.35)
Atado al segmento: se desliza por él
on_side(AB, 0.35)
Atado a un lado del polígono
center(Q)
Centro de una figura: el promedio de sus vértices (en un paralelogramo, el cruce de sus diagonales)
center(c)
Centro de una circunferencia, con nombre y vivo: se mueve con ella

Compás (vivo)

circle(O, 2)
Centro en un punto con nombre (libre, medio, cruce, vértice): la circunferencia lo sigue
circle(O, P)
Con centro en O y que pasa por P: el radio cambia al mover O o P
circle(C, AB)
Copia la longitud del segmento o lado AB
circle(O, P, dashed)
Punteada: circunferencia auxiliar de la construcción
circle(A, 3, arcs)
Marcas de compás: solo un arco corto junto a cada cruce que la usa
intersection(k1, k2)
Cruce de dos circunferencias; la otra rama con second. La rama no salta al arrastrar
arc(O, A, B)
Arco de centro O desde A, antihorario, hasta la dirección de B; vivo, acepta dashed y se interseca

Relaciones

parallel(A, B, P)
Paralela
perpendicular(A, B, P)
Perpendicular, con su cuadradito de 90° donde corta a AB
perpendicular_bisector(A, B)
Mediatriz
tangent(c, P)
Tangente: de P al punto de tangencia. Con P sobre la circunferencia (on_circle), la tangente EN P, perpendicular al radio; con P adentro no hay tangente
tangent(c, P, extend=2)
Tangente prolongada 2 unidades más allá del punto de tangencia (en P, 2 unidades a cada lado)
secant(c, P)
Secante

Circunferencias tangentes

tangente_a(k0, k1, k2)
Circunferencia tangente a tres objetos: circunferencias, rectas o segmentos, lados AB o puntos; sin más, la de menor radio (second, la siguiente). Viva: sigue la misma solución sin saltar. Alias tangent_circle
tangente_a(k0, k1, k2, tipo=(int, ext, ext), derecha)
tipo= por objeto: int (la nueva queda dentro), ext (por fuera), contiene o _ (no importa); después, un selector de intersection. Sin solución no se dibuja y el motor lo dice
tangencia(k1, k2)
Punto donde se tocan dos circunferencias, o una circunferencia y una recta; vivo. Si no son tangentes, no compila. Alias tangency
apoloniano(k0, k1, k2, k3, profundidad=3)
Relleno apoloniano de cuatro tangentes dos a dos: profundidad= (generaciones) o radio_min=; tope de 500. Piezas G_gen1_1…; G y G.gen2 son grupos en /qf-style; animate va por generación. Alias apollonian

Figuras y marcas

square_on(AB, external)
Cuadrado sobre el lado de un polígono, hacia afuera
square_on(A, B, left)
Sobre dos puntos sueltos: izquierda o derecha de A→B
region(A, E, P, H)
Sombrea la región sobre puntos que ya existen (3 o más)
region(A, E, P, fill=ambar)
La misma región con un color elegido
region((0, 0), (4, 0), (2, 3))
Vértices por coordenadas (solos o con puntos); queda fija
angle(A, B, C)
Marca de ángulo viva en el vértice B; reemplaza la que el triángulo ya ponga ahí
angle(A, B, C, alfa)
La misma marca con un símbolo en vez de la medida: alfa dibuja α (también beta, theta, Delta → Δ…, o la letra escrita). Entre comillas, el texto tal cual, también una fórmula: "$\alpha_1$"

Marcas

angle(r, s)
Ángulo entre dos segmentos o rectas por dos puntos: en su extremo común (angle(AB, AC) es angle(B, A, C)) o en su cruce, hacia el segundo punto de cada una; vivo
marca(AC, BC, 1)
Rayitas de igualdad (1, 2 o 3) sobre cada tramo: un segmento, un lado (AB) o dos puntos (BD); vivas. Si los tramos no miden lo mismo, las notas lo dicen. Alias mark
paralela_marca(AB, DC)
Puntas de flecha de paralelismo, todas en el sentido del primer tramo; 2 o 3 al final para otro par. Si no son paralelos, las notas lo dicen. Alias parallel_mark
angle(A, P, B, alfa, radio=1.5)
radio= multiplica el radio del arco (de 0,5 a 4) y manda sobre el automático. Sin él, los ángulos que comparten vértice se anidan: el que contiene a otros va afuera (+14 px por nivel, o lo que pida el rótulo de adentro), los adyacentes comparten radio y uno de menos de 25° se agranda
angle(A, O, B, rotulo=dentro)
El rótulo entre el vértice y el arco; por defecto, fuera
marca(b1, b2, 2)
Igualdad de ángulos con nombre: 1, 2 o 3 arcos concéntricos en cada uno; se combina con el símbolo del ángulo. Si no miden lo mismo, las notas lo dicen
marca(b1, b2, 2, rayitas)
La misma igualdad con rayitas que cruzan el arco

Textos y medidas vivas

note((0, 3), "AB = 4")
Texto suelto centrado en (x, y)
note((0, 4), "Teorema", tamaño=20, peso=negrita, giro=15)
Tamaño en px, negrita y giro en grados (antihorario)
label(s, "MO = {{length(s)}}")
Rótulo de un punto, segmento o recta (alias rótulo). En {{…}} van medidas vivas: dist(P, Q), length(s), angle(A, B, C), area(T), perimeter(T); se recalculan al mover la figura
label(P1, "$P_1$")
Subíndices explícitos: el punto P1 se rotula «P1»; P₁ se pide con una fórmula entre $…$ o con el carácter, label(P1, "P₁")

Centros y círculos

centroid(T)
Baricentro
orthocenter(T)
Ortocentro
incenter(T)
Incentro
circumcenter(T)
Circuncentro
incircle(T)
Círculo inscrito
circumcircle(T)
Círculo circunscrito
circumcircle(A, F, E)
Circunferencia por tres puntos cualesquiera, también construidos; viva

Elipses

ellipse(focos=(B, C), pasa_por=A)
Elipse de focos B y C que pasa por A; viva: sigue a los tres. Con a=5 en lugar de pasa_por=, por su semieje mayor. Se corta con rectas, circunferencias y otras elipses. Alias elipse
ellipse((0, 0), a=4, b=2, giro=30)
Por su centro (un par o un punto con nombre) y sus semiejes; giro en grados, antihorario; dashed la dibuja punteada. El relleno va en /qf-style, como el de una circunferencia: k { relleno: translúcido amarillo }

Homotecia

homothety(A, O, 2)
La imagen viva desde el centro O con razón 2 (negativa: del otro lado de O) de un punto, segmento, recta, circunferencia, elipse o figura. Alias homotecia
homothety(h1, V, T1, T2)
Con la razón que lleva T1 a T2: con V en el punto de fuga y T2 sobre la fila, la imagen escala sola al mover T2
homothety(T, O, -1)
Los vértices de la imagen de una figura, con prima (A′B′C′), se llaman T2_A, T2_B…; los extremos de la de un segmento s2 no se ven y se llaman s2_A y s2_B

Plano cartesiano

Curvas, cónicas, regiones y sumas de Riemann. Aquí los ejes SON el sistema de coordenadas, así que no hay escala en píxeles que declarar.

Ejemplo completo
/qf-dsl
window x: [-3, 4], y: [-2, 8]
f = plot(x^2)
g = plot(2*x + 1)
P = intersection(f, g)
S = area(f, g, [-1:3], donde g > f)

Lienzo y parámetros

window x: [-4, 4], y: [-1, 10]
Ventana de los ejes
frame x: [0.05, 0.95], y: [0, 0.55]
En qué parte del lienzo se dibuja el plano, en fracciones de 0 a 1 (la y, desde arriba). Sin ella el plano se centra ocupando todo; dentro de un panel, coloca ese plano solo
axis x: "t (s)", y: "v"
Etiquetas
grid on · title "…"
Rejilla y título
axes off
Sin ejes ni sus números (también axis off); la rejilla va aparte: grid off
param a = 2 in [-5, 5]
Deslizador
panel "Velocidad"
Plano debajo del anterior con el mismo eje x (hasta 3); dentro, window y, axis y, grid y sus objetos

Curvas y cónicas

plot(x^2 + 2, [-1:3])
Curva y = f(x), con dominio
plot(f(x - 2) + 1)
Una curva y = f(x) del script dentro de otra expresión: traslada, refleja o compone (se copia su expresión, no su dominio)
curve(cos(t), sin(t), [0:2*pi])
Paramétrica
polar(2*cos(3*theta), [0:2*pi])
Polar
plot(sen(x), punteado, color=gris)
Trazo discontinuo («puntos»: de puntos; «punto-raya»: de raya y punto) y color; también en curve, polar, cónicas y objetos lineales
circle((0, 0), 3)
Circunferencia
ellipse((1, 0), a=4, b=2)
Elipse
ellipse(focos=(B, C), pasa_por=A)
Elipse por sus focos y un punto (o con a=, su semieje mayor); sigue a sus puntos
hyperbola((0,0), a=2, b=1)
Hipérbola
parabola((0,-1), p=2, abajo)
Parábola

Puntos y objetos lineales

point(2, 3)
Punto por coordenadas
point(4*cos(30°), sqrt(3))
Coordenadas con expresiones y grados; con parámetros, el punto sigue al deslizador
segment(A, B) · vector(A, B) · ray(A, B) · line(A, B)
Segmento, vector, rayo y recta por dos puntos
polygon(A, B, C, fill=azul)
Polígono por sus vértices

Puntos derivados

intersection(f, g)
TODAS las intersecciones, por x ascendente
P[0] · P[1]
Un miembro de la colección; A = P[0] lo rotula «A»
intersection(f, g, segunda)
Atajo de P[1]
intersection(f, g, cerca_de=M)
Un solo cruce, por lo que es: cerca_de= y lejos_de= (un punto o un par), sobre= (otra curva), arriba, abajo, izquierda o derecha. Sigue siendo el mismo al mover un deslizador; si ninguno la cumple, no se dibuja y se avisa
roots(f) · root(f, branch=0)
Cortes con el eje x; uno solo con root
at(f, 0)
El punto (0, f(0))
on_curve(c, pi/2)
Sobre una paramétrica en su t, una polar en su θ o y = f(x) en su x; con un parámetro recorre la curva. at() acepta lo mismo
intersection(r1, r2)
Entre rectas, segmentos y semirrectas: UN punto, si se cortan
on_circle(k, 55)
Sobre la cónica, en grados

Regiones y texto

area(f, [0:3])
Entre la curva y el eje x
area(f, g, [0:3], donde g > f)
Sombreado condicional
region(arriba f)
Inecuación y ≥ f(x)
region(abajo f, abajo g, x in [0:6])
Bajo las dos curvas a la vez (su mínimo); con varias «arriba», sobre su máximo
riemann(f, [0:3], n=8)
Suma de Riemann
riemann(f, [0:3], n=k)
Ligada a un parámetro entero (step 1, entre 1 y 200): los rectángulos siguen a su deslizador
inequality(2*x + 3*y <= 6)
Semiplano con su recta frontera (discontinua si es estricta); varias del mismo plano se pintan como una región, su intersección
sistema(x + y <= 8, x >= 0, y >= 0)
La región que cumple TODAS (programación lineal), con la frontera de cada desigualdad; cada sistema es su propia región. Alias system
vertices(F)
Las esquinas de la región de un sistema: V[0], V[1]… en sentido antihorario desde la de más abajo; con coords, rotuladas con sus coordenadas
note((1, 5), "…") · label(A, "P")
Texto suelto y rótulo (de un punto, una curva y = f(x), un segmento, una recta o un polígono; en una cónica o una región todavía no se dibuja, y se avisa)
note((1, 4), "dx = {{1/n}}")
Entre llaves dobles, una expresión con los parámetros: imprime su valor y cambia al mover el deslizador
note((0, 5), "AB = {{dist(A, B)}}")
dist(P, Q) en las llaves: la distancia viva entre dos puntos
note((0, 4), "{{angle(A, B, C)}}°")
Más medidas vivas: angle(A, B, C) en grados (el ° va afuera), length(s) de un segmento o vector, area(T) y perimeter(T) de un polígono, o area(A, B, C) con los puntos de su contorno
label(s, "s = {{length(s)}}")
Las medidas también van en un rótulo
label(P1, "$P_1$")
Subíndices y letras griegas, explícitos: con una fórmula entre $…$ ("$\alpha$") o con el carácter, label(P1, "P₁"). Sin pedirlo, P1 se rotula «P1»

Física (mecánica)

Cuerpos, soportes y conectores unidos por relaciones: no se declaran coordenadas — la posición de cada cuerpo sale del grafo (on, stack, pin, attach, route) y las fuerzas de un diagrama de cuerpo libre se deducen de él, no se dibujan a mano. Las magnitudes sí se pueden declarar en newtons cuando el enunciado las da, y el largo de cada flecha las respeta.

Ejemplo completo
/qf-dsl
rampa = ramp(30)
A = block(4kg, "m₁")
on(A, rampa, at=0.55, mu=0.3)
fbd(A)
axes(A, decompose=on)
angle(rampa, "θ")

Cuerpos

block(4kg, "m₁")
Bloque; masa y etiqueta opcionales
sphere(2kg, r=20)
Esfera, con radio en píxeles
sphere(2kg, color=rojo)
Color del cuerpo: rojo, verde, azul, amarillo, naranja, morado, rosa, celeste, gris, negro, blanco, marron (o en inglés), o hex sin #: color=dc2626. Vale también en block()
sphere(2kg, v0=5, angle=30)
Esfera LANZADA: v0 en m/s y angle en grados desde +x (y hacia arriba). Sin apoyo ni cuerda sale como proyectil; sobre un apoyo, v0 tiene que ir a lo largo de él. Vale también en block()
beam(length=220)
Barra rígida (alias: bar): el peso en su centro; cuerdas y fuerzas hacen momento donde se atan
pulley(24)
Polea, con su radio

Soportes

floor()
Piso horizontal
table()
Mesa: tablero y dos patas hasta el piso; length= (500) y height= (160, del tablero al piso). Para la física es un apoyo como floor()
anchor(mesa, at=1)
Soporte en una esquina de la mesa (at=0, la izquierda), en el canto. Con pin(P.axle, ancla), la polea queda afuera: la cuerda del apoyado llega horizontal arriba del disco y lo que cuelga baja vertical, afuera de la mesa. pin(P.axle, mesa) es la esquina derecha
ramp(30)
Rampa, con su ángulo en grados: entre 0 y 90, sin incluirlos
surface(length=600)
Superficie horizontal genérica; para inclinarla, ramp(θ)
surface(path=[(180,200), (360,380), arc(480,440, 600,380), (780,200)])
Superficie POR TRAMOS: rectas y arcos en un mismo apoyo (un valle con fondo curvo, un tobogán). Cada (x,y) es un tramo recto; arc(porX,porY, hastaX,hastaY) es un arco de circunferencia que pasa por el primer punto y termina en el segundo — los tres puntos fijan radio y sentido, no hay que declararlos. Al reproducir, un cuerpo apoyado con on() corre por los arcos como partícula guiada sin rozamiento: la normal lleva el término centrípeto y, donde se anula, el cuerpo se despega y sigue como proyectil
surface(path=[(100,150), (400,400), arc(500,300, 400,200), arc(300,300, 400,400), (800,400)])
RIZO: rampa que baja al fondo de un círculo cerrado por DOS arcos (el que sube y el que baja) y sale por un piso. Con on(bola, pista, at=0) en lo alto de la rampa, al reproducir la esfera completa la vuelta si parte de h ≥ 2.5·R (con R el radio del rizo), se despega antes de la cima si no llega, y sube y vuelve si no pasa de la horizontal; el ⓘ predice cuál de los tres. El mismo perfil recorrido por FUERA (una loma) se despega después de la cima
ceiling()
Techo
wall(left)
Pared izquierda o derecha
anchor(techo, at=0.3)
Punto de anclaje sobre techo, superficie o barra

Conectores y fuerzas

rope()
Cuerda. Compacta: rope(from=A.top, via=[over(P)], to=B.top)
rope(orientation=vertical)
Eje declarado para el motor: vertical, horizontal, along_surface (sigue la tangente del apoyo) o free. El cálculo cree la declaración, no el trazo. Sin declararla, usa la dirección del trazo en cada extremo y el ⓘ de la reproducción dice cuál tomó
spring(k=200, rest=120)
Resorte. Como la cuerda, ata sus dos extremos (si no, se rechaza)
spring(k=200, orientation=horizontal)
Eje del resorte, horizontal o vertical: su extremo en una pared, un techo o un piso va donde ese eje lo toca. Sin declararlo, horizontal contra una pared (a la altura del bloque) y vertical colgado; free lo deja en el centro del soporte. Un eje que no llega a su soporte se rechaza
junction()
Nudo donde concurren varias cuerdas: sin masa, sus tensiones se equilibran entre sí
rope(from=V.rightEnd, to=pared, direction=150)
Dirección de la cuerda, de from= hacia to=, en grados como en force(): el amarre va donde esa recta toca el soporte (pared, techo o superficie) y el cálculo usa ese ángulo. «A 30° con el techo» desde un nudo J: direction=150. Sin via= ni anchor()
force(50N, direction=down)
Fuerza externa: right/up/left/down o un ángulo
force(direction=30)
Fuerza de magnitud DESCONOCIDA: la despeja equilibrium() (la caja que sube a velocidad constante); sin él se dibuja sin valor y se avisa

Relaciones (la posición sale del grafo)

on(A, rampa, at=0.55, mu=0.3)
Apoyo sobre una superficie; mu= añade roce: estático si alcanza para sostener el cuerpo (vale −Fₜ, y si es 0 no se dibuja), si no cinético (µ·N) contra el movimiento. Sale de la cara de contacto
on(A, rampa, mu=0.3, motion=up)
Movimiento declarado: up o down (cuesta arriba o abajo), left o right. El roce es cinético y se opone a él; con una fuerza desconocida, equilibrium() da la de velocidad constante
stack(B, A)
B apilado sobre A
contact(b, mesa, at=0.4)
Contacto genérico cuerpo–superficie
pin(P1.axle, techo)
Unión persistente del eje de una polea
pin(V, pared, at=0)
Pasador de una barra en un soporte fijo (pared, techo, piso o anchor()); at= entre sus extremos (0 el izquierdo, 1 el derecho; en una pared, el que la toca). La barra gira alrededor de él y el soporte la sostiene con Rx y Ry: las despeja equilibrium() y el DCL las dibuja en el pasador
attach(c1.a, A.top)
Terminal de cuerda o resorte a un puerto del cuerpo; los soportes van SIN puerto: attach(c1.b, techo)
route(c1, [over(P1), over(P2)])
La cuerda rodea las poleas, en orden. Una polea SIN pin que la cuerda rodea entre una fija y el techo (o entre dos puntos fijos) cuelga en el seno: es MÓVIL, con sus dos tramos verticales; su carga va con attach(c2.a, M.axle), cuya cuerda tira con el doble de la tensión
apply(F, viga.rightEnd)
Aplica la fuerza sobre un punto del cuerpo

Vistas y layout

fbd(A)
Diagrama de cuerpo libre: las fuerzas se deducen del grafo
fbd(J)
DCL de un nudo: solo las tensiones de sus cuerdas, a escala entre sí (un nudo no tiene peso)
forces(A, B)
DCL de varios cuerpos a la vez
fbd(A, weight=20N, normal=10N)
Magnitudes declaradas en newtons: el largo de cada flecha las respeta (weight, normal, friction, tension)
fbd(A, weight="Fg", weight_t="Fg·senθ")
Rótulo por fuerza y por componente de la descomposición (_t tangencial, _n normal)
fbd_scale(1.5)
Calibrado global: multiplica el largo de todas las flechas del DCL
equilibrium()
¿El sistema está en equilibrio? El motor resuelve y responde con las tensiones, normales y roces (estático o cinético, con su sentido), la reacción de cada pasador (Rx, Ry, con su sentido) y despeja la fuerza sin magnitud; el DCL los dibuja a escala, vivos al editar. Dice los supuestos que usó
datos()
Los valores EN la figura: una ficha arriba de ella con el veredicto, la aceleración, las tensiones, las normales, los roces y las reacciones, que el motor rehace al editar (respeta «decimal: coma»). datos(A), la de un cuerpo. Si el motor no puede resolver la escena, lo dice. Alias info
note(A, "T = {{tension(c)}} N")
Texto junto a un objeto, del lado donde no tapa nada, y lo sigue; note((480, 60), "…") en un punto. Medidas vivas entre {{…}}: tension(c), normal(A), roce(A) (normal(A, piso) con dos apoyos), acel(A), reaccion(V) y reaccion(V, x) o (V, y); |1 fija los decimales. Sin respuesta del motor, «—». Alias nota
axes(A, decompose=on)
Ejes vivos anclados al cuerpo (rotan con él); decompose descompone los vectores cercanos en componentes (mg·senθ, mg·cosθ). Etiquetas: axes(A, "t", "n")
angle(rampa, "θ")
Arco de ángulo sobre la rampa, con símbolo o medida
label(A, "caja")
Rótulo del objeto
place(B, x=380, y=260, rot=15)
Solo cuerpos que ninguna relación ubica

Vistas de la reproducción (al pulsar ▶)

velocity(A)
Flecha de la velocidad del cuerpo mientras la escena corre, con su valor en m/s. Acepta varios: velocity(m1, m2)
accel(A)
Flecha de la aceleración, en m/s². En vuelo apunta siempre hacia abajo (vale g); sobre un apoyo recto va a lo largo de él
trail(A)
Rastro del camino recorrido, punteado. Se borra al volver al reposo con ↻

Escala y marco (unidades reales)

gravity(10)
La g de la escena en m/s² (sin declararla, 9.81): cambia el peso, el equilibrio y la reproducción. Va arriba, antes del primer objeto
scale(300px, 1m)
Cuántos píxeles mide una unidad (mm, cm, m, km); es la escala de toda la escena y va arriba, antes de la primera medida con unidad. Sin declararla rige 300 px = 1 m. Caso astronómico: scale(1px, 425km)
frame(x=100, y=400)
Origen del marco de referencia, en píxeles del lienzo (la cuadrícula lo muestra); rot= gira +x en grados antihorarios. Es el punto desde el que se miden los metros
place(B, x=1m, y=0.5m)
Ubicación en METROS desde el origen de frame(), con y hacia ARRIBA. Las dos coordenadas van en el mismo sistema (píxeles o metros)
floor(length=3m)
Toda longitud admite unidad: length=2m, r=10cm, rest=40cm, width=0.5m, y los puntos de path=[(0m,0m), (2m,1m)]

Macros direccionables

S = atwood(5kg, 3kg)
Máquina de Atwood; piezas: S.techo, S.polea, S.m1, S.m2, S.cuerda
W = incline(30, 4kg)
Plano inclinado con bloque; piezas: W.rampa, W.bloque

Óptica geométrica

L = lens(convergente, f=2)
Lente convergente o divergente de distancia focal f (positiva), sobre un eje horizontal; at= la corre sobre el eje (0 por defecto). Las medidas son unidades del banco óptico: sin unidad o todas en cm, m…; el banco se encuadra solo. Crece hasta cubrir los rayos que la tocan, más un 10 %. Alias lente
L2 = lens(convergente, f=2, at=4.5, abertura=5)
abertura= fija el alto total del elemento; un rayo que la toca por fuera no entra: no se dibuja y la respuesta lo avisa
E = mirror(cóncavo, f=3)
Espejo cóncavo, convexo o plano (sin f); refleja hacia la izquierda. El curvo se dibuja delgado —la recta del vértice con las puntas curvadas— y dobla la luz en ella, así que los rayos concurren en la imagen de la ecuación. Alias espejo
obj = object(L, at=-5, height=2)
La flecha-objeto: su posición sobre el eje y su altura (hacia arriba, positiva); sin elemento, object(at=-1.5, height=0.6), la luz entra por la izquierda. Una etiqueta entre comillas («"A"») nombra sus imágenes A′, A″…; sin ella, A. Alias objeto
rays(obj)
Los rayos notables, desviados por el motor; también rays(obj, paralelo, focal) para elegirlos. Alias rayos
img = image(obj)
La imagen final, que calcula el motor: real o virtual (punteada, con sus prolongaciones), derecha o invertida. Al arrastrar el objeto o un elemento, los rayos y la imagen se rehacen. Alias imagen
image(obj, L1)
Con varios elementos, la luz los recorre todos en su orden (un espejo la devuelve) y cada imagen es el objeto del siguiente, real o virtual: image(obj, L1) dibuja la intermedia que forma L1
cota(obj, L)
Cota viva bajo el eje: dₒ = 5. También cota(img, L) (dᵢ), cota(L.F, L) (f), cota(E.C, E) (R) o entre dos puntos cualesquiera del banco; cota(obj, altura) y cota(img, altura) dan la altura con signo (h, h′). Un tercer argumento entre comillas cambia el rótulo; con "" queda solo el número. Sigue a la construcción al arrastrar
note((2, -3), "m = {{aumento(obj)}}")
Texto en el banco (x sobre el eje, y hacia arriba). Entre {{…}}, medidas vivas: dist(P, Q), height(X), aumento(obj), f(L), do(obj) y di(obj) (con signo: + real); cambian al arrastrar y respetan «decimal: coma» y las unidades. Alias nota
datos(obj)
La ficha del objeto, arriba a la izquierda: f, dₒ, dᵢ, el aumento y si la imagen es real o virtual, derecha o invertida, mayor o menor; en un banco, el aumento total. En /qf-style, obj { color: rojo } pinta el objeto, sus rayos y su imagen, e img { color: … }, solo la imagen. Alias info

Refracción: medios y láser

P = prism(n=1.5)
Prisma de índice n (1 a 5) o material= de la lista (agua, acrilico, vidrio, flint, diamante); sin ellos, vidrio (1.5). Equilátero de lado 4, vértice arriba y centro en (0, 0): angle= (el del vértice), side=, at=(x, y) y rot= (grados, antihorario). Unidades del banco, y hacia arriba. Alias prisma
B = slab(n=1.5, width=6, height=2)
Lámina de caras paralelas (ancho × alto): el rayo sale paralelo y corrido. at= y rot=, como el prisma. Alias lamina
agua = medium(n=1.33, below=0)
Un medio que sigue más allá de la figura: todo lo que está bajo y = 0 (above=, sobre ella), sin borde y con su superficie. Alias medio
M = medium(n=1.5, [(0,0), (4,0), (0,3)])
Un polígono simple por sus vértices, en cualquier sentido de giro
S = semicircle(n=1.5, r=2)
Semicilindro con la cara plana abajo, centro en at= y rot= para girarlo; la cara curva es exacta. laser(S, incidence=45) entra por ella, sin desviarse, al centro de la cara plana: el ángulo crítico. Alias semicirculo
G = disc(material=agua, r=2)
Un disco: la gota o un cilindro. laser(G, incidence=59.4) llega horizontal y la toca a r·sen θ de su centro. Alias disco, gota
D = mirror(plano, at=(0, 3), angle=135)
Espejo plano suelto, para el láser: at=(x, y) lo saca del eje, angle= inclina la superficie (desde la horizontal; 90, vertical) y mira= elige la cara que refleja (izquierda; arriba si es horizontal). Mide 2,7 (abertura= lo cambia). No convive con un objeto del banco
l = laser(P, incidence=40)
El láser, al medio de la cara de P que mira a la luz (o al centro de un espejo plano), con su incidencia (desde la normal, de 0 a 90): el motor pone el origen y la dirección. from= dice de dónde llega (izquierda por defecto; derecha, arriba, abajo); si la cara mira al otro lado, la luz va por dentro del medio (del agua al aire)
laser(toward=(0, 0), incidence=30)
A un punto de la cara de un medio, con su incidencia
laser((-4, 1), angle=-10)
Desde un punto, con angle= (grados, antihorario desde +x) o toward=(x, y). El motor lo sigue por la escena: Snell en cada cara, reflexión total más allá del ángulo crítico y reflejos parciales más tenues (reflejos=no los apaga); cruza las lentes y rebota en los espejos, que crecen hasta cubrirlo. Si no toca nada, se avisa
l = laser(P, incidence=50, luz=blanca)
Luz blanca: siete colores que se separan en un medio que dispersa (el violeta se desvía más). lambda=650nm, un solo color con su índice
normals(l)
La normal y los ángulos, desde la normal, en cada cara (hasta seis). Alias normales. l { color: verde } pinta el haz
note((0, -2.5), "δ = {{desviacion(l)}}°")
Medidas vivas, en grados (el ° afuera): incidencia(l, k) y refraccion(l, k) en el impacto k (1 si se omite; en un espejo o en la reflexión total, el de reflexión), desviacion(l), critico(P) e indice(P). Se rehacen al mover el láser; un impacto que ya no existe se lee «—»
datos(l)
La ficha del láser: llegada y salida en cada impacto (hasta seis), la desviación total y el ángulo crítico de cada medio que cruza, vivos

Estilos (/qf-style)

El aspecto de la figura va aparte de lo que la figura es: al final del script, después de una línea /qf-style, reglas como las de una hoja de estilos —un tipo o un nombre, y entre llaves sus propiedades—. Sirve en las tres gramáticas. Lo que la línea del DSL ya dice (color=, punteado) manda sobre el bloque; lo que nadie dice sale del estilo estándar. Los números van con punto decimal, salvo que el bloque pida coma.

Ejemplo completo
/qf-dsl
window x: [-3, 3], y: [-1, 5]
f = plot(x^2, color=morado)
g = plot(x + 2)
P = intersection(f, g)
A = point(1.5, 2.25, coords)
S = area(f, g, [-1:2], donde g > f)
/qf-style
decimal: coma
curva { grosor: grueso }
g { línea: punto-raya }
punto { marcador: cruz; radio: 5 }
región { relleno: degradado(blanco, celeste, 90) }

Bloque y ajustes

/qf-style
Abre el bloque al final del script; todo lo que sigue es aspecto. Un /qf-dsl después del bloque es un error. «# » abre un comentario; #dc2626 (tres o seis cifras) es un color.
decimal: coma
Coma decimal en todos los números que escribe la figura (ejes, medidas, rótulos, {{valores}}) y punto y coma entre las coordenadas de un par. decimal: punto vuelve al de por defecto.

Reglas

curva { color: azul }
Por tipo: punto, segmento, semirrecta, recta, vector, curva, circunferencia, elipse, arco, polígono, región, ángulo, eje, cuadrícula, texto (también en plural). Varias propiedades, con «;» o una por línea; la coma no las separa: «grosor: grueso, color: azul» se lee como un solo valor y da error.
A, f { color: rojo }
Por nombre: los del script y los vértices de una figura; un lado (AB) no es un objeto aparte: el borde entero va con el nombre de la figura. Un objeto que se llama como un tipo, su plural o un alias (curva, recta, área, fuerza…) no recibe reglas por su nombre: la regla da error y pide otro nombre. La cascada: lo que la línea del DSL ya dice (color=, punteado, fill=) > regla por nombre > regla por tipo > estilo estándar; entre reglas iguales, la última.
elipse { relleno: translúcido amarillo }
Una elipse es una curva cerrada: le llegan las reglas de curva (su trazo) y las de elipse, que además la rellenan como a una circunferencia (relleno, opacidad, borde). En el plano cartesiano todavía no se rellena: avisa y aplica el trazo.
P { marcador: ninguno; rótulo: no }
Esconde un punto: sin marca ni nombre, sigue en la figura y construyendo. Con el nombre de una colección del plano, todos sus puntos.
k, r { oculto: sí }
Esconde un objeto y lo que lo acompaña: no se dibuja y sigue construyendo. En geometría y física no cuenta para el encuadre. En el plano: puntos, curvas, cónicas, rectas, polígonos, áreas y notas (texto { oculto: sí }); lo que está sobre ellos o los corta sigue en su lugar. La región de una inecuación no: «relleno: ninguno». eje { oculto: sí } quita ejes y números. También visible: no.

Propiedades

grosor: grueso
fino, medio, normal, grueso (1 · 1,5 · 2 · 3 px, la escala de TikZ) o un número en px.
línea: punto-raya
continua, discontinua, puntos, punto-raya. El patrón se escala con el grosor.
color: rojo
Un nombre (rojo, verde, azul, amarillo, naranja, morado, rosa, celeste, gris, negro, blanco, marrón; también en inglés) o un hexadecimal: #dc2626 o dc2626. En un polígono, una circunferencia o una elipse es el borde; en una región, el relleno.
marcador: cuadrado
lleno, hueco, cuadrado, cruz o ninguno (sin marca). Solo puntos.
radio: 5
El tamaño del punto, en px.
rótulo: no
Esconde el nombre del punto; «rótulo: sí» lo devuelve. En el plano, un label() de la línea manda. Solo puntos.
relleno: translúcido celeste
ninguno, sólido, translúcido, rayado, puntos (con un color si quieres) o un color solo. Polígonos, circunferencias, elipses y regiones; en el plano cartesiano, polígonos y regiones.
relleno: degradado(blanco, azul, 90)
Degradado lineal de un color al otro; el ángulo, en grados, en el sentido de la pantalla: 0, de izquierda a derecha; 90, de arriba hacia abajo.
relleno: radial(blanco, azul)
Degradado del centro al borde.
opacidad: 0.5
La del relleno (en la cuadrícula, la de sus líneas): de 0 a 1, o un porcentaje (50%). En una región también la de su contorno.
borde: ninguno
Sin contorno: polígonos, circunferencias, elipses y regiones de geometría.
Inicia cada script con /qf-dsl. Cada nombre se define una vez y puede reutilizarse. Escribe /qf-dsl help en el asistente para ver la referencia del lienzo activo.