Két radír, és miért a vonáshoz tartozik a törlés
A saját elemként kezelt radírvonás kézenfekvő megoldás. Az objektumok mozgatása megtöri, ezért a Lulucat Notesban a törlés ahhoz a vonáshoz tartozik, amelynek a tintáját eltávolítja.
Amikor egy jegyzetfüzetben radíroznak, az emberek két különböző dolgot érthetnek alatta. Néha egy jelölés hibás, és az egésznek mennie kell: egy segédvonalnak, egy áthúzott szónak vagy egy pihenő tenyér által hagyott kósza vonásnak. Máskor csak egy jelölés egy része hibás: egy karakter fele, egy diagram egyik ága vagy egy mondaton túlfutott kiemelőhúzás vége. A Lulucat Notes most mindkettőt kínálja, két külön eszközként.
A nehezebb tervezési kérdés az, hogy hová tartozik a törlés.
A vonásradír volt az első
Az első radír egész vonásokat törölt. Egy érintés minden vonást megvizsgál: a befoglaló téglalapok szinte mindet elutasítják, a megmaradókon pedig a radír sugarához és a szakasz mentén interpolált vonássugárhoz képest számított pont–szakasz négyzetes távolságvizsgálat fut. A szélesség a nyomással változik, ezért egyetlen névleges szélesség vékony, meg nem érintett vonásokat is törölne, a megérintett vastagokat pedig kihagyná.
Egy mozdulat egyetlen visszavonási bejegyzéssé olvad össze, és a bejegyzés minden eltávolított vonást az eredeti indexével együtt tárol. A visszavonás fordított sorrendben illeszti vissza őket, így a rétegzési sorrend pontosan visszaáll.
Ez a radír gyors, kiszámítható, és képtelen egy szó felét eltávolítani.
A MaLiang ecsetté teszi a radírt
A MaLiang, az MIT-licencű rajzolási keretrendszer, amelytől már a kiemelő stamp-modelljét is átvettük, huszonnégy sorban megoldja a részleges törlést. Az Eraser egy Brush alosztálya, amely egyetlen dolgot, a keverési beállításokat írja felül, hogy összeadás helyett kivonjon. A radírvonás ugyanabban a listában szereplő elem, mint minden más vonás. A visszavonás ezt az elemet távolítja el. Egyetlen meglévő vonás sem módosul, mert a törlés csak az újrajátszás során, lista szerinti sorrendben történik.
Ezt a modellt szállítottuk, és két eszközös munkamenetben felmondta a szolgálatot.
Az objektum mozgatása felfedi a hiányzó tulajdonost
Az első hibajelzés így szólt: jelölj ki vonásokat a lasszóval, húzd el őket, és a lyuk ott marad mögöttük.

