Jak Kimi K3 samodzielnie zbudował cyfrowe pociągnięcie pióra wiecznego
Chcieliśmy kierunkowego pióra wiecznego z Freeform w Lulucat Notes. Specyfikacją był zrzut ekranu pisma odręcznego. Dwa modele, jedno pytanie rozstrzygające i eliptyczna stalówka później pióro wodne pisze tak, jak powinno. Badania, kod i walidację przeprowadził Kimi K3 na Fireworks.

Wyrenderowane przez potok Metal w Lulucat Notes, którego dotyczy ten wpis.
Lulucat Notes ma zwykłe pióro i zakreślacz. Kolejnym narzędziem w planie było pióro wodne — kierunkowe pióro wieczne znane z Apple Notes i Freeform, w którym pionowe pociągnięcia wychodzą grube, a poziome cienkie. Specyfikacją tego narzędzia nie był dokument. Był nią zrzut ekranu: trzy linie odręcznego pisma, słowa “pencilkit”, “无边记” i “这种有方向的水笔能力” — ta kierunkowa funkcja pióra wodnego.
Podobnie jak wcześniej w przypadku potoku Metal, pracę wykonał Kimi K3, otwarty model firmy Moonshot uruchomiony na Fireworks: pomiary, modelowanie, kod i walidację. Człowiek dostarczył zrzut ekranu, odpowiedział na jedno pytanie i ocenił wrażenia na prawdziwym iPadzie.
Specyfikacja to zrzut ekranu
Statyczny obraz nie powie, dlaczego pociągnięcie jest cienkie. Może tylko pokazać, jak cienkie jest i gdzie. Pierwszym krokiem były więc pomiary. Przeskanowaliśmy zrzut ekranu wiersz po wierszu i kolumna po kolumnie, śledząc przesunięcie linii środkowej każdego pociągnięcia, aby uzyskać jego kierunek, a następnie przeliczyliśmy szerokość skanu na rzeczywistą szerokość za pomocą sinusa tego kąta:
| Pociągnięcie | Kierunek | Rzeczywista szerokość |
|---|---|---|
| Wznosząca kreska litery “l”, “pencilkit” | ≈ 78° | 27.3 px |
| Trzon litery “k”, “pencilkit” | ≈ 90° | 28 px |
| Lewy opadający zamach w 力 | ≈ 66° | 25.7 px |
| Łączniki kursywy | ≈ 8° | 11 px |
| Chińskie poziome kreski (横) | ≈ 0° | 6–11 px |
Stosunek grubości do cienkości wynosi ≈ 2.5. Dopasowanie
Wersja pierwsza: szerokość z kierunku
Pierwszy model był oczywisty. Dla każdego punktu wejściowego oblicz kierunek, odwzoruj go na szerokość za pomocą dopasowanej krzywej i w momencie przechwytywania zapisz wynik w promieniu punktu. Kierunek szacowano przyczynowo — jako wykładniczo malejącą średnią ważoną z kilku ostatnich punktów łuku, obliczaną w przestrzeni podwojonego kąta, aby odwrócenie kierunku ruchu nie wyzerowało oszacowania. Wykorzystywane są wyłącznie wcześniejsze punkty, więc pociągnięcie na żywo i zatwierdzone pociągnięcie są identyczne bajt po bajcie.
Builder przeszedł test jednostkowy: syntetyczne ścieżki poziome, pionowe, pod kątem 45° oraz ze zmianą kierunku wszystkie zapisały teoretyczne szerokości — 0.99, 2.52, 2.00 i 0.99 punktu. Renderowanie nie wymagało żadnego nowego kodu: pociągnięcie z zapisanym promieniem to po prostu łańcuch okrągłych stempli, a nasz potok point-sprite już je rysował.
Na iPadzie człowiek odrzucił je po około dziesięciu sekundach: “to jest pióro artystyczne, nie pióro wodne”.
Dwie historie pasują do jednego obrazu
Dlaczego sprawiało złe wrażenie? Istniały dwa możliwe wyjaśnienia, których zrzut ekranu nie pozwalał rozdzielić:
- Blokada kierunku. Geometria stalówki wymusza cienkie poziome i grube pionowe, niezależnie od tego, co robi dłoń. To właśnie zaimplementowała wersja pierwsza.
- Nacisk i prędkość. Pióro reaguje na nacisk, a wzór z próbki to po prostu dynamika pisma odręcznego: przy pociągnięciach w dół naturalnie naciskamy mocniej, a łączniki są naturalnie szybkie i lekkie.
Oba wyjaśnienia dają obraz z cienkimi poziomymi i grubymi pionowymi. Różnica ujawnia się wtedy, gdy mocno naciśniesz poziome pociągnięcie. Wersja pierwsza pozostawia je cienkie. Pióro reagujące na nacisk pogrubiłoby je. Zadaliśmy więc człowiekowi jedno pytanie: czy mocno naciśnięte poziome pociągnięcie powinno stać się grubsze?
“Nie. Poziome pozostają cienkie.”
Blokada kierunku została potwierdzona. Coś jednak nadal było nie tak, bo wersja pierwsza również blokowała kierunek.
Odpowiedź kryła się w zakończeniach pociągnięć
Kolejna wskazówka znajdowała się na szczytach wznoszących kresek. Zbliżenie trzonów liter “l” i “k” w próbce pokazało dwie rzeczy: proste pociągnięcie zachowuje jedną szerokość od początku do końca, a jego końce to płaskie ukośne cięcia — kształt dłutowatej stalówki odrywającej się od papieru. Nie okrągłe kropki. Nie zwężenia od nacisku.

