डेटा भंडारण के लिए बिंदुओं में 2 डी वक्र परिवर्तित करना


12

मैंने एक एल्गोरिथ्म बनाया है जो किसी भी वक्र अर्थात पथ को न्यूनतम अंकों में परिवर्तित करता है ताकि मैं इसे फ़ाइल या डेटाबेस में सहेज सकूं।

विधि सरल है: यह तीन चरणों को समान चरणों में ले जाता है और इन बिंदुओं के बीच के कोण को मापता है। यदि कोण सहिष्णुता से बड़ा है तो यह उस बिंदु पर एक नया घन वक्र बनाता है। फिर यह लाइनों को आगे बढ़ाता है और कोण को फिर से मापता है ...

जो लोग Android Path Class जानते हैं उनके लिए - ध्यान दें कि dstPath एक कस्टम क्लास है, जो पॉइंट्स को एक Array में रिकॉर्ड करता है ताकि मैं बाद में पॉइंट्स बचा सकूँ , जबकि srcPath एक रीजन यूनियन का परिणाम है और इसलिए मेरे लिए कोई महत्वपूर्ण बिंदु नहीं है बचाना।

समस्या यह है कि सर्कल सुचारू नहीं दिखता जैसा कि आप इस छवि में देख सकते हैं, नीचे दिए गए कोड द्वारा उत्पादित किया गया है, जहां स्रोत पथ में एक पूर्ण सर्कल और आयत शामिल हैं। मैंने सहिष्णुता के कोण और कदम की लंबाई को बदलने की कोशिश की, लेकिन कुछ भी मदद नहीं करता है। मुझे आश्चर्य है कि अगर आप इस एल्गोरिथ्म, या एक अलग दृष्टिकोण के लिए किसी भी सुधार का सुझाव दे सकते हैं।

EDIT: मैंने अब एंड्रॉइड जावा का उपयोग करने वालों के लिए पूरा कोड पोस्ट किया है, ताकि वे आसानी से कोशिश कर सकें और प्रयोग कर सकें।

यहाँ छवि विवरण दर्ज करें

public class CurveSavePointsActivity extends Activity{

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(new CurveView(this));
    }

    class CurveView extends View{

        Path srcPath, dstPath;
        Paint srcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        public CurveView(Context context) {
            super(context);

            srcPaint.setColor(Color.BLACK);
            srcPaint.setStyle(Style.STROKE);
            srcPaint.setStrokeWidth(2);
            srcPaint.setTextSize(20);

            dstPaint.setColor(Color.BLUE);
            dstPaint.setStyle(Style.STROKE);
            dstPaint.setStrokeWidth(2);
            dstPaint.setTextSize(20);

            srcPath = new Path();
            dstPath = new Path();

        }

        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);

            //make a circle path
            srcPath.addCircle(w/4, h/2, w/6 - 30, Direction.CW);

            //make a rectangle path
            Path rectPath = new Path();
            rectPath.addRect(new RectF(w/4, h/2 - w/16, w*0.5f, h/2 + w/16), Direction.CW);


            //create a path union of circle and rectangle paths
            RectF bounds = new RectF();
            srcPath.computeBounds(bounds, true);
            Region destReg = new Region();
            Region clip = new Region();
            clip.set(new Rect(0,0, w, h));
            destReg.setPath(srcPath, clip);
            Region srcReg = new Region();
            srcReg.setPath(rectPath, clip); 
            Region resultReg = new Region();
            resultReg.op(destReg, srcReg, Region.Op.UNION);
            if(!resultReg.isEmpty()){
                srcPath.reset();
                srcPath.addPath(resultReg.getBoundaryPath());
            }

            //extract a new path from the region boundary path
            extractOutlinePath();

            //shift the resulting path bottom left, so they can be compared
            Matrix matrix = new Matrix();
            matrix.postTranslate(10, 30);
            dstPath.transform(matrix);

        }

         @Override 
            public void onDraw(Canvas canvas) { 
                super.onDraw(canvas);    
                canvas.drawColor(Color.WHITE);
                canvas.drawPath(srcPath, srcPaint);
                canvas.drawPath(dstPath, dstPaint);

                canvas.drawText("Source path", 40, 50, srcPaint);
                canvas.drawText("Destination path", 40, 100, dstPaint);
         }


         public void extractOutlinePath() {

             PathMeasure pm = new PathMeasure(srcPath, false); //get access to curve points

             float p0[] = {0f, 0f}; //current position of the new polygon
             float p1[] = {0f, 0f}; //beginning of the first line
             float p2[] = {0f, 0f}; //end of the first & the beginning of the second line
             float p3[] = {0f, 0f}; //end of the second line

             float pxStep = 5; //sampling step for extracting points
             float pxPlace  = 0; //current place on the curve for taking x,y coordinates
             float angleT = 5; //angle of tolerance

             double a1 = 0; //angle of the first line
             double a2 = 0; //angle of the second line

             pm.getPosTan(0, p0, null); //get the beginning x,y of the original curve into p0
             dstPath.moveTo(p0[0], p0[1]); //start new path from the beginning of the curve
             p1 = p0.clone(); //set start of the first line

             pm.getPosTan(pxStep, p2, null); //set end of the first line & the beginning of the second

             pxPlace = pxStep * 2;
             pm.getPosTan(pxPlace, p3, null); //set end of the second line


             while(pxPlace < pm.getLength()){
             a1 = 180 - Math.toDegrees(Math.atan2(p1[1] - p2[1], p1[0] - p2[0])); //angle of the first line
             a2 = 180 - Math.toDegrees(Math.atan2(p2[1] - p3[1], p2[0] - p3[0])); //angle of the second line

             //check the angle between the lines
             if (Math.abs(a1-a2) > angleT){

               //draw a straight line to the first point if the current p0 is not already there
               if(p0[0] != p1[0] && p0[1] != p1[1]) dstPath.quadTo((p0[0] + p1[0])/2, (p0[1] + p1[1])/2, p1[0], p1[1]);

               dstPath.quadTo(p2[0] , p2[1], p3[0], p3[1]); //create a curve to the third point through the second

               //shift the three points by two steps forward
               p0 = p3.clone();
               p1 = p3.clone();
               pxPlace += pxStep;
               pm.getPosTan(pxPlace, p2, null); 
               pxPlace += pxStep;
               pm.getPosTan(pxPlace, p3, null);
               if (pxPlace > pm.getLength()) break;
             }else{
               //shift three points by one step towards the end of the curve
               p1 = p2.clone(); 
               p2 = p3.clone();
               pxPlace += pxStep;
               pm.getPosTan(pxPlace, p3, null); 
             }
             }
             dstPath.close();
         }
    }

}

