Proč jsme tři geometrické enginy zvýrazňovače nahradili razítky
Skutečná data z Apple Pencil odhalila vady přehlédnuté syntetickými testy a přivedla Lulucat Notes od tří obrysových enginů k modelu razítek MaLiang.
Lulucat Notes začal konvenčním vykreslovačem zvýrazňovače: kolem vzorkované trajektorie vytvořit geometrický obrys a pak ho jednou vyplnit průsvitnou barvou. Jediná výplň zabraňuje tmavším pásům, které vznikají v místech překrytí průsvitných segmentů. Zároveň dostává každé spojení, vlastní průsečík, zakončení i krátký tah do stejného geometrického problému.
Tři generace vlastní geometrie vždy odstranily jednu třídu vad a odhalily jinou. Vykreslovač, který nyní dodáváme, používá měkká kruhová razítka založená na MaLiang, open-source frameworku pro kreslení od Harley-xk s licencí MIT. Výměnou za odstranění celé kategorie selhání obrysu jsme přijali tmavší vlastní průsečíky a konce zužující se podle tlaku.

Původní snímek obrazovky zařízení při 228% zvětšení. Na obou koncích několika tahů se objevují malé háčky.
Skutečný rukopis vytvářel háčky, které syntetické scény neukázaly
Naše syntetické scény pokrývaly přímé čáry, oblouky, krátké dotyky, návraty, změny tlaku a ostré zatáčky a všechny se vykreslily čistě. Skutečné čínské písmo přesto na začátku a konci mnoha tahů vytvářelo háčky dlouhé asi 1–3 body.
Proto jsme do aplikace přidali tlačítko pro uložení a zaznamenali 29 úplných tahů. Každý vzorek zachoval svou polohu, poloměr, tlak, azimut, výšku, časové razítko a příznaky odhadu z UIKit. Offline program dekódoval tento JSON a zkompiloval stejné soubory Stroke.swift, StrokeRenderer.swift a HighlighterStrokeBuilder.swift, které používá aplikace. Jeho výstup odpovídal snímku obrazovky zařízení tah po tahu, včetně háčků.
Protože offline vykreslovač reprodukoval vadu pouze ze zaznamenaných vstupů, spočívala příčina ve vzorcích a geometrii, nikoli v chování displeje. Nyní jsme mohli číst přesné vzorky, které do vykreslovače vstoupily.
Naše první hypotéza svalovala vinu na odhadovanou orientaci Pencil při dotyku a odlepení. Data ji vyvrátila. Ve všech 29 tazích se azimut během prvních a posledních osmi vzorků změnil nejvýše o 0.09 radiánu a výška nejvýše o 0.02 radiánu; na tomto iPadu byl každý příznak odhadu nastaven na false. Orientace zůstávala stabilní, zatímco tlak a směr se měnily prudce.
Prahová hodnota tlaku vzešla z čisté mezery v datech
Poslední vzorek každého zaznamenaného tahu měl tlak 0.000. Vzorky při psaní měly hodnotu 0.01 nebo vyšší, zatímco vzorky zaznamenané poté, co Pencil opustil povrch, měly hodnotu 0.002 nebo nižší. Tyto vzorky ve vzduchu často pokračovaly 2–7 bodů a urazily 0.8–9.0 bodu; jejich směr se mohl od psaného tahu odchýlit zhruba o 90 stupňů.
Tah 2 ukazuje mechanismus. Jeho tělo končilo mezi 143 a 180 stupni. Ocas s nízkým tlakem se pak stočil z −90 na −67 stupňů a urazil dalších 8.5 bodu. Vykreslovač obrysu považoval poslední vzorek ve vzduchu za směrodatný koncový bod, a proto k němu protáhl úzký kus geometrie.
Naměřené tlaky ponechávají mezi psaním a pohybem ve vzduchu téměř řádový rozdíl, takže jsme mez umístili doprostřed této mezery:
private static let pressureFloor: CGFloat = 0.005
Odstraňují se pouze vzorky s nízkým tlakem na obou koncích. Vzorek s nízkým tlakem uprostřed tahu zůstává jeho součástí. Toto pravidlo sleduje naměřenou hranici události a ponechává psanou trajektorii beze změny.

Před ořezáním vykreslovač kotví zakončení k vzorkům ve vzduchu. Zelené tečky jsou surové vzorky; červená čára je vygenerovaný obrys.

