Como o Kimi K3 construiu, sozinho, um traço digital de caneta-tinteiro
Queríamos a caneta-tinteiro direcional do Freeform no Lulucat Notes. A especificação era uma captura de ecrã de escrita à mão. Dois modelos, uma pergunta discriminante e uma pena elíptica depois, a caneta de água escreve como deve ser. A investigação, o código e a validação foram feitos pelo Kimi K3 na Fireworks.

Renderizado pelo pipeline Metal do Lulucat Notes de que este artigo trata.
O Lulucat Notes tem uma caneta simples e um marcador. A ferramenta seguinte no planeamento era uma caneta de água — a caneta-tinteiro direcional que se conhece do Apple Notes e do Freeform, em que os traços verticais saem grossos e os horizontais saem finos. A especificação desta ferramenta não era um documento. Era uma captura de ecrã: três linhas de escrita à mão, as palavras “pencilkit”, “无边记” e “这种有方向的水笔能力” — esta capacidade direcional de caneta de água.
Tal como no pipeline Metal anterior, o trabalho foi feito pelo Kimi K3, o modelo aberto da Moonshot a correr na Fireworks: as medições, a modelação, o código e a validação. O humano forneceu a captura de ecrã, respondeu a uma pergunta e avaliou a sensação num iPad real.
A especificação é uma captura de ecrã
Uma imagem estática não consegue dizer por que razão um traço é fino. Só consegue dizer quão fino e onde. Por isso, o primeiro passo foi medir. Analisámos a captura de ecrã linha a linha e coluna a coluna, seguindo o desvio da linha central de cada traço para obter a sua direção; depois convertemos a largura da varredura em largura real usando o seno desse ângulo:
| Traço | Direção | Largura real |
|---|---|---|
| Haste ascendente do “l”, “pencilkit” | ≈ 78° | 27.3 px |
| Haste do “k”, “pencilkit” | ≈ 90° | 28 px |
| Traço descendente para a esquerda de 力 | ≈ 66° | 25.7 px |
| Conectores cursivos | ≈ 8° | 11 px |
| Horizontais chinesas (横) | ≈ 0° | 6–11 px |
A proporção entre grosso e fino é ≈ 2.5. Ajustar
Versão um: largura a partir da direção
O primeiro modelo era o óbvio. Calcular uma direção para cada ponto de entrada, mapear a direção para uma largura através da curva ajustada e incorporar o resultado no raio do ponto no momento da captura. A direção era estimada causalmente — uma média ponderada com decaimento exponencial sobre os últimos pontos do arco, calculada no espaço de ângulo duplo para que uma inversão do movimento não anulasse a estimativa. Só são usados pontos anteriores, por isso o traço ao vivo e o traço consolidado coincidem byte a byte.
O builder passou a verificação unitária: trajetórias sintéticas horizontais, verticais, a 45° e de inversão incorporaram todas as larguras teóricas — 0.99, 2.52, 2.00 e 0.99 pontos. A renderização não precisou de código novo: um traço com raio incorporado é apenas uma cadeia de carimbos redondos, e o nosso pipeline de point sprites já os desenhava.
No iPad, o humano demorou cerca de dez segundos a rejeitá-lo: “isto é uma caneta artística, não a caneta de água”.
Duas histórias cabem numa imagem
Porque é que parecia errado? Havia duas explicações candidatas, e a captura de ecrã não as conseguia separar:
- Bloqueio de direção. A geometria da pena obriga as horizontais a serem finas e as verticais grossas, independentemente do que a mão faça. Foi isso que a versão um implementou.
- Pressão e velocidade. A caneta é controlada pela pressão, e o padrão da amostra é apenas a dinâmica da escrita à mão: as passadas descendentes recebem naturalmente mais pressão, e os conectores são naturalmente rápidos e leves.
Ambas produzem uma captura de ecrã com horizontais finas e verticais grossas. A diferença está no que acontece quando se pressiona com força um traço horizontal. A versão um mantém-no fino. Uma caneta controlada pela pressão torna-o grosso. Por isso fizemos uma pergunta ao humano: um traço horizontal pressionado com força deve ficar mais grosso?
“Não. As horizontais ficam finas.”
O bloqueio de direção ficou confirmado. Mas continuava a haver algo errado, porque a versão um também estava bloqueada pela direção.
A resposta estava nas pontas dos traços
A pista seguinte estava no topo das hastes ascendentes. Ao ampliar as hastes do “l” e do “k” na amostra, vimos duas coisas: um traço direito mantém a mesma largura do princípio ao fim, e as pontas dos traços são cortes diagonais planos — o formato de uma pena de talhe a levantar-se do papel. Não são pontos redondos. Não são afunilamentos causados pela pressão.