यहाँ मूल और मेरे एल्गोरिथ्म के बीच एक तुलना है:

रास्तों के बीच तुलना;  व्युत्पन्न पर उल्लेखनीय रूप से चिकनी कोने


बी-स्प्लिन का उपयोग क्यों नहीं किया जाता है?
ग्रिफिनहार्ट

4
यदि आप जानते हैं कि बात एक वृत्त और आयत है, तो एक वृत्त और एक आयत क्यों नहीं संग्रहीत करें? और सामान्यीकृत रूप में - जो कुछ भी इनपुट आपकी चीज़ उत्पन्न करता है वह संभवतः इसे स्टोर करने के लिए एक उचित प्रारूप है। यदि आप एक संपीड़न योजना की तलाश कर रहे हैं जो एक अलग प्रश्न की तरह लगता है (या कम से कम हमें स्रोत डेटा के बारे में बहुत अधिक जानकारी की आवश्यकता होगी सहायक होना)।
जेफ गेट्स

यह किसी भी अप्रत्याशित आकार का हो सकता है जैसा कि मैंने पहले वाक्य में कहा था - यहां सर्कल और रेक्टैंग केवल एक परीक्षण उदाहरण हैं।
लुमिस

@ लुमिस आपको वास्तव में बी-स्प्लिन में देखना चाहिए, यह वही है जिसके लिए वे हैं। अपने स्वयं के समाधान को लागू करने का प्रयास करने का कोई कारण?
ग्रिफिनहार्ट

1
अच्छी तरह से पथ वर्ग स्प्लिन के साथ उन वक्रों का निर्माण करेगा ताकि आप पहले से ही इसका उपयोग कर रहे हैं। मेरे पास एक और सुझाव है, कम गणित उन्मुख: अंक बचाने के बजाय, उपयोगकर्ता इनपुट (कमांड पैटर्न) को सहेजें और उसी "छवि" के निर्माण के लिए इसे फिर से चलाएँ।
ग्रिफिनहार्ट

जवाबों:


6

मुझे लगता है कि आपको दो समस्याएं हैं:

गैर-सममितीय नियंत्रण बिंदु

प्रारंभ में आप p0 से p1 और p1 से P2 के बीच समान दूरी से शुरू करते हैं। यदि लाइन खंडों के बीच सहिष्णुता कोण पूरा नहीं हुआ है, तो आप p1 और P2 को आगे बढ़ाते हैं, लेकिन p0 को वहीं रखें जहां यह था। यह p0 से p1 के बीच की दूरी को बढ़ाता है जबकि p1 से P2 के बीच की दूरी समान रखता है। जब आप नियंत्रण बिंदु के रूप में p1 का उपयोग करके एक वक्र बनाते हैं, तो यह अंतिम वक्र के बाद कितने पुनरावृत्तियों से गुजरा है, इसके आधार पर P2 के लिए भारी पक्षपाती हो सकता है। यदि आप पी 1 की तुलना में दोगुनी मात्रा में पी 2 स्थानांतरित करेंगे, तो आपको अंकों के बीच भी दूरी मिलेगी।

