HoopLang Docs
/
Playground
El libro · Capítulo 07

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#

HoopLang
const ancho = 70mm // Length
const marco = 3mm
const giro: Angle = 30deg // con el tipo escrito
const centro = (0mm, 0mm) // Point
const nombre = "ANDES" // String

El 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:

Salida del compilador
t.hoop:3:10: error E001: `g` is declared Angle but its value is Length

Los 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:

Salida del compilador
t.hoop:4:1: error E007: `a` is already defined

Por 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ónResultado
Length + Length, Length - LengthLength
Length * Int, Length * Ratio, Length / IntLength
Length / LengthRatio
Angle ± Angle, Angle * Int, Angle / IntAngle
Int + Int, Int - Int, Int * Int, Int mod IntInt
Point + Point, Point - PointPoint
HoopLang
const r        = 30mm
const grosor   = 2.5mm
const interior = r - grosor // 27.5mm
const paso     = 360deg / 8 // 45deg

Sumar peras con manzanas es un error de tipos:

Salida del compilador
t.hoop:3:15: error E001: cannot compute Length + Angle

Hay una que sorprende: Int / Int no existe, porque no hay resultado entero exacto (7 / 2 no es un número de puntadas):

Salida del compilador
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á:

Salida del compilador
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:

HoopLang
// 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
  }
}
Salida del compilador
galeria/polera.hoop: ok — 5 threads, 2 layers, 2 objects

place "archivo.hoop" at (x, y) va dentro de una capa, y acepta:

  • anchor: qué punto del lienzo del diseño colocado cae en at: center (por defecto), top_left, top_center, top_right, center_left, center_right, bottom_left, bottom_center o bottom_right.
  • rotate: un Angle, gira el diseño entero.
  • scale: un Ratio, 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 seq vienen con él, y el compilador optimiza el orden de todo junto. Por eso la polera dice 5 threads: los de la insignia más el suyo.

  • Lo que cose se revisa contra el lienzo y los inside de aquí: si la insignia no cabe en el pecho, es un E006 en la línea del place.

  • 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 compilador
    yo.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:

Salida del compilador
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:

HoopLang
// 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:

HoopLang
// 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
}
Salida del compilador
cartel.hoop: 150.4 × 60.4 mm, 5378 stitches, 3 threads, 7 trims, 37 ms

Los argumentos se revisan como todo lo demás. Uno que no existe, con sugerencia:

Salida del compilador
c2.hoop:12:33: error E007: `estrella.hoop` has no param `puntos`; did you mean `puntas`?

uno de otro tipo:

Salida del compilador
c3.hoop:12:3: error E001: in `estrella.hoop` 2:1: error: `puntas` is Int but it was given Length

y 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):

Salida del compilador
c4.hoop:12:3: error E006: in `estrella.hoop` 18:5: error: fill overflows the canvas by 5.0 mm at the top

Como 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 (o const nombre: Tipo = valor) nombra un valor; el tipo se infiere y se comprueba.
  • La aritmética sigue las unidades: Length + Angle y Int / Int no 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í.
  • param declara lo que quien coloca puede cambiar: place "x.hoop"(nombre: "LUIS") at ….

Referencia: declaraciones · expresiones · tipos · módulos