Po ořezání vzorků pod 0.005 končí zakončení u posledního psaného bodu. Tato prahová hodnota tlaku zůstává i v současném vykreslovači s razítky.
Prahová hodnota odstranila dlouhé háčky ve vzduchu. Krátké tahy při dotyku a oblouky při odlepení, jejichž tlak zůstal nad 0.005, přežily, protože jde o psaný pohyb. Zpracování těchto pohybů uvnitř modelu obrysu vedlo k dalším kolům úprav geometrie.
Tři geometrické enginy jen přesouvaly vady
První generace: jeden obrys, jedna výplň
První engine citlivý na tlak následoval známý postup. Vyhladil středovou křivku, vypočítal levé a pravé odsazení, spojil je do jediného polygonu a tento polygon jednou vyplnil. Hrot byl elipsa řízená azimutem a výškou Apple Pencil, takže jeho průřez se měnil podle směru psaní. Krátké dotyky měly samostatný tvar a plochá zakončení používala malé asymetrie, aby se vyhnula kapsám s nulovým číslem vinutí.
Jediná výplň vyřešila hromadění krytí uvnitř tahu a vyžadovala platný polygon při každé zatáčce. Tahy při dotyku vytvářely mezi zakončením a tělem klíny. Návraty mohly vytvářet bílé srpkovité kapsy. Ostré zatáčky mohly způsobit vlastní průsečík vnitřního obrysu a zanechat nevyplněný zářez.
Druhá generace: oprava obrysu poblíž konců
Druhý engine zachoval obrys a přidal lokální opravy: ořezávání návratů, tlumení časně odhadovaných vlastností, narovnávání konců, ořezávání přechodného směru a malé prodloužení zakončení. Každá záplata řešila reprodukovanou vadu. Dohromady však způsobily, že chování konců záviselo na několika prahových hodnotách a oknech.
Skutečné psaní pak přineslo případy mimo tato okna. Pevné okno narovnání o délce 24 bodů vymazalo záměrné háčky v krátkých tazích. Adaptivní okno tyto háčky zachovalo, ale oblouky při odlepení se stále mohly změnit ve viditelné rohy. Ořezávání zohledňující směr odstranilo více přechodného pohybu, ovšem za cenu zahození asi 2.5 bodu skutečně zakřiveného začátku v jednom zaznamenaném tahu.
Každá záplata vyřešila svůj cílový případ. Model pod nimi ale stále vyžadoval tvrdý obrys, který zůstane platný i při zašuměném a zakřiveném vstupu.
Třetí generace: sjednocení pokrytí s oříznutými čtvercovými zakončeními
Třetí engine přestal vytvářet jediný obrys. Pro každý segment vykreslil lichoběžník a u každého vnitřního vzorku disk do šedotónové masky, vzal sjednocení jejich pokrytí a pak jednou aplikoval barvu zvýrazňovače. Srpkovité návraty, tmavnutí ve vlastních průsečících a díry s nulovým číslem vinutí zmizely, protože vykreslovač už nezávisel na vinutí polygonu.
Sjednocení zaoblilo konce a my jsme obnovili čtvercová zakončení ořezáním pokrytí proti dvěma polorovinám u koncových bodů. Toto ořezání opravilo přímé čáry s konstantním tlakem a zavedlo závažnější vadu: tělo zakřiveného tahu může oprávněně překročit polorovinu koncového bodu, takže ořezání zakončení odstranilo část středu tahu. Během živého vstupu se směr konce stále měnil, oříznutá oblast se pohybovala a tah blikal.

Globální podmínka zakončení protíná tělo zakřiveného tahu.

