Lulucat

Hvorfor vi erstattede tre geometrimotorer til overstregning med stempler

Gaoge ZhangGaoge Zhang

Data fra en ægte Apple Pencil afslørede fejl, som syntetiske tests overså, og førte Lulucat Notes fra tre konturmotorer til en MaLiang-stempelmodel.

Opdateret

Lulucat Notes begyndte med en konventionel overstregnings-renderer: opbyg en geometrisk kontur omkring den samplede bane, og udfyld derefter konturen én gang med en gennemskinnelig farve. En enkelt udfyldning undgår de mørkere bånd, der opstår, hvor gennemskinnelige segmenter overlapper. Den placerer også alle sammenføjninger, selvskæringer, endekapper og korte streger i det samme geometriproblem.

Tre generationer af specialtilpasset geometri fjernede hver sin type fejl og afdækkede samtidig en anden. Den renderer, vi sender med nu, bruger bløde cirkulære stempler baseret på MaLiang, et open source-tegnerammeværk af Harley-xk under MIT-licensen. Til gengæld for at fjerne en hel kategori af konturfejl accepterede vi mørkere selvskæringer og trykafsmalnede ender.

Blå overstregningshåndskrift optaget på en iPad, med tynde kroge synlige ved begyndelsen og slutningen af flere streger.

Det oprindelige skærmbillede fra enheden ved 228 % zoom. Små kroge ses i begge ender af flere streger.

Ægte håndskrift skabte kroge, som syntetiske scenarier ikke gjorde

Vores syntetiske scenarier dækkede lige linjer, buer, korte tryk, tilbagesporinger, trykændringer og skarpe sving, og alle blev gengivet rent. Ægte kinesisk håndskrift skabte stadig kroge på cirka 1–3 punkters længde i begyndelsen og slutningen af mange streger.

Derfor tilføjede vi en lagringsknap i appen og optog 29 komplette streger. Hvert målepunkt bevarede sin position, radius, tryk, azimut, hældning, tidsstempel og UIKit-estimeringsflag. Et offlineprogram afkodede den JSON og kompilerede de samme Stroke.swift, StrokeRenderer.swift og HighlighterStrokeBuilder.swift-filer, som appen bruger. Dets output matchede skærmbilledets streger punkt for punkt, inklusive krogene.

Da offline-rendereren genskabte fejlen alene ud fra de optagede inputdata, lå årsagen i målepunkterne og geometrien snarere end i visningen. Vi kunne nu aflæse de nøjagtige målepunkter, der var kommet ind i rendereren.

Vores første hypotese skyldte den estimerede Pencil-orientering ved nedslag og løft. Dataene afviste den. På tværs af alle 29 streger ændrede azimut sig højst 0.09 radianer og hældning højst 0.02 radianer inden for de første og sidste otte målepunkter, og alle estimeringsflag var false på den iPad. Orienteringen forblev stabil, mens tryk og retning ændrede sig brat.

En trykgrænse kom fra et tydeligt hul i dataene

Det sidste målepunkt i hver optaget streg havde trykket 0.000. Skrivepunkterne lå på eller over 0.01, mens punkter, der blev optaget efter at Pencil havde forladt overfladen, lå på eller under 0.002. Disse punkter i luften fortsatte ofte i 2–7 målepunkter og bevægede sig 0.8–9.0 punkter, og deres retning kunne dreje omtrent 90 grader væk fra den skrevne streg.

Streg 2 viser mekanismen. Dens krop sluttede mellem 143 og 180 grader. Halen med lavt tryk drejede derefter til −90 gennem −67 grader og bevægede sig yderligere 8.5 punkter. Kontur-rendereren behandlede det sidste punkt i luften som det autoritative slutpunkt, så den strakte et smalt stykke geometri hen mod dette punkt.

De målte tryk efterlader næsten en størrelsesorden mellem skrivning og bevægelse i luften, så vi lagde grænsen inde i dette hul:

private static let pressureFloor: CGFloat = 0.005

Kun målepunkter med lavt tryk i de to ender fjernes. Et målepunkt med lavt tryk midt i en streg forbliver en del af den. Reglen følger den målte hændelsesgrænse og lader den skrevne bane være uredigeret.

Instrumenteret nærbillede før trimning: grønne inputmålepunkter drejer væk fra stregen, mens den røde kontur danner en smal krog.

Før trimning forankrer rendereren endekappen til punkter i luften. Grønne prikker er rå målepunkter; den røde linje er den genererede kontur.

Instrumenteret nærbillede efter trimning: den blå udfyldning slutter ved de sidste skrivepunkter, og de grønne målepunkter med lavt tryk forbliver uden for den.

Efter trimning af målepunkter under 0.005 slutter endekappen ved det sidste skrevne punkt. Denne trykgrænse forbliver i den aktuelle stempel-renderer.

