7. Constantes y componentes
Un diseño crece. El escudo del club aparece en la polera, en el gorro y en el
parche; el ancho del borde aparece en el relleno y en el satín; el nombre de
cada alumno cambia y el resto no. En este capítulo aprendes a nombrar
valores (const), a escribir diseños que otros diseños usan como piezas
(param y place) y a pasarles argumentos.
const: valores con nombre#
const ancho = 70mm // Length
const marco = 3mm
const giro: Angle = 30deg // con el tipo escrito
const centro = (0mm, 0mm) // Point
const nombre = "ANDES" // StringEl compilador infiere el tipo del valor: const ancho = 70mm es un
Length. Si quieres, escribe el tipo después de :, y el compilador lo
comprueba:
t.hoop:3:10: error E001: `g` is declared Angle but its value is LengthLos tipos que puedes escribir son los del lenguaje: Length, Angle,
Ratio, Int, Bool, String, Color, Point, Path, Shape y List.
Una const no cambia nunca, y su nombre no se puede repetir:
t.hoop:4:1: error E007: `a` is already definedPor qué: en galeria/insignia.hoop, marco aparece en el relleno
(escudo.inset(marco)) y en el borde (width: marco). Si mañana el borde
pasa de 3 a 4 mm, cambias una línea y el relleno se achica solo. Sin la
constante tendrías que acordarte de cambiar las dos, y si olvidas una, el
satín queda sobre tela o el relleno se mete bajo todo el satín.
Aritmética#
Las constantes se combinan con operadores, pero sólo cuando el resultado tiene sentido físico:
| Operación | Resultado |
|---|---|
Length + Length, Length - Length | Length |
Length * Int, Length * Ratio, Length / Int | Length |
Length / Length | Ratio |
Angle ± Angle, Angle * Int, Angle / Int | Angle |
Int + Int, Int - Int, Int * Int, Int mod Int | Int |
Point + Point, Point - Point | Point |
const r = 30mm
const grosor = 2.5mm
const interior = r - grosor // 27.5mm
const paso = 360deg / 8 // 45degSumar peras con manzanas es un error de tipos:
t.hoop:3:15: error E001: cannot compute Length + AngleHay una que sorprende: Int / Int no existe, porque no hay resultado entero
exacto (7 / 2 no es un número de puntadas):
t.hoop:4:63: error E001: Int / Int has no whole result: use mod for the remainder (i mod 2), or a Length (10mm / 3)const y shape#
Una forma puede ir en una const o en un shape. La diferencia es que
shape exige una forma cerrada: un path sin close en un shape es un
error, mientras que en una const es un camino abierto válido (para un
corrido o un satín along). Usa shape para las formas: el error aparece
donde la declaras, no donde la usas.
El orden de las declaraciones#
El archivo se lee de arriba abajo: un nombre existe desde que se declara. Si
usas escudo antes de declararlo, el compilador todavía no lo conoce, y te
dice dónde está:
t.hoop:3:27: error E007: `escudo` is used before it is declared (line 4)Un nombre que no está en ninguna parte es unknown name, con la sugerencia
si escribiste uno parecido (unknown name escudp; did you mean escudo?).
Componentes: un diseño dentro de otro#
Cualquier design puede usarse como pieza de otro diseño con place. No hace
falta nada especial: la insignia de galeria/insignia.hoop es un diseño
completo, y galeria/polera.hoop la coloca en el pecho de una polera:
// polera.hoop: la insignia como componente, en el pecho de una polera.
// El lienzo es el espacio del pecho; la insignia se coloca más chica y el
// texto va debajo, fuera de la insignia pero dentro del pecho.
import { c002 as negro } from "../bibliotecas/janome.hoop"
design "Polera promoción" {
canvas: rect(size: (80mm, 95mm)) // el espacio en el pecho
fabric: Pique
}
layer insignia {
place "insignia.hoop" at (40mm, 4mm) anchor: top_center scale: 90%
}
layer texto {
text negro {
value: "PROMO 2026", font: Montserrat, height: 6mm,
at: (40mm, 88mm), align: center
}
}galeria/polera.hoop: ok — 5 threads, 2 layers, 2 objectsplace "archivo.hoop" at (x, y) va dentro de una capa, y acepta:
anchor: qué punto del lienzo del diseño colocado cae enat:center(por defecto),top_left,top_center,top_right,center_left,center_right,bottom_left,bottom_centerobottom_right.rotate: unAngle, gira el diseño entero.scale: unRatio, lo escala entero.
Lo que pasa al colocar:
El diseño colocado se compila con la tela de quien lo coloca: la insignia, escrita en sarga (
Twill), se cose como piqué en la polera.Sus hilos y sus
seqvienen con él, y el compilador optimiza el orden de todo junto. Por eso la polera dice5 threads: los de la insignia más el suyo.Lo que cose se revisa contra el lienzo y los
insidede aquí: si la insignia no cabe en el pecho, es unE006en la línea delplace.Sus errores se informan en la línea del
place, con el archivo y la posición de adentro.Un diseño que se coloca a sí mismo (directa o indirectamente) es un ciclo:
Salida del compiladoryo.hoop:2:11: error E007: place cycle: yo.hoop → yo.hoop
Cuidado con scale: escalar un diseño escala todo, también el ancho de
los satines y la altura de las letras. La insignia al 90% tiene un marco de
2.7 mm en vez de 3 mm, y su texto baja de 9 a 8.1 mm, justo sobre los 8 mm
que pide Varsity. Al 85% bajaría a 7.65 mm y el compilador avisaría:
galeria/polera.hoop:12:3: warning W004: at scale 85%, text in `insignia.hoop` is 7.6 mm high, below the minimum for Varsity (8 mm)Dentro de un ±20% suele andar bien; para cambios grandes conviene un diseño propio, con sus satines y letras pensados para ese tamaño.
param: lo que quien coloca puede cambiar#
Un componente se vuelve realmente útil cuando se puede ajustar. param
declara un valor con un valor por defecto, que quien coloca el diseño puede
cambiar:
// estrella.hoop: un componente. Quien lo coloca puede cambiar sus `param`.
param puntas = 5 // Int
param radio = 20mm // Length
thread oro = #d8a21b "Oro"
thread negro = #1c1616 "Negro"
design "Estrella" {
canvas: circle(center: (0mm, 0mm), r: 25mm)
}
const paso = 360deg / puntas
shape rayo = polygon((-3mm, 0mm), (0mm, -radio), (3mm, 0mm))
layer rayos {
for i in 0..puntas {
fill oro { shape: rayo.rotate(i * paso, (0mm, 0mm)) }
}
}
layer centro {
fill negro { shape: circle(center: (0mm, 0mm), r: 4mm) }
}(El for repite los rayos: lo verás en el capítulo 8. Este diseño está
dibujado alrededor de (0mm, 0mm): las coordenadas que escribes son las que
valen, y el lienzo es el que dice dónde está el diseño. Si prefieres pensar
desde el centro, como en Wilcom, escribe origin: center en el design: el
centro del lienzo pasa a ser (0, 0).)
Solo, estrella.hoop compila con sus valores por defecto: cinco puntas de
20 mm. Colocado, recibe argumentos con nombre, entre paréntesis después
del archivo:
// cartel.hoop: tres estrellas, cada una con sus argumentos.
design "Cartel" { canvas: rect(size: (150mm, 60mm)) }
thread azul = #1f3a8a "Azul"
layer fondo {
satin azul { border: canvas, width: 2mm }
}
layer estrellas {
place "estrella.hoop" at (30mm, 30mm)
place "estrella.hoop"(puntas: 7) at (75mm, 30mm)
place "estrella.hoop"(puntas: 8, radio: 14mm) at (120mm, 30mm) rotate: 22.5deg
}cartel.hoop: 150.4 × 60.4 mm, 5378 stitches, 3 threads, 7 trims, 37 msLos argumentos se revisan como todo lo demás. Uno que no existe, con sugerencia:
c2.hoop:12:33: error E007: `estrella.hoop` has no param `puntos`; did you mean `puntas`?uno de otro tipo:
c3.hoop:12:3: error E001: in `estrella.hoop` 2:1: error: `puntas` is Int but it was given Lengthy uno que hace que el diseño colocado se salga de su propio lienzo (una estrella de 30 mm de radio en un lienzo de 25):
c4.hoop:12:3: error E006: in `estrella.hoop` 18:5: error: fill overflows the canvas by 5.0 mm at the topComo con const, el tipo del param se infiere de su valor por defecto, o se
escribe: param nombre: String = "ANA".
Una limitación de esta versión: un param no puede ser un hilo. Los hilos se
declaran con thread y una primitiva necesita el nombre de un thread
declarado; un param hilo = oro no sirve para fill hilo { … }. Para un
componente con colores distintos según el caso, declara los hilos en el
componente y elige con un param y un if (capítulo 8).
El caso de uso estrella de param es el parche de nombre
(galeria/parche.hoop): un param nombre = "GONZALO", un texto con
value: nombre encajado en una caja, y una plancha que lo coloca doce veces
con doce nombres (capítulo 9).
Resumen#
const nombre = valor(oconst nombre: Tipo = valor) nombra un valor; el tipo se infiere y se comprueba.- La aritmética sigue las unidades:
Length + AngleyInt / Intno existen. - Los nombres existen desde la línea en que se declaran; usarlos antes es un error que dice en qué línea están.
place "x.hoop" at (x, y) anchor: … rotate: … scale: …coloca un diseño dentro de otro, compilado con la tela de aquí.paramdeclara lo que quien coloca puede cambiar:place "x.hoop"(nombre: "LUIS") at ….
Referencia: declaraciones · expresiones · tipos · módulos