Era isso que a “sensação de caneta artística” significava. A versão um modelou o aspeto da amostra — a largura como função da direção estimada — mas não a caneta. Um estimador de direção é um sensor: treme com uma entrada ruidosa, atrasa-se nas curvas e arredonda cada ponta de traço até formar um círculo. Uma pena verdadeira não tem nenhum destes problemas, porque não calcula nada. A largura é geometria.
Versão dois: uma pena elíptica
O modelo final não tem qualquer estimador de direção. A pena é uma elipse orientada: eixo maior horizontal, eixo menor fixo. Os carimbos desta elipse são colocados densamente ao longo do caminho do traço, e tudo o resto resulta da geometria:
-
Um traço horizontal percorre a borda aberta, por isso tem sempre
de largura — uma linha fina constante, com qualquer pressão. É exatamente a especificação confirmada. -
Um traço vertical atravessa todo o eixo maior:
, a extremidade grossa. -
Um traço diagonal assume a largura da corda da elipse perpendicular ao movimento,
-
As pontas dos traços são cortes elípticos — as pontas planas com o formato da pena da amostra, sem trabalho adicional.
-
A pressão escala apenas o eixo maior,
, por isso aumenta o volume de tinta nas passadas descendentes e nunca pode engrossar uma horizontal.
A partir das medições da amostra, mantivemos
A renderização precisou de um novo fragment shader e de mais nada. O formato do vértice — posição, diâmetro, cor — já transportava tudo: o diâmetro é o eixo maior e o eixo menor é um uniform por passagem. O shader avalia um SDF de elipse com a mesma rampa de cobertura de meio pixel dos nossos carimbos redondos, o que o mantém comparável ao nível do pixel com a implementação de referência do Core Graphics (fillEllipse per stamp). A validação passou pelo gate habitual: um corpus sintético com dois traços de caneta-tinteiro, renderizado das duas formas e comparado pixel a pixel — zero diferenças estruturais, e um traço horizontal verificado pontualmente mediu 12 px nos dois renderers.

A versão um está à esquerda e a versão dois à direita. A mesma entrada manuscrita, o mesmo pipeline. As pontas contam a história.
No iPad, a nova caneta passou imediatamente: “好,很好” — bom. Muito bom.
Ficar com o erro
A versão um não foi para o lixo. É um pincel legitimamente interessante — simplesmente não é uma caneta de água. Por isso, foi lançada como a primeira entrada de um novo menu de pincéis experimentais, com o nome que o humano lhe deu: 漏水的圆珠笔, a esferográfica que verte. A barra de ferramentas ganhou um botão em forma de frasco que abre uma lista textual de experiências; acrescentar a seguinte custa uma linha num registo. Um modelo rejeitado não é trabalho desperdiçado quando pode ser lançado como uma experiência claramente identificada.
Uma noite de ciclos
O percurso completo — medir, modelar, construir, testar no dispositivo, perguntar, remodelar, reconstruir, validar — demorou uma noite. O Kimi K3 na Fireworks tratou de todo o lado técnico do ciclo: concebeu as varreduras de medição, propôs a pergunta discriminante em vez de adivinhar uma segunda vez, apagou o seu próprio estimador de direção quando as evidências mudaram e ampliou o pixel-diff harness antes de tocar na aplicação. A velocidade de inferência da Fireworks manteve o ciclo interativo — diffs longos de Metal e Swift, scripts de análise de píxeis e ferramentas de corpus chegavam suficientemente depressa para que o gargalo continuasse onde devia: no juízo humano.
O padrão de colaboração foi o mesmo do artigo sobre o pipeline Metal e voltou a funcionar: o modelo é rápido e preciso; o humano fica responsável pelo gosto e pela verificação de ponta a ponta. Uma frase de feedback baseada na sensação — “caneta artística, não caneta de água” — bastou para o modelo localizar o erro exato de modelação e substituí-lo por um design mais simples.
O modelo certo acabou por ser mais pequeno do que o errado. A versão dois foi lançada com menos partes móveis do que a versão um — sem estimador, sem janela de suavização e sem expoente ajustado. A pena não calcula a largura. A pena é a largura.