Lulucat

दोन इरेझर, आणि पुसण्याची मालकी स्ट्रोककडे का असते

Gaoge ZhangGaoge Zhang

इरेझरला स्वतंत्र घटक मानणे स्वाभाविक डिझाइन आहे. पण ऑब्जेक्ट हलवल्यावर ते मोडते; म्हणून Lulucat Notes मध्ये ज्या स्ट्रोकमधून शाई काढली जाते, त्याच स्ट्रोककडे पुसण्याची मालकी असते.

अद्ययावत

नोटबुकमध्ये काही पुसताना लोक दोन वेगवेगळ्या गोष्टींपैकी एक अभिप्रेत ठेवतात. कधी एखादी खूण चुकीची असते आणि संपूर्ण खूणच काढून टाकायची असते: मार्गदर्शक रेषा, काट मारलेला शब्द, किंवा विसावलेल्या तळहातामुळे उमटलेला अनावश्यक स्ट्रोक. कधी खूणेचा फक्त काही भाग चुकीचा असतो: एखाद्या अक्षराचा अर्धा भाग, आकृतीची एक फांदी, किंवा वाक्य ओलांडून गेलेल्या हायलाइटर स्विपची शेपटी. Lulucat Notes मध्ये आता दोन्ही स्वतंत्र साधनांप्रमाणे उपलब्ध आहेत.

कठीण डिझाइन प्रश्न पिक्सेल कसे काढायचे हा नाही. पुसण्याची क्रिया नेमकी कुठे राहते हा आहे.

स्ट्रोक इरेझर आधी आला

पहिला इरेझर संपूर्ण स्ट्रोक्स हटवत असे. प्रत्येक टचमध्ये सर्व स्ट्रोक्स तपासले जातात: bounding boxes जवळजवळ सर्वांना नाकारतात, आणि उरलेल्यांवर इरेझरच्या त्रिज्येत त्या सेगमेंटवर इंटरपोलेट केलेली स्ट्रोकची त्रिज्या जोडून point-to-segment squared distance test केली जाते. रुंदी दाबानुसार बदलते, त्यामुळे एकच nominal width वापरली तर वापरकर्त्याने स्पर्शही न केलेले बारीक स्ट्रोक्स हटतील आणि स्पर्श केलेले जाड स्ट्रोक्स सुटतील.

एक gesture एका undo entry मध्ये एकत्रित होते. त्या entry मध्ये काढलेला प्रत्येक स्ट्रोक त्याच्या मूळ index सोबत साठवला जातो. Undo त्यांना उलट क्रमाने पुन्हा घालते, त्यामुळे stacking order अगदी आधीसारखा परत येतो.

हा इरेझर जलद आणि अंदाज करता येण्यासारखा आहे, पण अर्धा शब्द काढून टाकू शकत नाही.

MaLiang इरेझरला ब्रश बनवतो

MaLiang हे MIT-परवानाधारक drawing framework आहे; त्याच्याकडून आम्ही हायलाइटरचे stamp model आधीच घेतले होते. ते partial erasing चा उपाय 24 ओळींत देते. त्यातील Eraser हा Brush चा subclass आहे. तो फक्त एक गोष्ट—blend options—override करतो आणि जोडण्याऐवजी वजाबाकी करतो. इरेझर स्ट्रोक हा इतर प्रत्येक स्ट्रोकसोबत त्याच यादीतील एक element असतो. Undo केल्यावर तो element काढला जातो. विद्यमान स्ट्रोकमध्ये कोणताही बदल होत नाही, कारण erasure फक्त replay दरम्यान, यादीच्या क्रमाने घडते.

आम्ही ते model पाठवले, आणि दोन device sessions मध्ये ते मोडले.

ऑब्जेक्ट हलवल्यावर मालक नसल्याचे दिसते

पहिली नोंद अशी होती: lasso ने स्ट्रोक्स निवडा, त्यांना drag करा, आणि भोक मागेच राहते.

हलवण्यापूर्वी आणि नंतर इरेझरच्या दोन मॉडेल्सची तुलना करणारे चार पॅनेल. इरेझर स्वतंत्र घटक असल्यास पोकळी जुन्या जागी राहते; इरेझरची मालकी स्ट्रोककडे असल्यास पोकळी शाईसोबत हलते.

तेच पुसलेले स्ट्रोक्स उजवीकडे हलवलेले. वर: इरेझर स्वतःचा स्वतंत्र element आहे. खाली: पुसण्याची क्रिया स्ट्रोक्सच्या मालकीची आहे.

निवडीमुळे समस्या आणखी वाढली. निवडलेले स्ट्रोक्स बिटमॅपमधून overlay मध्ये उचलले जातात आणि तिथे इरेझर element शिवाय थेट काढले जातात. त्यामुळे पुसलेला स्ट्रोक निवडताच तो पुन्हा पूर्ण दिसू लागला.

दुसरी नोंद: इरेझर हायलाइटरच्या वर तरंगत होता. हायलाइटरची शाई इतर शाईच्या खाली असली पाहिजे, म्हणून आमचा renderer दोन passes मध्ये replay करतो—आधी highlighters आणि नंतर pens. इरेझर element ने दोन्ही passes मध्ये भाग घेतला पाहिजे, नाहीतर तो दोन्ही प्रकारची शाई पुसू शकत नाही. Pen pass मध्ये भाग घेणे म्हणजे प्रत्येक हायलाइटर काढून झाल्यानंतर इरेझर चालवणे; त्यामुळे काळाच्या क्रमाने नंतर काढलेले हायलाइटर स्ट्रोक्ससुद्धा इरेझरने कापले.