To właśnie oznaczało „wrażenie pióra artystycznego”. Wersja pierwsza zamodelowała wygląd próbki — szerokość jako funkcję oszacowanego kierunku — ale nie pióro. Estymator kierunku jest czujnikiem: drży przy zaszumionym wejściu, opóźnia się na zakrętach i zaokrągla każdy koniec pociągnięcia do koła. Prawdziwa stalówka nie ma żadnego z tych problemów, bo niczego nie oblicza. Szerokość to geometria.
Wersja druga: eliptyczna stalówka
Końcowy model nie ma żadnego estymatora kierunku. Stalówka jest zorientowaną elipsą: długa oś jest pozioma, a krótka ma stałą długość. Stemple tej elipsy są gęsto rozmieszczone wzdłuż ścieżki pociągnięcia, a cała reszta wynika z geometrii:
-
Poziome pociągnięcie biegnie wzdłuż otwartej krawędzi, więc zawsze ma szerokość
— stała cienka linia przy dowolnym nacisku. Dokładnie tak brzmi potwierdzona specyfikacja. -
Pionowe pociągnięcie przecina całą długą oś:
, czyli gruby koniec. -
Ukośne pociągnięcie przyjmuje szerokość cięciwy elipsy prostopadłej do kierunku ruchu,
-
Końce pociągnięć są eliptycznymi cięciami — płaskie końce w kształcie stalówki z próbki, bez dodatkowej pracy.
-
Nacisk skaluje tylko długą oś,
, więc zwiększa objętość atramentu przy pociągnięciach w dół i nigdy nie pogrubia poziomego pociągnięcia.
Na podstawie pomiarów próbki zachowaliśmy
Renderowanie wymagało jednego nowego fragment shadera i niczego więcej. Format wierzchołka — pozycja, średnica, kolor — już zawierał wszystko: średnica jest długą osią, a krótka oś jest uniformem dla danego przebiegu. Shader oblicza SDF elipsy z tą samą rampą pokrycia półpiksela co nasze okrągłe stemple, dzięki czemu można go porównywać piksel w piksel z implementacją referencyjną Core Graphics (fillEllipse per stamp). Walidacja przeszła przez zwykłą bramkę: syntetyczny korpus z dwoma pociągnięciami pióra, wyrenderowany na oba sposoby i porównany piksel po pikselu — zero różnic strukturalnych, a sprawdzone punktowo poziome pociągnięcie miało 12 px w obu rendererach.

Wersja pierwsza jest po lewej, a druga po prawej. Ten sam odręczny input, ten sam potok. Końce mówią wszystko.
Na iPadzie nowe pióro przeszło od razu: “好,很好” — dobrze. Bardzo dobrze.
Zachować nietrafiony model
Wersja pierwsza nie trafiła do kosza. To interesujący pędzel — po prostu nie jest piórem wodnym. Dlatego trafiła jako pierwsza pozycja do nowego menu eksperymentalnych pędzli, pod nazwą nadaną przez człowieka: 漏水的圆珠笔, czyli cieknący długopis. Pasek narzędzi zyskał przycisk kolby, który otwiera tekstową listę eksperymentów; dodanie kolejnego kosztuje jedną linię w rejestrze. Odrzucony model nie oznacza zmarnowanej pracy, jeśli może zostać wydany jako wyraźnie opisany eksperyment.
Jeden wieczór pętli
Cały przebieg — zmierzyć, zamodelować, zbudować, sprawdzić na urządzeniu, zadać pytanie, przebudować model, przebudować implementację, zwalidować — zajął jeden wieczór. Kimi K3 na Fireworks wykonał całą techniczną stronę tej pętli: opracował skany pomiarowe, zaproponował pytanie rozstrzygające zamiast zgadywać po raz drugi, usunął własny estymator kierunku, gdy dowody wskazały inną drogę, i rozszerzył harness do pixel-diffów, zanim dotknął aplikacji. Szybkość wnioskowania Fireworks utrzymała interaktywny charakter pętli — długie diffy Metal i Swift, skrypty analizy pikseli oraz narzędzia korpusowe pojawiały się wystarczająco szybko, by wąskim gardłem pozostał człowiek i jego osąd.
Wzorzec współpracy był taki sam jak w artykule o potoku Metal i znów się sprawdził: model jest szybki i precyzyjny, a człowiek odpowiada za wyczucie oraz weryfikację od początku do końca. Jedno zdanie informacji zwrotnej opartej na wrażeniu — “pióro artystyczne, nie pióro wodne” — wystarczyło, by model odnalazł dokładny błąd modelowania i zastąpił go prostszym projektem.
Właściwy model okazał się mniejszy od błędnego. Wersja druga trafiła do produktu z mniejszą liczbą ruchomych części niż pierwsza — bez estymatora, bez okna wygładzania i bez dopasowanego wykładnika. Stalówka nie oblicza szerokości. Stalówka jest szerokością.