Mengapa Kami Mengganti Tiga Mesin Geometri Highlighter dengan Stempel
Data Apple Pencil nyata mengungkap cacat yang luput dari pengujian sintetis, sehingga Lulucat Notes beralih dari tiga mesin kontur ke model stempel MaLiang.
Lulucat Notes bermula dengan renderer highlighter konvensional: membuat kontur geometris di sekitar jalur sampel, lalu mengisi kontur itu sekali dengan warna transparan. Satu kali pengisian mencegah pita yang lebih gelap di tempat segmen transparan saling tumpang tindih. Cara ini juga menempatkan setiap sambungan, perpotongan dengan diri sendiri, ujung, dan goresan pendek dalam masalah geometri yang sama.
Tiga generasi geometri kustom masing-masing menghilangkan satu jenis cacat dan memunculkan jenis lainnya. Renderer yang kini kami kirimkan menggunakan stempel lingkaran lembut berbasis MaLiang, framework menggambar open source berlisensi MIT karya Harley-xk. Sebagai imbalan karena menghapus seluruh kategori kegagalan kontur, kami menerima perpotongan dengan diri sendiri yang lebih gelap dan ujung yang meruncing mengikuti tekanan.

Tangkapan layar asli dari perangkat pada zoom 228%. Kait kecil muncul di kedua ujung beberapa goresan.
Tulisan tangan nyata menghasilkan kait yang tidak muncul dalam adegan sintetis
Adegan sintetis kami mencakup garis lurus, busur, ketukan singkat, gerakan kembali, perubahan tekanan, dan belokan tajam, dan semuanya dirender dengan bersih. Tulisan tangan Tionghoa nyata tetap menghasilkan kait sepanjang sekitar 1–3 titik di awal dan akhir banyak goresan.
Karena itu kami menambahkan tombol simpan ke aplikasi dan merekam 29 goresan lengkap. Setiap sampel menyimpan posisi, radius, tekanan, azimut, ketinggian, stempel waktu, dan flag estimasi UIKit. Program offline mendekode JSON itu dan mengompilasi file Stroke.swift, StrokeRenderer.swift, dan HighlighterStrokeBuilder.swift yang sama dengan yang digunakan aplikasi. Hasilnya cocok dengan tangkapan layar perangkat goresan demi goresan, termasuk kaitnya.
Karena renderer offline mereproduksi cacat hanya dari input yang direkam, penyebabnya berada pada sampel dan geometri, bukan pada perilaku tampilan. Kini kami dapat membaca sampel persis yang masuk ke renderer.
Hipotesis pertama kami menyalahkan orientasi Pencil yang diestimasi saat menyentuh dan meninggalkan permukaan. Data menolaknya. Di seluruh 29 goresan, azimut berubah paling banyak 0.09 radian dan ketinggian paling banyak 0.02 radian dalam delapan sampel pertama dan terakhir, dan setiap flag estimasi bernilai false di iPad itu. Orientasi tetap stabil, sementara tekanan dan arah berubah tajam.
Batas tekanan berasal dari celah data yang jelas
Sampel terakhir dari setiap goresan yang direkam memiliki tekanan 0.000. Sampel saat menulis berada pada atau di atas 0.01, sedangkan sampel yang direkam setelah Pencil meninggalkan permukaan berada pada atau di bawah 0.002. Sampel di udara itu sering berlanjut selama 2–7 titik dan menempuh 0.8–9.0 titik, dan arahnya dapat berbelok kira-kira 90 derajat menjauhi goresan tulisan.
Goresan 2 menunjukkan mekanismenya. Badannya berakhir di antara 143 dan 180 derajat. Ekor bertekanan rendah kemudian berbelok ke −90 hingga −67 derajat dan menempuh 8.5 titik lagi. Renderer kontur menganggap titik terakhir di udara sebagai endpoint yang menentukan, sehingga merentangkan bagian geometri yang sempit ke arah titik itu.
Tekanan yang terukur menyisakan jarak hampir satu orde magnitudo antara gerakan menulis dan gerakan di udara, jadi kami menempatkan cutoff di dalam celah tersebut:
private static let pressureFloor: CGFloat = 0.005
Hanya sampel bertekanan rendah di kedua ujung yang dihapus. Sampel bertekanan rendah di tengah goresan tetap menjadi bagian darinya. Aturan ini mengikuti batas peristiwa yang terukur dan tidak mengubah jalur tulisan.

Sebelum dipangkas, renderer menambatkan ujung pada sampel di udara. Titik hijau adalah sampel mentah; garis merah adalah kontur yang dihasilkan.