दोन्ही अपयशांचे मूळ एकच आहे. MaLiang मध्ये lasso, move किंवा brushes मधील layer order नाही, त्यामुळे मालक नसलेला element तिथे पुरेसा असतो. आमच्या canvas मध्ये ही तिन्ही वैशिष्ट्ये आहेत, आणि मालक नसलेल्या erasure साठी “शाई हलवल्यावर काय होते” किंवा “ही क्रिया कोणत्या layer वर चालते” याचे ठरलेले उत्तरच नाही.

पुसण्याची मालकी स्ट्रोककडे

आता प्रत्येक Stroke स्वतःचे erase paths सोबत बाळगतो. इरेझर ज्या स्ट्रोक्सना स्पर्शतो ते तपासतो आणि प्रत्येकामध्ये canvas coordinates मध्ये एक path जोडतो. एका स्ट्रोकचे rendering करताना त्याची ink आणि त्याची स्वतःची erasures एका transparency layer मध्ये काढल्या जातात:

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()

Destination-out destination जपते आणि source alpha च्या प्रमाणात ते काढून टाकते:

Transparency layer च्या आत destination हा तो एकच स्ट्रोक असतो, त्यामुळे ही क्रिया कागदापर्यंत, खालील स्ट्रोक्सपर्यंत किंवा नंतर काढल्या जाणाऱ्या स्ट्रोक्सपर्यंत पोहोचू शकत नाही. ज्या स्ट्रोक्सना erasures नाहीत त्यांच्यासाठी layer चा कोणताही खर्च नाही, आणि असे स्ट्रोक्स जवळजवळ सर्वच असतात: ते मूळ path वापरतात.

मालकीमुळे दोन्ही अपयशांना थेट उत्तर मिळते. Translation केल्यावर erase paths sample points सोबत हलतात, त्यामुळे हलवलेला स्ट्रोक आपली पोकळी सोबत घेऊन जातो. Selection overlay तोच stroke object काढतो, त्यामुळे निवडलेल्या स्ट्रोकमध्येही त्याच पोकळ्या दिसतात. Layer order महत्त्वाचा राहत नाही, कारण erasure स्वतःच्या स्ट्रोकखेरीज इतर कशावरही लागू होत नाही.

Undo gesture वर काम करते: एका entry मध्ये त्या gesture ने तयार केलेली प्रत्येक (stroke, path) जोडी असते; undo ते paths काढते आणि redo त्यांना पुन्हा जोडते. Erasure खालील ink कधीही बदलली जात नाही, त्यामुळे काहीही पुन्हा बांधून काढावे लागत नाही.

लाइव्ह फीडबॅक टाइल बिटमॅपमधून येतो

काढला जात असलेला स्ट्रोक सामान्यतः composited page च्या वर overlay म्हणून render केला जातो. Erasure तिथे काम करू शकत नाही, कारण तो overlay कागद आणि ink या दोन्हींच्या वर असतो आणि destination-out कागदालाही भेदून जाईल.

म्हणून इरेझर तात्काळ स्ट्रोक्समध्ये लिहितो, आणि नव्या samples च्या प्रत्येक batch मुळे फक्त प्रभावित rectangle चे स्थानिक replay सुरू होते. Tile pipeline आधीपासून rectangle साफ करून त्याला छेदणारे स्ट्रोक्स पुन्हा replay करते; इरेझर तोच मार्ग वापरतो आणि दुसरी drawing semantic जोडत नाही.

दोन्ही इरेझर शेजारी शेजारी

Toolbar मध्ये लपलेल्या mode असलेल्या एका बटणाऐवजी दोन इरेझर बटणे आहेत. Partial eraser स्ट्रोकचा काही भाग काढतो. Stroke eraser संपर्क होताच संपूर्ण स्ट्रोक्स काढतो. त्यांच्या radii स्वतंत्र constants आहेत आणि सध्या दोन्ही 4 points आहेत.

संपूर्ण स्ट्रोक delete केल्यावर त्याचे erase paths सुद्धा त्याच्यासोबत जातात, कारण ते त्या स्ट्रोकचा भाग आहेत; undo दोन्ही एकत्र परत आणते. Erase paths saved file चा भागसुद्धा असतात: radius आणि coordinates बाकी सर्व गोष्टींप्रमाणे point च्या दहाव्या भागांत quantize केले जातात.

एक मर्यादा अजून आहे. पूर्णपणे पुसून टाकलेला स्ट्रोक data म्हणून अस्तित्वात राहतो. तो काहीही render करत नाही, पण lasso अजूनही तो निवडू शकतो, stroke eraser अजूनही तो delete करू शकतो आणि तो document मध्ये जागा व्यापत राहतो. पूर्णपणे पुसलेले स्ट्रोक्स गोळा करण्यासाठी coverage test आवश्यक आहे; तो आम्ही अजून लिहिलेला नाही, म्हणून सध्या ते तसेच राहतात.

दुसऱ्या प्रकल्पातील एखादे मॉडेल तुमच्या प्रकल्पात काम करणे थांबवते, तेव्हा फरक सहसा तुम्ही कॉपी केलेल्या कोडमध्ये नसतो. तो त्या प्रकल्पात नसलेल्या वैशिष्ट्यांमध्ये असतो.