Як Kimi K3 створила цифровий штрих перової ручки
Ми хотіли додати до Lulucat Notes спрямований штрих перової ручки з Freeform. Специфікацією був лише скріншот рукопису. Після двох моделей, одного розрізнювального запитання та еліптичного пера водяна ручка пише так, як має. Дослідження, код і перевірку виконала Kimi K3 на Fireworks.

Відрендерено Metal-конвеєром Lulucat Notes, про який ідеться в цій публікації.
У Lulucat Notes є проста ручка та маркер. Наступним інструментом у дорожній карті була водяна ручка — спрямована перова ручка, яку ви знаєте з Apple Notes і Freeform: вертикальні штрихи виходять товстими, а горизонтальні — тонкими. Специфікацією цього інструмента був не документ, а скріншот: три рядки рукопису, слова «pencilkit», «无边记» і «这种有方向的水笔能力» — ця здатність водяної ручки працювати за напрямком.
Як і попередній Metal-конвеєр, цю роботу виконала Kimi K3 — відкрита модель Moonshot, що працює на Fireworks: вимірювання, моделювання, код і перевірка. Людина надала скріншот, відповіла на одне запитання та оцінила відчуття на справжньому iPad.
Специфікація — це скріншот
Статичне зображення не може пояснити, чому штрих тонкий. Воно може показати лише, наскільки він тонкий і де саме. Тому першим кроком було вимірювання. Ми просканували скріншот рядок за рядком і стовпчик за стовпчиком, відстежили зміщення центральної лінії кожного штриха, щоб визначити його напрямок, а потім перетворили ширину сканування на справжню ширину через синус цього кута:
| Штрих | Напрямок | Справжня ширина |
|---|---|---|
| Верхній штрих «l», «pencilkit» | ≈ 78° | 27.3 px |
| Стрижень «k», «pencilkit» | ≈ 90° | 28 px |
| Лівий спадний рух 力 | ≈ 66° | 25.7 px |
| Курсивні з’єднання | ≈ 8° | 11 px |
| Китайські горизонталі (横) | ≈ 0° | 6–11 px |
Співвідношення товщини до тонкості ≈ 2.5. Підгонка
Перша версія: ширина з напрямку
Перша модель була очевидною: обчислювати напрямок для кожної вхідної точки, відображати напрямок у ширину через підігнану криву й записувати результат у радіус точки під час захоплення. Напрямок оцінювався причинно — як експоненційно згасаюче середнє з вагами для кількох останніх точок дуги; обчислення в просторі подвоєного кута не давало розвороту руху скасувати оцінку. Використовувалися лише минулі точки, тому поточний і зафіксований штрихи збігалися байт у байт.
Конструктор пройшов свій unit check: синтетичні горизонтальний, вертикальний, 45° і розворотний шляхи записали теоретичні ширини — 0.99, 2.52, 2.00 та 0.99 points. Для рендерингу не знадобилося жодного нового коду: штрих із зафіксованим радіусом — це просто ланцюг круглих відбитків, а наш point-sprite-конвеєр уже їх малював.
На iPad людині знадобилося близько десяти секунд, щоб відхилити його: «це художнє перо, а не водяна ручка».
Два пояснення підходять до однієї картини
Чому відчуття було неправильним? Було два можливі пояснення, а скріншот не міг їх розрізнити:
- Фіксація напрямку. Геометрія пера змушує горизонталі бути тонкими, а вертикалі — товстими, що б не робила рука. Саме це реалізувала перша версія.
- Тиск і швидкість. Ручка керується тиском, а візерунок зразка — лише динаміка рукопису: на спадних штрихах природно натискають сильніше, а з’єднання природно виходять швидкими й легкими.
Обидва пояснення дають скріншот із тонкими горизонталями та товстими вертикалями. Різниця в тому, що відбувається, коли сильно натиснути на горизонтальний штрих. Перша версія залишає його тонким. Ручка з керуванням тиском робить його товстішим. Тому ми поставили людині одне запитання: чи має горизонтальний штрих, на який сильно натиснули, стати товстішим?
«Ні. Горизонталі залишаються тонкими».
Фіксацію напрямку підтверджено. Але щось іще було не так, адже перша версія теж фіксувала напрямок.
Відповідь була на кінцях штрихів
Наступна підказка була на верхівках верхніх штрихів. Збільшення стрижнів «l» і «k» у зразку показало дві речі: прямий штрих зберігає одну ширину від початку до кінця, а кінці штрихів — плоскі діагональні зрізи — форма долотоподібного пера, яке відривається від паперу. Це не круглі крапки й не звуження від тиску.