Setelah sampel di bawah 0.005 dipangkas, ujung berakhir di titik tulisan terakhir. Batas tekanan ini tetap digunakan oleh renderer stempel saat ini.
Batas ini menghapus kait panjang di udara. Seretan singkat saat menyentuh permukaan dan busur saat mengangkat Pencil yang tekanannya tetap di atas 0.005 tetap dipertahankan, karena keduanya adalah gerakan tulisan. Menangani gerakan itu di dalam model kontur membawa kami ke putaran geometri berikutnya.
Tiga mesin geometri hanya memindahkan lokasi cacat
Generasi pertama: satu kontur, satu pengisian
Mesin pertama yang peka terhadap tekanan mengikuti pipeline yang sudah umum. Mesin itu menghaluskan garis tengah, menghitung offset kiri dan kanan, menyatukannya menjadi satu poligon, lalu mengisi poligon itu sekali. Ujung pena berbentuk elips yang digerakkan azimut dan ketinggian Apple Pencil, sehingga penampangnya berubah mengikuti arah tulisan. Ketukan singkat memiliki bentuk terpisah, dan ujung datar menggunakan sedikit asimetri untuk menghindari kantong dengan winding nol.
Satu kali pengisian menyelesaikan akumulasi opasitas di dalam goresan, tetapi membutuhkan poligon yang valid di setiap belokan. Seretan saat menyentuh permukaan membuat irisan di antara ujung dan badan. Gerakan kembali dapat membuat kantong putih berbentuk bulan sabit. Belokan tajam dapat membuat kontur bagian dalam berpotongan dengan dirinya sendiri dan meninggalkan takik yang tidak terisi.
Generasi kedua: memperbaiki kontur di dekat ujungnya
Mesin kedua mempertahankan kontur dan menambahkan perbaikan lokal: pemangkasan gerakan kembali, peredaman untuk properti terestimasi di awal, pelurusan endpoint, pemangkasan arah transien, dan sedikit perpanjangan ujung. Setiap patch menangani kegagalan yang telah direproduksi. Jika digabungkan, perilaku endpoint menjadi bergantung pada beberapa ambang dan jendela.
Tulisan nyata kemudian menghasilkan kasus di luar jendela tersebut. Jendela pelurusan tetap sepanjang 24 titik menghapus kait yang memang disengaja pada goresan pendek. Jendela adaptif mempertahankan kait itu, tetapi busur saat mengangkat Pencil masih dapat berubah menjadi sudut yang terlihat. Pemangkasan yang memperhatikan arah menghapus lebih banyak gerakan transien, dengan mengorbankan sekitar 2.5 titik dari awal yang benar-benar melengkung pada satu goresan yang direkam.
Setiap patch memperbaiki kasus sasarannya. Model di bawahnya tetap membutuhkan kontur keras yang valid saat menerima input berisik dan melengkung.
Generasi ketiga: union cakupan dengan ujung persegi yang dipotong
Mesin ketiga berhenti membangun satu kontur. Mesin itu menggambar trapesium untuk setiap segmen dan cakram pada setiap sampel interior ke dalam mask skala abu-abu, mengambil union cakupannya, lalu menerapkan warna highlighter sekali. Bulan sabit pada gerakan kembali, penggelapan akibat perpotongan dengan diri sendiri, dan lubang winding nol menghilang karena renderer tidak lagi bergantung pada winding poligon.
Union tersebut membuat ujung menjadi bulat, lalu kami mengembalikan ujung persegi dengan memotong cakupan menggunakan dua half-plane endpoint. Pemotongan itu memperbaiki garis lurus bertekanan konstan, tetapi memperkenalkan cacat yang lebih serius: badan goresan melengkung dapat secara sah melintasi half-plane endpoint, dan pemotongan ujung kemudian menghapus bagian tengah goresan. Saat input langsung berlangsung, arah endpoint terus berubah, sehingga area yang dipotong berpindah dan goresan berkedip.

Batas ujung global memotong badan goresan yang melengkung.