Grænsen fjernede de lange kroge i luften. Korte træk ved nedslag og buer ved løft, hvor trykket forblev over 0.005, overlevede den, fordi de er skrevne bevægelser. Håndteringen af disse bevægelser i en konturmodel førte til de næste geometrirunder.

Tre geometrimotorer flyttede fejlene rundt

Generation et: én kontur, én udfyldning

Den første trykfølsomme motor fulgte en velkendt pipeline. Den udglattede en midterlinje, beregnede venstre og højre forskydninger, samlede dem i én polygon og udfyldte polygonen én gang. Spidsen var en ellipse styret af Apple Pencilens azimut og hældning, så dens tværsnit ændrede sig med skriveretningen. Korte tryk havde en separat form, og flade endekapper brugte små asymmetrier for at undgå lommer med nul-omløb.

Den ene udfyldning løste opbygningen af opacitet inde i en streg, og den krævede en gyldig polygon i hvert sving. Træk ved nedslag skabte kiler mellem endekappen og kroppen. Tilbagesporinger kunne skabe hvide halvmåneformede lommer. Skarpe sving kunne få den indre kontur til at selvskære og efterlade et uudfyldt hak.

Generation to: reparér konturen nær enderne

Den anden motor beholdt konturen og tilføjede lokale reparationer: trimning af tilbagesporinger, dæmpning af tidligt estimerede egenskaber, opretning af slutpunkter, trimning af midlertidig retning og en lille forlængelse af endekappen. Hver rettelse håndterede en genskabt fejl. Tilsammen gjorde de endepunkternes adfærd afhængig af flere tærskler og vinduer.

Ægte skrivning gav derefter tilfælde uden for disse vinduer. Et fast opretningsvindue på 24 punkter slettede bevidste kroge i korte streger. Et adaptivt vindue bevarede disse kroge, og buer ved løft kunne stadig blive synlige hjørner. Retningsbevidst trimning fjernede mere midlertidig bevægelse, men kostede omkring 2.5 punkter af en reelt kurvet begyndelse i én optaget streg.

Hver rettelse løste det tilfælde, den var rettet mod. Modellen under dem krævede stadig en hård kontur, der forblev gyldig ved støjende, kurvet input.

Generation tre: dækningsunion med klippede firkantede endekapper

Den tredje motor holdt op med at konstruere én samlet kontur. Den tegnede en trapez for hvert segment og en skive ved hvert indre målepunkt i en gråtone-maske, tog unionen af deres dækning og anvendte derefter overstregningsfarven én gang. Halvmåneformede lommer fra tilbagesporinger, mørkfarvning ved selvskæringer og huller med nul-omløb forsvandt, fordi rendereren ikke længere afhang af polygonens omløbsretning.

Unionen rundede enderne, og vi genskabte firkantede endekapper ved at klippe dækningen mod to halvplaner ved slutpunkterne. Denne klipning løste lige linjer med konstant tryk og indførte samtidig en mere alvorlig fejl: kroppen i en kurvet streg kan legitimt krydse et halvplan ved slutpunktet, hvorefter endekapklipningen fjernede en del af midten af stregen. Under live-input blev slutpunktets retning hele tiden ændret, så det klippede område flyttede sig, og stregen flimrede.

En kurvet blå streg før rettelsen af endekapklipningen, hvor den øverste del af kroppen er skåret af langs en diagonal linje; grønne prikker viser hele den samplede bane.

Den globale endekapbegrænsning skærer gennem kroppen i en kurvet streg.

Den samme kurvede blå streg efter tilføjelse af en lokal buelængdebegrænsning, hvor begge grene er gendannet omkring den grønne samplede bane.

Ved kun at begrænse endekapklipningen til primitiver inden for én halvbredde fra slutpunktet gendannes denne streg.

En begrænsning baseret på buelængde gjorde endekapklipningen lokal og reparerede alle optagede tilfælde. En ekstrem syntetisk krog viste stadig et lille hvidt hak, hvor en spids med lavt tryk mødte den klippede endekappe. Den tredje motor havde igen byttet én geometrifejl ud med en anden.

MaLiang fjerner den hårde grænse

MaLiang bygger en streg af gentagne teksturstempler i stedet for én lukket kontur. Dens banegenerator bruger kvadratiske Bézier-segmenter, der går gennem midtpunkterne mellem nabomålepunkter, og dens linje-renderer placerer stempler med et regelmæssigt interval langs banen. Størrelse, rotation, farve og opacitet for et stempel kan alle variere langs stregen.

Et blødt stempel har ingen separat endekappe, sammenføjning eller omløbsregel. En skarp ændring i retning forbliver en sekvens af overlappende mærker. Tilbagesporinger kan ikke skabe en tom polygonlomme, og et halvplan ved slutpunktet kan ikke skære midten af en streg, fordi modellen ikke indeholder nogen klipning ved slutpunktet.