Omezení ořezání zakončení na primitiva ve vzdálenosti nejvýše jedné poloviny šířky od koncového bodu tento tah obnoví.
Podmínka podle délky oblouku učinila ořezání zakončení lokálním a opravila každý zaznamenaný případ. Extrémní syntetický háček stále ukázal malý bílý zářez v místě, kde se hrot s nízkým tlakem setkal s oříznutým zakončením. Třetí engine znovu vyměnil jednu chybu geometrie za jinou.
MaLiang odstraňuje tvrdou hranici
MaLiang vytváří tah z opakovaných texturových razítek místo jediného uzavřeného obrysu. Jeho generátor trajektorie používá kvadratické Bézierovy segmenty, které procházejí středy sousedních vzorků, a jeho vykreslovač čáry umisťuje razítka v pravidelném kroku podél této trajektorie. Velikost, otočení, barva i krytí razítka se mohou v průběhu tahu měnit.
Měkké razítko nemá samostatné zakončení, spojení ani pravidlo vinutí. Prudká změna směru zůstává sekvencí překrývajících se značek. Návraty nemohou vytvořit prázdnou polygonovou kapsu a polorovina koncového bodu nemůže odříznout střed tahu, protože model žádné ořezání konce neobsahuje.
Převzali jsme model, ne kód. Současný HighlighterStrokeBuilder provádí čtyři operace:
- Odstraní pouze koncové vzorky, jejichž tlak je pod
0.005. - Vyhladí polohy pomocí středových kvadratických Bézierových segmentů.
- Umístí jedno kruhové razítko na každý bod podél délky oblouku.
- Nastaví průměr razítka z normalizovaného tlaku a složí razítka pomocí alfa kompozice source-over.
Builder klesl ze 686 řádků na 190. Azimut, výška, konstrukce obrysu, sjednocovací masky, okna směru koncových bodů, prodloužení zakončení i ořezávání zakončení z cesty zvýrazňovače zmizely. Orientaci Pencil stále zaznamenáváme, protože ji vstupní model podporuje, ale samotný zvýrazňovač tato pole už nečte.
Tlakový vzorec v MaLiang vyžadoval kalibraci zařízení
MaLiang určuje velikost razítka vzorcem ekvivalentním
který předpokládá, že force využívá užitečnou část rozsahu od 0 do 1. Naše data ze zařízení nikoli. Z přibližně 3 400 vzorků z Apple Pencil byl medián tlaku 0.047 a maximum 0.178. Když jsme tyto hodnoty dosadili přímo do vzorce, běžné psaní bylo mnohem tenčí než nominální velikost štětce.
Proto tlak normalizujeme kolem pozorovaného mediánu, poté aplikujeme exponent a výsledek omezíme:
Medián tlaku při psaní se nyní mapuje na nominální průměr 12 bodů. Velmi lehký dotyk zůstává viditelný při 35 % nominální velikosti, zatímco silný tlak přestane růst na 120 %.
Tato kalibrace patří ke vstupnímu zařízení a chování štětce, nikoli k MaLiang. Kdybychom zkopírovali původní vzorec bez změření vlastního rozložení tlaku, zachovali bychom architekturu, ale vyrobili nesprávný štětec.
Krytí jednotlivých razítek vycházelo ze známého počtu překrytí
MaLiang kompenzuje překrývající se průsvitná razítka empirickým výrazem alpha ÷ overlapping × 2.5. Náš štětec má pevný nominální průměr 12 bodů a krok razítek 1 bod, takže jeho středová linie dostává přibližně 12 vrstev. V našem případě je počet překrytí známý a kompenzaci lze vyřešit, nikoli odhadovat.
Pro cílové krytí tahu
takže jedno razítko vyžaduje
Při

Finální vykreslovač přehrává zaznamenané trajektorie ze zařízení. Háčky, diagonální řezy i bílé kapsy chybí.
Dvě přijaté ceny
Vlastní průsečíky tmavnou. Alfa kompozice source-over započítá každé razítko, včetně razítek položených při dřívějším průchodu stejnou oblastí, čemuž se engine sjednocení pokrytí vyhýbal. Tmavší překrytí jsme přijali, protože je předvídatelné a zachovává tvar tahu.
Konce se s tlakem zužují. Skutečné odlepení snižuje tlak, a tím i průměr razítka, takže se tah zužuje místo toho, aby končil plnošířkovým čtvercovým zakončením. Toto čtvercové zakončení jsme považovali za požadavek ve všech třech geometrických enginech. Jeho odstraněním zmizelo ořezání a stroje pro práci s konci, které stály za několika výše popsanými vadami, takže jsme zúžený konec přijali.
Model, který dodáváme, proto dělá méně než ty předchozí. Zachovává naměřenou událost psaní, po kalibraci zařízení reaguje na tlak a vykresluje zakřivené i samoprotínající se trajektorie bez potřeby globálně platného obrysu. Hrot ve tvaru dláta, dokonale čtvercové zakončení a přesná lomená čára mezi surovými vzorky jsou funkce, kterých jsme se za to vzdali.
Ze všeho, co šetření přineslo, je prahová hodnota tlaku nejsnáze přenositelná do dalších projektů. Hodnota 0.005 vzešla z mezery mezi dvěma shluky ve skutečném vstupu: psaní na hodnotě 0.01 a výše a sledování ve vzduchu na hodnotě 0.002 a níže. Stejné zaznamenané tahy ukázaly, kde selhala každá oprava geometrie. Tato selhání vedla k modelu razítek a ke dvěma cenám, které s sebou nese.