Membatasi pemotongan ujung pada primitif yang berada dalam satu setengah lebar dari endpoint memulihkan goresan ini.
Gerbang panjang busur membuat pemotongan ujung menjadi lokal dan memperbaiki setiap kasus yang direkam. Kait sintetis ekstrem masih menunjukkan takik putih kecil ketika ujung bertekanan rendah bertemu dengan ujung yang dipotong. Mesin ketiga kembali menukar satu kegagalan geometri dengan kegagalan lainnya.
MaLiang menghilangkan batas keras
MaLiang membangun goresan dari stempel tekstur berulang, bukan satu kontur tertutup. Generator jalurnya menggunakan segmen Bézier kuadratik yang melewati titik tengah sampel bersebelahan, dan renderer garisnya menempatkan stempel pada jarak beraturan di sepanjang jalur itu. Ukuran, rotasi, warna, dan opasitas stempel semuanya dapat berubah sepanjang goresan.
Stempel lembut tidak memiliki ujung, sambungan, atau aturan winding terpisah. Perubahan arah yang tajam tetap menjadi rangkaian tanda yang saling tumpang tindih. Gerakan kembali tidak dapat membuat kantong poligon kosong, dan half-plane endpoint tidak dapat memotong bagian tengah goresan, karena model ini tidak memiliki pemotongan endpoint.
Kami mengadopsi modelnya, bukan kodenya. HighlighterStrokeBuilder saat ini melakukan empat operasi:
- Hapus hanya sampel endpoint yang tekanannya di bawah
0.005. - Haluskan posisi dengan segmen Bézier kuadratik titik tengah.
- Tempatkan satu stempel lingkaran untuk setiap titik panjang busur.
- Tetapkan diameter stempel dari tekanan yang dinormalisasi dan kompositkan stempel dengan alpha source-over.
Builder turun dari 686 baris menjadi 190. Azimut, ketinggian, konstruksi kontur, mask union, jendela arah endpoint, perpanjangan ujung, dan pemotongan ujung semuanya dikeluarkan dari jalur highlighter. Kami tetap merekam orientasi Pencil karena model input mendukungnya, tetapi highlighter itu sendiri tidak lagi membaca field tersebut.
Formula tekanan MaLiang memerlukan kalibrasi perangkat
MaLiang menentukan ukuran stempel dengan formula yang setara dengan
yang mengasumsikan bahwa force menggunakan bagian rentang 0 hingga 1 yang berguna. Data perangkat kami tidak demikian. Dari sekitar 3,400 sampel Apple Pencil, median tekanan adalah 0.047 dan maksimumnya 0.178. Memasukkan nilai itu langsung ke formula membuat tulisan normal jauh lebih tipis daripada ukuran kuas nominal.
Karena itu kami menormalisasi di sekitar median yang teramati, lalu menerapkan eksponen dan membatasi hasilnya:
Tekanan tulisan median kini dipetakan ke diameter nominal 12 poin. Sentuhan yang sangat ringan tetap terlihat pada 35% ukuran nominal, sementara tekanan berat berhenti membesar pada 120%.
Kalibrasi ini terkait dengan perangkat input dan perilaku kuas, bukan dengan MaLiang. Menyalin formula asli tanpa mengukur distribusi tekanan kami sendiri akan mempertahankan arsitekturnya, tetapi menghasilkan kuas yang salah.
Alpha per stempel berasal dari jumlah tumpang tindih yang diketahui
MaLiang mengompensasi stempel transparan yang tumpang tindih dengan ekspresi empiris alpha ÷ overlapping × 2.5. Kuas kami memiliki diameter nominal tetap 12 poin dan langkah stempel 1 poin, sehingga garis tengahnya menerima sekitar 12 lapisan. Dalam kasus kami, jumlah tumpang tindih diketahui, sehingga kompensasinya dapat dihitung, bukan diperkirakan.
Untuk opasitas goresan target
jadi satu stempel memerlukan
Dengan

Renderer final memutar ulang jalur perangkat yang direkam. Kait, potongan diagonal, dan kantong putih tidak ada.
Dua biaya yang kami terima
Perpotongan dengan diri sendiri menjadi lebih gelap. Komposit source-over menghitung setiap stempel, termasuk stempel yang diletakkan oleh lintasan sebelumnya di area yang sama, sementara mesin union cakupan menghindari hal itu. Kami menerima tumpang tindih yang lebih gelap karena hasilnya dapat diprediksi dan mempertahankan bentuk goresan.
Ujung menyempit mengikuti tekanan. Pengangkatan Pencil yang nyata mengurangi tekanan, sehingga diameter stempel berkurang dan goresan meruncing, bukan berakhir dengan ujung persegi selebar penuh. Kami sebelumnya memperlakukan ujung persegi itu sebagai persyaratan dalam ketiga mesin geometri. Menghapusnya menyingkirkan mekanisme pemotongan dan endpoint yang menjadi penyebab beberapa kegagalan di atas, jadi kami menerima ujung yang meruncing.
Model yang kami kirimkan karenanya melakukan lebih sedikit daripada model-model sebelumnya. Model ini mempertahankan peristiwa tulisan yang terukur, merespons tekanan setelah kalibrasi perangkat, dan merender jalur melengkung serta jalur yang berpotongan dengan dirinya sendiri tanpa memerlukan kontur yang valid secara global. Ujung pena berbentuk pahat, ujung persegi sempurna, dan polilin yang tepat di antara sampel mentah adalah fitur yang kami lepaskan untuk itu.
Batas tekanan adalah bagian yang paling mudah diterapkan ke proyek lain. Nilai 0.005 berasal dari celah antara dua kelompok dalam input nyata: tulisan pada 0.01 dan di atasnya, pelacakan di udara pada 0.002 dan di bawahnya. Goresan yang sama-sama direkam menunjukkan tempat setiap perbaikan geometri gagal. Kegagalan itu membawa kami ke model stempel dan dua biaya yang menyertainya.