द्विघात वक्र

जैसा कि अन्य उत्तरों में भी उल्लेख किया गया है, द्विघात वक्र इस मामले के लिए बहुत अच्छा नहीं है। आपके द्वारा बनाए जाने वाले आसन्न घटता एक नियंत्रण बिंदु और एक स्पर्शरेखा साझा करना चाहिए । जब आपका इनपुट डेटा सिर्फ अंक होता है, तो Catmull-Rom Spline उस उद्देश्य के लिए एक अच्छा विकल्प है। यह एक क्यूबिक हर्माइट वक्र है, जहां नियंत्रण बिंदुओं के लिए स्पर्शरेखाओं की गणना पिछले और अगले बिंदुओं से की जाती है।

Android में Path API Bézier घटता का समर्थन करता है, जो मापदंडों के संबंध में Hermite घटता की तुलना में थोड़ा अलग है। सौभाग्य से हर्माइट घटता को बेज़ियर घटता में परिवर्तित किया जा सकता है। यहां पहला उदाहरण कोड है जिसे मैंने Googling के दौरान पाया था। इस Stackoverflow जवाब भी सूत्र देने के लिए लगता है।

आपने तेज किनारों की समस्या का भी उल्लेख किया है। आपके पास मौजूद इनपुट डेटा के साथ, यह पता लगाना असंभव है कि क्या कोई वास्तविक तेज कोना है या बस बहुत खड़ी वक्र है। यदि यह एक समस्या बन जाती है, तो आप आवश्यकतानुसार कदम-दर-फ़्लाय को बढ़ा / घटाकर पुनरावृति को अधिक अनुकूल बना सकते हैं।

संपादित करें: आगे की सोच के बाद द्विघात वक्रों का उपयोग किया जा सकता है। नियंत्रण बिंदु के रूप में p1 से p1 से द्विघात वक्र को खींचने के बजाय, इसे नियंत्रण बिंदु के रूप में एक नए बिंदु p0_1 का उपयोग करके p0 से p1 तक खींचें। नीचे तस्वीर देखें। नए नियंत्रण बिंदु

यदि p0_1 p0 और p1 में स्पर्शरेखा के चौराहे पर है, तो परिणाम सुचारू होना चाहिए। इससे भी बेहतर, चूंकि PathMeasure.getPosTan()रिटर्न तीसरे पैरामीटर के रूप में भी स्पर्शरेखा है, आप आसन्न बिंदुओं से अनुमानों के बजाय वास्तविक सटीक स्पर्शरेखाओं का उपयोग कर सकते हैं। इस दृष्टिकोण के साथ आपको अपने मौजूदा समाधान में कम बदलाव की आवश्यकता है।

इस उत्तर के आधार पर , अंतर सूत्र की गणना निम्न सूत्र से की जा सकती है:

getPosTan(pxPlace0, p0, t0); // Also get the tangent
getPosTan(pxPlace1, p1, t1);
t1 = -t1; // Reverse direction of second tangent
vec2 d = p1 - p0;
float det = t1.x * t0.y - t1.y * t0.x;
float u = (d.y * t1.x - d.x * t1.y) / det;
float v = (d.y * t0.x - d.x * t0.y) / det; // Not needed ... yet
p0_1 = p0 + u * t0;

यह समाधान हालांकि तभी काम करता है जब यू और वी दोनों गैर-नकारात्मक हों। देखिये दूसरी तस्वीर: किरणों को काटना नहीं है

यहाँ किरणें अन्तर्विभाजित नहीं करतीं, हालाँकि रेखाएँ नकारात्मक होती हैं, क्योंकि आप नकारात्मक हैं। इस मामले में यह एक द्विघात वक्र को खींचना संभव नहीं है जो आसानी से पिछले एक से जुड़ जाएगा। यहां आपको बेज़ियर कर्व्स की आवश्यकता है। आप इसके लिए नियंत्रण बिंदुओं की गणना इस उत्तर में पहले दी गई विधि से कर सकते हैं या उन्हें सीधे स्पर्शरेखा से प्राप्त कर सकते हैं। स्पर्शरेखा रे p0 + u * t0 के लिए प्रोजेक्टिंग p0 और दूसरी किरण के लिए vise versa दोनों को नियंत्रण बिंदु c0 और c1 देता है। जब तक यह स्पर्शरेखा किरण पर स्थित है तब तक c0 के बजाय p0 और c0 के बीच किसी भी बिंदु का उपयोग करके आप वक्र को समायोजित कर सकते हैं।