Vi tog modellen til os, ikke koden. Den aktuelle HighlighterStrokeBuilder udfører fire operationer:

  1. Fjern kun endemålepunkter, hvis tryk er under 0.005.
  2. Udglat positioner med kvadratiske Bézier-segmenter gennem midtpunkter.
  3. Placér ét cirkulært stempel for hvert punkt langs buelængden.
  4. Sæt stemplets diameter ud fra normaliseret tryk, og kompositér stemplerne med source-over-alfa.

Builderen faldt fra 686 linjer til 190. Azimut, hældning, konturopbygning, unionsmasker, retningsvinduer ved slutpunkter, endekapforlængelse og endekapklipning forlod alle overstregningsbanen. Vi registrerer stadig Pencil-orientering, fordi inputmodellen understøtter den, men selve overstregningen læser ikke længere disse felter.

MaLiangs trykformel krævede kalibrering af enheden

MaLiang dimensionerer et stempel med en formel svarende til

som antager, at force bruger en nyttig del af området fra 0 til 1. Det gjorde vores enhedsdata ikke. På tværs af omkring 3.400 Apple Pencil-målepunkter var mediantrykket 0.047, og maksimum var 0.178. Når vi sendte disse værdier direkte ind i formlen, blev normal skrivning meget tyndere end den nominelle penselstørrelse.

Derfor normaliserer vi omkring den observerede median, anvender eksponenten og begrænser resultatet:

Det mediane skrive-tryk mappes nu til den nominelle diameter på 12 punkter. Meget let kontakt forbliver synlig ved 35 % af den nominelle størrelse, mens hårdt tryk stopper væksten ved 120 %.

Denne kalibrering hører til inputenheden og penslens adfærd, ikke til MaLiang. Hvis vi havde kopieret den oprindelige formel uden at måle vores egen trykfordeling, ville vi have bevaret arkitekturen og fået den forkerte pensel.

Alfa pr. stempel kom fra det kendte antal overlapninger

MaLiang kompenserer for overlappende gennemskinnelige stempler med det empiriske udtryk alpha ÷ overlapping × 2.5. Vores pensel har en fast nominel diameter på 12 punkter og et stempelinterval på 1 punkt, så dens midterlinje modtager omkring 12 lag. I vores tilfælde er antallet af overlapninger kendt, og kompensationen kan løses i stedet for at blive estimeret.

For en ønsket stregopacitet og ens source-over-lag giver en stempelopacitet

så ét stempel kræver

Med og bruger hvert stempel en opacitet på cirka . Midten når den tilsigtede farve efter gentagen komposition, mens kanten modtager færre stempler og bevarer en blød overgang på én eller to punkter.

Et ark med varierede blå overstregningsstreger gengivet af den endelige model med bløde stempler, inklusive løkker, kroge, korte mærker og selvskæringer.

Den endelige renderer afspiller de optagede baner fra enheden. Kroge, diagonale snit og hvide lommer er fraværende.

De to omkostninger, vi accepterede

Selvskæringer bliver mørkere. Source-over-kompositionen tæller hvert stempel, inklusive stempler, der blev lagt ned af en tidligere passage gennem det samme område, og dækningsunionsmotoren havde undgået dette. Vi accepterede det mørkere overlap, fordi det er forudsigeligt og lader stregformen være intakt.

Endepunkterne bliver smallere med trykket. Et ægte løft reducerer trykket, hvilket reducerer stemplets diameter, så stregen tilspidses i stedet for at slutte med en endekappe i fuld bredde. Vi havde behandlet den firkantede endekappe som et krav gennem alle tre geometrimotorer. At droppe den fjernede klipningen og endepunktsmekanikken bag flere af fejlene ovenfor, så vi accepterede den tilspidsede ende.

Den model, vi sender med, gør derfor mindre end de tidligere modeller. Den bevarer den målte skrivehændelse, reagerer på tryk efter kalibrering af enheden og gengiver kurvede baner og baner, der krydser sig selv, uden at kræve en globalt gyldig kontur. En mejseludformet spids, en perfekt firkantet endekappe og den nøjagtige polylinje mellem rå målepunkter er de funktioner, vi gav afkald på for at opnå dette.

Af alt det, undersøgelsen frembragte, kan trykgrænsen lettest overføres til andre projekter. Værdien 0.005 kom fra afstanden mellem to klynger i ægte input: skrivning ved 0.01 og derover, sporing i luften ved 0.002 og derunder. De samme optagede streger viste, hvor hver geometrireparation fejlede. Disse fejl førte til stempelmodellen og de to omkostninger, den medfører.