Ugyanazok a törölt vonások jobbra mozgatva. Fent: a radír önálló elem. Lent: a törlés a vonásokhoz tartozik.
A kijelölés még rosszabbá tette a helyzetet. A kijelölt vonások kiemelődnek a bitképből egy overlay-be, amely közvetlenül rajzolja ki őket, a radírelem nélkül, ezért amint egy törölt vonást kijelöltél, az újra egésznek látszott.
A második hibajelzés: a radír a kiemelő fölött lebegett. A renderelőnk két menetben játszik újra: először a kiemelőket, aztán a tollakat rajzolja ki, mert a kiemelő tintájának a toll tintája alatt kell maradnia. Egy radírelemnek mindkét menetben részt kell vennie, különben nem tudja mindkét fajtát törölni. Ha részt vesz a tollak menetében, az azt jelenti, hogy minden kiemelő kirajzolása után fut, így a radír az időben később megrajzolt kiemelővonásokat is kivágta.
Mindkét hiba ugyanonnan ered. A MaLiangben nincs lasszó, mozgatás és rétegsorrend az ecsetek között, ezért ott elegendő egy tulajdonos nélküli elem. A mi vásznunkban mindhárom megvan, és egy tulajdonos nélküli törlésre nincs meghatározott válasz arra, hogy „mi történik, amikor a tinta elmozdul”, illetve „melyik rétegen működik”.
A vonás tulajdonában lévő törlés
Mostantól minden Stroke a saját törlési útvonalait hordozza. A radír megvizsgálja, mely vonásokat érinti, és minden ilyen vonáshoz hozzáfűz egy, vászonkoordinátákban megadott útvonalat. Egy vonás renderelésekor a tintáját és a saját törléseit egy átlátszósági rétegen belül rajzoljuk ki:
context.beginTransparencyLayer(auxiliaryInfo: nil)
drawInk(stroke, in: context, clip: clip, colorOverride: colorOverride)
context.setBlendMode(.destinationOut)
for path in stroke.erasePaths { fillErasePath(path, in: context) }
context.endTransparencyLayer()
A destination-out megtartja a célterületet, és a forrás alfájával arányosan eltávolítja azt:
Egy átlátszósági rétegen belül a cél az az egy vonás, így a művelet nem érheti el a papírt, az alatta lévő vonásokat vagy a később kirajzolt vonásokat. A réteg a törléssel nem rendelkező vonásoknak semmibe sem kerül, márpedig ezek teszik ki szinte az összeset: ezek az eredeti útvonalat használják.
A tulajdonlás közvetlenül megválaszolja a két hibát. Az eltolás a törlési útvonalakat a mintapontokkal együtt mozgatja, ezért a mozgatott vonás magával viszi a réseit. A kijelölési overlay ugyanazt a Stroke objektumot rajzolja ki, ezért a kijelölt vonás ugyanazokat a réseket mutatja. A rétegsorrend többé nem számít, mert a törlés soha nem alkalmazható máson, csak a saját vonásán.
A visszavonás a mozdulaton dolgozik: egy bejegyzés felsorolja a mozdulat által létrehozott összes (stroke, path) párt, a visszavonás eltávolítja ezeket az útvonalakat, az újra végrehajtás pedig ismét hozzáfűzi őket. A törlés alatti tinta soha nem módosul, ezért semmit sem kell rekonstruálni.
Az élő visszajelzés a csempézett bitképen keresztül működik
A rajzolt vonást normál esetben a kompozitált oldal fölött lévő overlay rendereli. A törlés ott nem működhet, mert ez az overlay a papír fölött is elhelyezkedik, a destination-out pedig átütné a papírt.
Ezért a radír azonnal beleír a vonásokba, és minden új mintákból álló köteg csak az érintett téglalap helyi újrajátszását indítja el. A csempézett feldolgozási folyamat már most is töröl egy téglalapot, majd újrajátssza a benne metsző vonásokat; a radír ezt az utat használja újra, és nem vezet be második rajzolási szemantikát.
A két radír egymás mellett
Az eszköztárban két radírgomb van, nem egyetlen, rejtett móddal rendelkező gomb. A részleges radír egy vonás egy részét távolítja el. A vonásradír érintéskor egész vonásokat töröl. A sugaraik külön konstansok, és jelenleg mindkettő értéke négy pont.
Egy egész vonás törlése a hozzá tartozó törlési útvonalakat is magával viszi, mivel azok a vonás részei, a visszavonás pedig együtt állítja vissza őket. A törlési útvonalak a mentett fájl részei is, és minden máshoz hasonlóan kvantáljuk őket: a sugarat és a koordinátákat pont tizedeiben tároljuk.
Egy korlát továbbra is megmarad. A teljesen eltörölt vonás adatként még létezik. Nem renderelődik, de a lasszó továbbra is kijelölheti, a vonásradír pedig törölheti, és továbbra is helyet foglal a dokumentumban. A teljesen eltörölt vonások összegyűjtéséhez fedettségvizsgálatra lenne szükség, ezt még nem írtuk meg, ezért egyelőre megmaradnak.
Amikor egy másik projekt modellje nem működik a tiédben, a különbség általában nem a lemásolt kódban van. Hanem azokban a funkciókban, amelyekkel az a projekt nem rendelkezik.