Edit2: यदि आपकी ड्राइंग स्थिति p1 में है, तो आप निम्न छद्म कोड के साथ bezier नियंत्रण बिंदुओं की गणना P2 में कर सकते हैं:

vec2 p0, p1, p2, p3; // These are calculated with PathMeasure
vec2 cp1 = p1 + (p2 - p0) / 6;
vec2 cp2 = p2 - (p3 - p1) / 6;

इनके साथ आप p1 से P2 तक का रास्ता जोड़ सकते हैं:

path.cubicTo(cp1.x, cp1.y, cp2.x, cp2.y, p2.x, p2.y);

अपने कोड से मेल करने के लिए फ्लोट [ 2 ] सरणियों पर प्रति घटक संचालन के साथ वेक्टर संचालन को बदलें । आप प्रारंभ करके शुरू करते p1 = start;हैं और पी 2 और पी 3 अगले अंक हैं। p0 शुरू में अपरिभाषित है। पहले खंड के लिए जहां आपके पास अभी तक p0 नहीं है, आप नियंत्रण बिंदु के रूप में cp2 के साथ p1 से P2 तक द्विघात वक्र का उपयोग कर सकते हैं। पथ के अंत के लिए वही जहां आपके पास p3 नहीं है, आप नियंत्रण बिंदु के रूप में cp1 के साथ p1 से P2 तक एक द्विघात वक्र बना सकते हैं। वैकल्पिक रूप से आप पहले खंड के लिए p0 = p1 और अंतिम खंड के लिए p3 = P2 को इनिशियलाइज़ कर सकते हैं। हर सेगमेंट के बाद आप p0 = p1; p1 = p2; and p2 = p3;आगे बढ़ने पर मूल्यों को स्थानांतरित करते हैं।

जब आप पथ को सहेज रहे हैं, तो आप बस सभी बिंदुओं को सहेजते हैं p0 ... pN। नियंत्रण बिंदु cp1 और cp2 को बचाने की आवश्यकता नहीं है, क्योंकि उन्हें आवश्यकतानुसार गणना की जा सकती है।

Edit3: जैसा कि कर्व जेनरेशन के लिए अच्छा इनपुट वैल्यू मिलना कठिन प्रतीत होता है, मैं एक और दृष्टिकोण प्रस्तावित करता हूं: सीरियलाइजेशन का उपयोग करें। Android पथ इसका समर्थन नहीं करता है, लेकिन सौभाग्य से क्षेत्र वर्ग करता है। इस उत्तर को कोड के लिए देखें । यह आपको सटीक परिणाम देना चाहिए। यदि यह अनुकूलित नहीं है, तो यह क्रमबद्ध रूप में कुछ जगह ले सकता है, लेकिन इस मामले में इसे बहुत अच्छी तरह से संपीड़ित करना चाहिए। GZIPOutputStream का उपयोग करके Android जावा में संपीड़न आसान है ।


यह आशाजनक लगता है। हालाँकि यह p0 नहीं है, लेकिन p1, P2, p3 का उपयोग किया जाता है, p0 केवल नए निश्चित बिंदुओं को संग्रहीत करने के लिए है जब उनकी गणना की जाती है और सीधी रेखाओं के लिए, ताकि वे प्रत्येक चरण का नमूना न लें। क्या आप नए नियंत्रण बिंदुओं के लिए x, y की गणना करने में मेरी मदद कर सकते हैं?
लुमिस

मैं बाद में ऐसा कर सकता था, लेकिन इस बीच stackoverflow.com/questions/2931573/… की जाँच करें । यू और वी के साथ आप चौराहे बिंदु प्राप्त कर सकते हैं।
एमएसएल

मदद के लिए धन्यवाद, मैं यह कोशिश करना चाहूंगा, लेकिन इसे एंड्रॉइड के लिए जावा में लिखने की आवश्यकता है। कोई वेक्टर 2 और टी 1 और पी 1 आदि फ्लोट एरेज़ नहीं हैं, इसलिए मैं उन पर कोई सीधा ऑपरेशन नहीं कर सकता जैसे टी 1 =-टी 1, या यू * टी 0। मेरा मानना ​​है कि t1 = -t1 का मतलब t1.x = -t1x है; t1.y = -t1.y आदि, सही है?
लूमिस

हां, इसे और अधिक कॉम्पैक्ट और पठनीय बनाने के लिए सिर्फ छद्म कोड था।
एमएसल