Ось що означало «відчуття художнього пера». Перша версія моделювала вигляд зразка — ширину як функцію оціненого напрямку, — але не ручку. Оцінювач напрямку — це сенсор: він тремтить на зашумленому вводі, відстає на поворотах і заокруглює кожен кінець штриха в коло. У справжнього пера немає цих проблем, бо воно нічого не обчислює. Ширина — це геометрія.
Друга версія: еліптичне перо
У фінальній моделі взагалі немає оцінювача напрямку. Перо — це орієнтований еліпс: довга вісь горизонтальна, коротка фіксована. Відбитки цього еліпса щільно розміщуються вздовж шляху штриха, а все інше випливає з геометрії:
-
Горизонтальний штрих рухається вздовж відкритого краю, тому завжди має ширину
— постійну тонку лінію за будь-якого тиску. Це саме підтверджена специфікація. -
Вертикальний штрих перетинає всю довгу вісь:
, товстий край. -
Діагональний штрих отримує хордову ширину еліпса перпендикулярно до напрямку руху,
-
Кінці штрихів — еліптичні зрізи: плоскі кінці у формі пера зі зразка, які виникають без додаткової роботи.
-
Тиск масштабує лише довгу вісь,
, тож збільшує об’єм чорнила на спадних штрихах, але ніколи не потовщує горизонталь.
За вимірюваннями зразка ми зберегли
Для рендерингу знадобився лише один новий фрагментний шейдер. Формат вершин — позиція, діаметр, колір — уже містив усе необхідне: діаметр є довгою віссю, а коротка вісь — uniform для кожного проходу. Шейдер обчислює SDF еліпса з тим самим півпіксельним градієнтом покриття, що й наші круглі відбитки; саме це робить його порівнюваним на рівні пікселів з еталонною реалізацією Core Graphics (fillEllipse для кожного відбитка). Перевірка пройшла звичний етап: синтетичний набір із двома перовими штрихами відрендерили обома способами й порівняли піксель за пікселем — структурних відмінностей не виявлено, а вибраний для перевірки горизонтальний штрих в обох рендерерах мав 12 px.

Зліва перша версія, справа друга. Той самий рукописний ввід, той самий конвеєр. Кінці розповідають історію.
На iPad нове перо пройшло одразу: «好,很好» — добре. Дуже добре.
Зберегти промах
Першу версію не викинули. Це справді цікавий пензель, просто не водяна ручка. Тому її випустили як перший запис у новому меню експериментальних пензлів під назвою, яку їй дала людина: 漏水的圆珠笔, тобто ручка, що протікає. На панелі інструментів з’явилася кнопка з колбою, яка відкриває текстовий список експериментів; додавання наступного потребує одного рядка в реєстрі. Відхилена модель не є марною роботою, якщо її можна випустити як чітко позначений експеримент.
Один вечір циклів
Уся дуга — виміряти, змоделювати, збудувати, перенести на пристрій, запитати, перемоделювати, перебудувати, перевірити — тривала один вечір. Kimi K3 на Fireworks вела всю технічну частину циклу: розробляла сканування для вимірювань, пропонувала розрізнювальне запитання замість другої спроби вгадати, видалила власний оцінювач напрямку, коли докази змінилися, і розширила pixel-diff harness ще до того, як торкнутися застосунку. Швидкість висновків Fireworks зберігала інтерактивність циклу — довгі Metal- і Swift-дифи, скрипти аналізу пікселів та інструменти корпусу надходили достатньо швидко, щоб вузьким місцем залишалося людське судження.
Модель співпраці була такою самою, як у нашій публікації про Metal-конвеєр, і знову спрацювала: модель швидка й точна, а людина відповідає за смак і наскрізну перевірку. Одного речення відгуку про відчуття — «художнє перо, а не водяна ручка» — вистачило, щоб модель локалізувала точну помилку моделювання й замінила її простішим дизайном.
Правильна модель виявилася меншою за неправильну. Друга версія вийшла з меншою кількістю рухомих частин, ніж перша, — без оцінювача, без вікна згладжування, без підігнаного показника. Перо не обчислює ширину. Перо і є шириною.