Inscribed Glyph (geometria de figura inscrita em contêiner)

draft v0.1 mandatory

Protocolo geométrico para inscrever uma figura plana arbitrária (glyph, símbolo, desenho irregular) dentro de um contêiner de UI (botão circular, tile quadrado, retângulo arredondado, pílula), garantindo centralização exata, margem uniforme e tamanho percebido consistente entre os irmãos de uma família. Formulação genérica (funcional de Minkowski do contêiner erodido pela margem), com casos resolvidos: círculo, quadrado, squircle e pílula. Inclui a etapa de VERIFICAÇÃO POR MEDIÇÃO da tinta renderizada — a regra que converte estética em teste automatizável (lint no KDS). Nasceu da calibração da barra de controles do PoC rastreador-web (Botucatu, 2026-06-10/11), onde três violações reais deste protocolo produziram glifos 30% menores, overrides silenciados e centros falsos.

Status: draft v0.1 (2026-06-11). Protocolo normativo; o lint automatizado (T1–T4 em CI via KDS/kicon) é follow-up.

R1 — O problema e os três objetos

Dado um contêiner K (região convexa do plano: círculo, quadrado, squircle, pílula) com centro C, uma margem m e uma figura F (conjunto de pontos com tinta — fills e strokes), inscrever F em K significa satisfazer três igualdades independentes:

  1. Centralização — o centro do envolvente da tinta coincide com C;
  2. Ocupação — a tinta escalada atinge exatamente o limite K⊖m

    (contêiner erodido pela margem), sem ultrapassá-lo;

  3. Consistência de família — todos os irmãos compartilham a mesma

    razão de ocupação.

A falha de qualquer uma produz um defeito que as outras duas não compensam.

figure <svg viewBox"0 0 360 130" xmlns"http:/ww.w3.org2000svg" role"img" aria-label"Anatomia: contêiner, margem, envolvente da tinta"> <circle cx"65" cy"65" r"56" fill"#1f2733" stroke"#475569" stroke-width"2"/> <circle cx"65" cy"65" r"40" fill"none" stroke"#f59e0b" stroke-width"1.5" stroke-dasharray="4 4"/> <rect x"38" y"40" width"54" height"50" fill"none" stroke"#22d3ee" stroke-width"1.5" stroke-dasharray"3 3"/> <path d"M65 44 L86 78 L44 78 Z" fill"#e2e8f0"/> <line x1"65" y1"9" x2"65" y2"25" stroke"#f59e0b" stroke-width"2"/> <text x"140" y"30" fill"#94a3b8" font-size"12" font-family="monospace">K contêiner (raio R)/text <text x"140" y"52" fill"#f59e0b" font-size"12" font-family="monospace">K⊖m limite útil (R − m)/text <text x"140" y"74" fill"#22d3ee" font-size"12" font-family="monospace">B envolvente da TINTA/text <text x"140" y"96" fill"#e2e8f0" font-size"12" font-family="monospace">F figura (tinta real)/text <text x"140" y"118" fill"#94a3b8" font-size"12" font-family="monospace">centro de B ≡ C (R2)/text /svg figcaptionAnatomia: contêiner K, margem m, envolvente da tinta B./figcaption /figure

R2 — Medir a TINTA, nunca a moldura

O envolvente B = [xmin,xmax]×[ymin,ymax] é o da tinta renderizada: strokes contam com a espessura (um traço de espessura t expande B em t/2 por lado). A moldura (viewBox) é irrelevante para o resultado visual — duas figuras na mesma moldura podem pintar extensões 40% diferentes.

Antipattern real: na barra do rastreador-web, pause, + e pintavam 10–12 unidades de uma moldura de 24 enquanto camadas pintava 20 — com a mesma escala, glifos 40% menores e margens desiguais.

R3 — Centralizar a figura na própria moldura (no desenho)

Translade F até o centro de B coincidir com o centro da moldura: x_min + x_max = V e y_min + y_max = V (moldura de lado V). Faça na geometria do path — a figura carrega a própria centralização para qualquer uso futuro. Curvas Bézier contam pelo extremo REAL da curva, não pelos pontos de controle.

R4 — Escala genérica: o funcional do contêiner erodido

Para contêiner convexo K de centro C e margem m, com tinta centrada (R3), o fator máximo de escala é:

s* = max { s :  s · (F − B_c)  ⊆  (K − C) ⊖ m }
   = min sobre os pontos p da tinta de  dist_K(p)

onde dist_K mede "quanto dá pra crescer na direção de p até tocar o limite útil". Por contêiner:

Contêiner Limite útil Quem manda na escala
Círculo (raio R) círculo R−m o ponto da tinta mais DISTANTE do centro: s = (R−m)/max‖p−B_c‖
Quadrado (lado L) quadrado L−2m a maior coordenada em módulo (norma do máximo): `s = (L2−m)max( px , py )`
Squircle / retângulo arredondado mesmo, com cantos: nos cantos vale a norma radial do arco, nos lados a do máximo misto — pontas diagonais são limitadas pelo arco do canto
Pílula semicircunferências + faixa central extremos longitudinais pela semicircunferência; o resto pela faixa

Consequência prática: a mesma figura inscrita num círculo e num quadrado de mesmo "tamanho" recebe escalas diferentes — o círculo pune pontas diagonais (√2 mais perto do limite), o quadrado não.

figure <svg viewBox"0 0 360 120" xmlns"http:/ww.w3.org2000svg" role"img" aria-label"Mesma figura, contêineres diferentes, escalas diferentes"> <circle cx"60" cy"60" r"50" fill"#1f2733" stroke"#475569" stroke-width"2"/> <path d"M60 26 L94 94 L26 94 Z" fill"none" stroke"#e2e8f0" stroke-width"3"/> <rect x"130" y"10" width"100" height"100" rx"10" fill"#1f2733" stroke"#475569" stroke-width"2"/> <path d"M180 22 L222 102 L138 102 Z" fill"none" stroke"#e2e8f0" stroke-width"3"/> <rect x"250" y"35" width"104" height"50" rx"25" fill"#1f2733" stroke"#475569" stroke-width"2"/> <path d"M302 42 L320 78 L284 78 Z" fill"none" stroke"#e2e8f0" stroke-width"3"/> <text x"40" y"118" fill"#94a3b8" font-size"10" font-family="monospace">círculo/text <text x"156" y"118" fill"#94a3b8" font-size"10" font-family="monospace">squircle (maior s)/text <text x"268" y"100" fill"#94a3b8" font-size"10" font-family="monospace">pílula/text /svg figcaptionO mesmo triângulo recebe escala diferente por contêiner: quem decide é a geometria do limite útil, não a moldura./figcaption /figure

R5 — Consistência de família: keylines

Numa família (toolbar, rail), o alvo NÃO é "todo glyph com a mesma extensão", e sim extensões-alvo por proporção (keylines, como Material/SF Symbols):

Keyline Proporção do envolvente Extensão-alvo (fração do limite útil)
Quadrado w ≈ h 0,82
Retrato h > 1,25·w altura 0,90
Paisagem w > 1,25·h largura 0,90
Circular glyph ~circular 0,86

Racional: um glyph paisagem com a MESMA largura de um quadrado parece menor (menos área); as keylines compensam massa percebida. Para famílias pequenas e homogêneas, um único k (caso rastreador-web: k = 16,5px num botão de 40px ⇒ ocupação 0,41 do diâmetro) é uma simplificação aceitável.

R6 — Correção óptica (formas assimétricas)

O centro do envolvente ≠ centroide da tinta em formas assimétricas (play, setas). Desloque na direção contrária ao desequilíbrio de massa em até 3% do diâmetro do contêiner. Formas com dois eixos de simetria dispensam.

R7 — Verificação por medição (fecha o ciclo; é o que vira lint)

Após renderizar: extraia o envolvente da TINTA da imagem final e valide:

  • T1 (centralização): |centro(B_render) − C| ≤ 1px nos dois eixos;
  • T2 (ocupação): extensão de B_render = alvo da keyline ± 1px;
  • T3 (família): desvio-padrão das ocupações dos irmãos ≤ 4% do alvo;
  • T4 (margem): nenhum pixel de tinta dentro da faixa de margem m.

A medição pegou, no caso real, dois defeitos invisíveis à revisão de código: (a) glifos 30–40% menores (R2 violada); (b) um override CSS de tamanho que NUNCA vencia por especificidade — o valor estava no stylesheet, mas a tinta renderizada provava que não era aplicado. Regra de ouro: o que se audita é o pixel, não o código.

R8 — Ordem do protocolo

medir tinta → centralizar na moldura → escalar pelo funcional do
contêiner (keyline) → compor (homotetia + translação) → ajuste óptico
(se assimétrica) → MEDIR DE VOLTA (T1–T4)

Inverter etapas (escalar antes de centralizar; centralizar pela moldura) reproduz exatamente os defeitos catalogados acima.