खैर, प्लॉट मोटा हो रहा है। क्योंकि एंड्रॉइड में दो रास्तों का रीजन चौराहा एक ऐसा रास्ता देता है, जो एंटी-अलियासड नहीं है, टैंजेंट जगह पर हैं। तो उचित समाधान यह होगा कि पहले दिए गए बिंदुओं के माध्यम से कुछ चिकनी वक्र को चलाएं और फिर उसका नमूना लें। आपका कोड एंटी-अलियास पथ पर पूरी तरह से ठीक काम करता है, यह उचित नियंत्रण बिंदु पैदा करता है।
लूमिस

13

W3C क्या करेगा?

इंटरनेट में यह समस्या रही है। वर्ल्ड वाइड वेब कंसोर्टियम देखा। इसमें 1999 से स्केलेबल वेक्टर ग्राफिक्स (एसवीजी) की सिफारिश की गई है । यह एक्सएमएल- आधारित फाइल प्रारूप है जो विशेष रूप से 2 डी आकार के भंडारण के लिए डिज़ाइन किया गया है।

" स्केलेबल-क्या? "

स्केलेबल वेक्टर ग्राफिक्स !

  • स्केलेबल : यह किसी भी आकार को आसानी से स्केल करने के लिए है।
  • वेक्टर : यह वैक्टर की गणितीय धारणा पर आधारित है ।
  • ग्राफिक्स । यह चित्र बनाने के लिए है।

यहाँ SVG संस्करण 1.1 के लिए तकनीकी विनिर्देश है।
(नाम से नहीं डरना चाहिए; यह वास्तव में पढ़ने के लिए सुखद है।)

उन्होंने ठीक नीचे लिखा है कि सर्कल या आयतों जैसे बुनियादी आकार कैसे संग्रहीत किए जाते हैं। उदाहरण के लिए, आयतों इन गुण होते हैं: , , , , , । ( और गोल कोनों के लिए इस्तेमाल किया जा सकता है।)xywidthheightrxryrxry

यहाँ एसवीजी में उनका उदाहरण आयत है: (ठीक है, दो वास्तव में - एक कैनवास की रूपरेखा के लिए।)

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="12cm" height="4cm" viewBox="0 0 1200 400"
     xmlns="http://www.w3.org/2000/svg" version="1.1">
  <desc>Example rect01 - rectangle with sharp corners</desc>

  <!-- Show outline of canvas using 'rect' element -->
  <rect x="1" y="1" width="1198" height="398"
        fill="none" stroke="blue" stroke-width="2"/>

  <rect x="400" y="100" width="400" height="200"
        fill="yellow" stroke="navy" stroke-width="10"  />
</svg>

यहाँ इसका प्रतिनिधित्व करता है:

एक नीली रूपरेखा के साथ एक पीला आयत

जैसा कि विनिर्देश कहता है, यदि आपको उनकी आवश्यकता नहीं है, तो आप कुछ गुणों को छोड़ने के लिए स्वतंत्र हैं। (उदाहरण के लिए, rxऔर ryविशेषताओं का उपयोग यहां नहीं किया गया था।) हां, शीर्ष पर एक टन cruft है DOCTYPEजिसके बारे में आपको अपने खेल के लिए बस आवश्यकता नहीं होगी। वे वैकल्पिक भी हैं।

पथ

एसवीजी पथ इस अर्थ में "पथ" हैं कि यदि आप एक पेपर पर एक पेंसिल डालते हैं, तो इसे चारों ओर घुमाएं और अंततः इसे फिर से बढ़ाएं , आपके पास एक रास्ता है। उन्हें बंद नहीं करना है , लेकिन वे हो सकते हैं।

प्रत्येक पथ में एक dविशेषता है (मुझे लगता है कि यह "ड्रा" के लिए खड़ा है), पथ डेटा युक्त , मूल रूप से सिर्फ एक कागज के लिए एक कलम डालकर उसे चारों ओर ले जाने के लिए आदेशों का एक क्रम ।

वे एक त्रिकोण का उदाहरण देते हैं:

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="4cm" height="4cm" viewBox="0 0 400 400"
     xmlns="http://www.w3.org/2000/svg" version="1.1">
  <title>Example triangle01- simple example of a 'path'</title>
  <desc>A path that draws a triangle</desc>
  <rect x="1" y="1" width="398" height="398"
        fill="none" stroke="blue" />
  <path d="M 100 100 L 300 100 L 200 300 z"
        fill="red" stroke="blue" stroke-width="3" />
</svg>

एक लाल त्रिकोण

में dविशेषता देखें path?

d="M 100 100 L 300 100 L 200 300 z"

Mएक है आदेश के लिए में ले जाएँ (निर्देशांक के बाद), Lएस के लिए कर रहे हैं करने के लिए लाइन (निर्देशांक के साथ) और zएक कमांड पथ बंद करने के लिए है (यानी पहले स्थान के लिए एक लाइन वापस आकर्षित; कि निर्देशांक की जरूरत नहीं है)।

सीधी रेखाएँ उबाऊ हैं? क्यूबिक या द्विघात बेज़ियर कमांड का उपयोग करें !

कुछ घन बेज़ियर

बेज़ियर कर्व्स के पीछे का सिद्धांत अच्छी तरह से कहीं भी कवर किया गया है (जैसे विकिपीडिया पर ), लेकिन यहाँ कार्यकारी सारांश है: बेज़ियर्स में एक शुरुआत और अंत बिंदु है, संभवतः कई नियंत्रण बिंदुओं के साथ जो प्रभावित करता है कि बीच में वक्र कहाँ जा रहा है।

एक द्विघात Bierz ट्रेसिंग

यदि आप चाहते हैं तो विनिर्देशन सबसे बुनियादी आकृतियों को पथ में परिवर्तित करने के निर्देश भी देता है।

एसवीजी का उपयोग क्यों और कब करना है

ध्यान से तय करें कि क्या आप इस रास्ते (नीयत) पर चलना चाहते हैं, क्योंकि यह वास्तव में पाठ में किसी भी मनमाने 2 डी आकार का प्रतिनिधित्व करने के लिए काफी जटिल है! आप अपने जीवन को इतना आसान बना सकते हैं यदि आप अपने आप को सिर्फ (सीधे तौर पर कई) सीधी रेखाओं से बने रास्तों तक सीमित कर दें।

लेकिन अगर आप तय करते हैं कि आप मनमाना आकार चाहते हैं, तो एसवीजी जाने का तरीका है: इसमें बढ़िया टूल सपोर्ट है: आप XML लेगर्स के लिए निम्न स्तर पर और SVG एडिटर टूल्स उच्च स्तर पर कई लाइब्रेरी पा सकते हैं ।

बावजूद, एसवीजी मानक एक अच्छा उदाहरण सेट करता है।


प्रश्न वक्र को बिंदुओं में परिवर्तित करने के बारे में है, इसे सहेजने में नहीं। लेकिन इस संदर्भ के लिए धन्यवाद, एसवीजी मानक के बारे में जानना अच्छा है।
लुमिस

@ लुमिस शीर्षक और सामग्री अन्यथा सुझाव देगी। प्रश्न को रीफ़्रेश करने पर विचार करें। (या, अब जब कि यह एक बहुत स्थापित है, एक और पूछ रहा है।)
एंको

4

आपके कोड में भ्रामक टिप्पणी है:

dstPath.quadTo(p2[0] , p2[1], p3[0], p3[1]); //create a curve to the third point through the second

एक द्विघात बेज़ियर वक्र करता नहीं जाना के माध्यम से दूसरी बात। यदि आप दूसरे बिंदु से गुजरना चाहते हैं, तो आपको एक अलग प्रकार के वक्र की आवश्यकता होती है, जैसे कि एक हेर्माइट वक्र । आप हर्माइट घटता को बेज़ियर में बदलने में सक्षम हो सकते हैं ताकि आप पाथ क्लास का उपयोग कर सकें।

एक और सुझाव अंकों के नमूने के बजाय, उन बिंदुओं के माध्यम का उपयोग करें, जिन्हें आप छोड़ रहे हैं।

एक अन्य सुझाव एक कोण को दहलीज के रूप में उपयोग करने के बजाय, वास्तविक वक्र और अनुमानित वक्र के बीच के अंतर का उपयोग करें। कोण असली समस्या नहीं हैं; असली समस्या यह है कि जब बिंदुओं का सेट एक बीज़ियर वक्र के लायक नहीं होता है।

एक अन्य सुझाव क्यूबिक बेज़ियर्स का उपयोग करना है, जिसमें से एक अगले के स्पर्शरेखा से मेल खाता है। अन्यथा (चतुर्भुज के साथ) मुझे लगता है कि आपके घटता आसानी से मेल नहीं खाएंगे।


आप सही हैं, दूसरा बिंदु केवल वक्र को "खींचता" है। क्यूबिकोटो को क्वाडटू के बजाय एक के बजाय दो नियंत्रण बिंदुओं की आवश्यकता होती है। समस्या यह है कि सही नियंत्रण बिंदु कैसे प्राप्त करें। ध्यान दें कि मैं तेज कोनों को खोना नहीं चाहता क्योंकि स्रोत पथ सीधे या गोल किसी भी आकार का एक संयोजन हो सकता है - मूल रूप से मैं एक छवि चयन उपकरण बना रहा हूं जहां मैं चयनित पथ को बचा सकता हूं।
लुमिस

4

दो रास्तों की एक चिकनी चौराहा पाने के लिए, आप उन्हें चौराहे से पहले स्केल कर सकते हैं और बाद में उन्हें स्केल कर सकते हैं।

मुझे नहीं पता कि यह एक अच्छा समाधान है, लेकिन यह मेरे लिए अच्छा काम करता है। यह भी तेज है। मेरे उदाहरण में, मैं एक पैटर्न के साथ एक गोल पथ को काटता हूं जिसे मैंने बनाया (स्ट्रिप्स)। यह स्केल्ड होने पर भी अच्छा लगता है।

यहाँ मेरा कोड:

    Path mypath=new Path(<desiredpath to fill with a pattern>);
    String sPatternType=cpath.getsPattern();

    Path pathtempforbounds=new Path(cpath.getPath());
    RectF rectF = new RectF();
     if (sPatternType.equals("1")){
         turnPath(pathtempforbounds, -45);
     }
     pathtempforbounds.computeBounds(rectF, true);

     float ftop=rectF.top;
     float fbottom=rectF.bottom;
     float fleft=rectF.left;
     float fright=rectF.right;
     float xlength=fright-fleft;

     Path pathpattern=new Path();

     float ypos=ftop;
     float xpos=fleft;

     float fStreifenbreite=4f;

     while(ypos<fbottom){
         pathpattern.moveTo(xpos,ypos);
         xpos=xpos+xlength;
         pathpattern.lineTo(xpos, ypos);
         ypos=ypos+fStreifenbreite;
         pathpattern.lineTo(xpos, ypos);
         xpos=xpos-xlength;
         pathpattern.lineTo(xpos, ypos);
         ypos=ypos-fStreifenbreite;
         pathpattern.lineTo(xpos, ypos);
         pathpattern.close();
         ypos=ypos+2*fStreifenbreite;

     }

     // Original vergrössern

     scalepath(pathpattern,10);
     scalepath(mypath,10);

     if (sPatternType.equals("1")){
         Matrix mdrehen=new Matrix();
         RectF bounds=new RectF();
         pathpattern.computeBounds(bounds, true);
         mdrehen.postRotate(45, (bounds.right + bounds.left)/2,(bounds.bottom + bounds.top)/2);
         pathpattern.transform(mdrehen);
     }

     RectF rectF2 = new RectF();
     mypath.computeBounds(rectF2, true);

     Region clip = new Region();
     clip.set((int)(rectF2.left-100f),(int)(rectF2.top -100f), (int)(rectF2.right+100f),(int)( rectF2.bottom+100f));
     Region region1 = new Region();
     region1.setPath(pathpattern, clip);

     Region region2 = new Region();
     region2.setPath(mypath, clip);

     region1.op(region2, Region.Op.INTERSECT);


     Path pnew=region1.getBoundaryPath();


     scalepath(pnew, 0.1f);
     cpath.setPathpattern(pnew);




public void turnPath(Path p,int idegree){
     Matrix mdrehen=new Matrix();
     RectF bounds=new RectF();
     p.computeBounds(bounds, true);
     mdrehen.postRotate(idegree, (bounds.right + bounds.left)/2,(bounds.bottom + bounds.top)/2);
     p.transform(mdrehen);
}

public void scalepath(Path p,float fscale){
     Matrix mverkleinern=new Matrix();
     mverkleinern.preScale(fscale,fscale);
     p.transform(mverkleinern);
}

यहाँ छवि विवरण दर्ज करें

कैनवास के साथ ज़ूम करने पर अभी भी चिकना दिखता है। यहाँ छवि विवरण दर्ज करें


उन लोगों के लिए धन्यवाद जिन्होंने कभी छवियों को जोड़ने के लिए मुझे 10 प्रतिष्ठा खर्च की :-)
user1344545

1
आश्चर्यजनक रूप से, यह सरल चाल दो समस्याओं को हल करती है: सबसे पहले यह चौराहे या संघ के परिणामस्वरूप पथ को सुचारू बनाती है और दूसरी बात यह है कि इस समान स्केल-अप पथ का नमूना करते समय प्रश्न में मेरा कोड पूरी तरह से चिकनी परिणाम पैदा करता है। क्या एक अप्रत्याशित और सरल समाधान, धन्यवाद!
लुमिस

@user संपादन मुफ्त है। <2k-rep उपयोगकर्ताओं के लिए, यह वास्तव में एक +2 है।
एको

@ लुमिस मैं थोड़ा उलझन में हूँ - मैंने सोचा कि आपने पूछा कि पथ कैसे स्टोर करें ?
एको

1
दुर्भाग्य से, अधिक परीक्षण के बाद मैंने पाया है कि क्योंकि क्षेत्र पिक्सल का उपयोग करता है जो कि खींचे जाने पर पथ पर कब्जा कर लेता है, ऐप आसानी से मेमोरी से बाहर निकलता है यदि पथ का स्केलिंग बड़ा है और बार-बार किया जाता है। तो यह समाधान सीमित और जोखिम भरा है, लेकिन ध्यान में रखना अच्छा है।
लुमिस

3

बहुभुज प्रक्षेप देखें ( http://en.wikipedia.org/wiki/Polynomial_interpolation )

मूल रूप से, आप n समान रूप से नोड्स लेते हैं (इष्टतम प्रक्षेप समान नहीं है, लेकिन आपके मामले के लिए यह पर्याप्त अच्छा और लागू करना आसान होना चाहिए)

आप ऑर्डर n के एक बहुभुज के साथ समाप्त होते हैं जो आपके वक्र के बीच की त्रुटि को कम करता है यदि (<- बड़ा अगर) आपकी रेखा पर्याप्त चिकनी है।

आपके मामले में, आप रैखिक (आदेश 1) प्रक्षेप कर रहे हैं ।

दूसरा मामला (जैसा कि ग्रिफिनहार्ट ने सुझाया था) स्प्लिन ( http://en.wikipedia.org/wiki/Spline_interpolation ) का उपयोग करना था

किसी भी मामले में आप अपने वक्र के लिए बहुपद फिट के कुछ रूप देंगे ।


2

यदि रूपांतरण का बिंदु केवल संग्रहण के लिए है, और जब आप इसे स्क्रीन पर वापस प्रस्तुत करते हैं, तो आपको इसे सुचारू रूप से रखने की आवश्यकता होती है, तब उच्चतम फ़िडेलिटी स्टोरेज आपको मिल सकता है, जबकि अभी भी दिए गए वक्र को बनाए रखने के लिए आवश्यक कुल संग्रहण कम से कम हो सकता है वास्तव में सर्कल (या एक चाप, बल्कि) की विशेषताओं को संग्रहीत करने और मांग पर इसे फिर से आकर्षित करने के लिए।

मूल। त्रिज्या। चाप को खींचने के लिए कोणों को शुरू / बंद करें।

यदि आपको रेंडरिंग के लिए किसी भी तरह सर्कल / आर्क को पॉइंट में बदलने की आवश्यकता है, तो आप संभवतः इसे स्टोरेज से लोड करने पर कर सकते हैं, जबकि हमेशा केवल एट्रिब्यूट्स को स्टोर करते हैं।


स्रोत पथ / वक्र किसी भी आकार का हो सकता है जिसमें एक मुक्त रेखा का चित्रण शामिल है। मैं उस समाधान पर विचार कर रहा हूं, जिसमें प्रत्येक घटक को अलग से सहेजना होगा और फिर लोड होने पर उन्हें संयोजित करना होगा, लेकिन इसके लिए बड़ी मात्रा में काम करने की आवश्यकता होती है और यह इस तरह की जटिल वस्तु के हेरफेर को धीमा कर देगा क्योंकि प्रत्येक परिवर्तन को प्रत्येक को लागू करना होगा अपने घटकों को फिर से बचाने में सक्षम होने के लिए।
लूमिस

2

क्या सीधी रेखाओं के विपरीत वक्रों के जाने का कोई कारण है? साथ काम करने के लिए सीधी रेखाएं सरल हैं, और हार्डवेयर में कुशलता से प्रदान की जा सकती हैं।

विचार के लायक अन्य दृष्टिकोण पिक्सेल के प्रति बिट्स के एक जोड़े को स्टोर करना है, अगर यह अंदर, बाहर या आकृति की रूपरेखा पर है। यह अच्छी तरह से संपीड़ित होना चाहिए, और जटिल चयनों के लिए लाइनों की तुलना में अधिक कुशल हो सकता है।

आपको ये लेख भी दिलचस्प / उपयोगी लग सकते हैं:


1

वक्र प्रक्षेप पर एक नज़र डालें - कुछ अलग प्रकार हैं जिन्हें आप लागू कर सकते हैं जो आपके वक्र को चिकना करने में मदद करेंगे। आप उस सर्कल पर जितने अधिक अंक प्राप्त कर सकते हैं, उतना बेहतर होगा। भंडारण बहुत सस्ता है - इसलिए यदि 360 क्लोज़ नोड्स को निकालना काफी सस्ता है (यहां तक ​​कि स्थिति के लिए 8 बाइट्स पर; 360 नोड्स को स्टोर करना मुश्किल है)।

आप कुछ प्रक्षेप नमूने के साथ रख सकते हैं यहां केवल चार अंक के साथ; और परिणाम काफी अच्छे हैं (मेरा पसंदीदा इस मामले के लिए बेजियर है, हालांकि अन्य प्रभावी समाधान के बारे में दूसरों को झंकार सकते हैं)।

आप में चारों ओर खेल सकते हैं यहाँ भी।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.