Mengubah kurva 2D menjadi poin untuk penyimpanan data

12

Saya telah membuat sebuah algoritma yang mengubah setiap kurva yaitu jalur menjadi jumlah minimum poin sehingga saya dapat menyimpannya ke dalam file atau database.

Metode ini sederhana: memindahkan tiga titik dalam langkah yang sama dan mengukur sudut antara garis-garis ini membentuk titik-titik. Jika sudut lebih besar dari toleransi maka itu menciptakan kurva kubik baru ke titik itu. Kemudian ia memindahkan garis ke depan dan mengukur sudut lagi ...

Bagi mereka yang tahu Android Path Class - Perhatikan bahwa dstPath adalah kelas khusus, yang mencatat poin ke dalam Array sehingga saya dapat menyimpan poin nanti, sedangkan srcPath adalah hasil dari penyatuan Daerah dan karenanya tidak memiliki poin kunci untuk saya untuk menyimpan.

Masalahnya adalah bahwa lingkaran tidak terlihat mulus seperti yang Anda lihat pada gambar ini, dihasilkan oleh kode di bawah ini, di mana jalur sumber terdiri dari lingkaran dan persegi panjang yang sempurna. Saya mencoba mengubah sudut toleransi dan panjang langkah, tetapi tidak ada yang membantu. Saya ingin tahu apakah Anda dapat menyarankan perbaikan pada algoritma ini, atau pendekatan yang berbeda.

EDIT: Saya sekarang telah memposting seluruh kode untuk mereka yang menggunakan Android java, sehingga mereka dapat dengan mudah mencoba dan bereksperimen.

masukkan deskripsi gambar di sini

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

}

Inilah perbandingan antara yang asli dan yang dihasilkan oleh algoritma saya:

perbandingan antara jalur;  sudut terasa lebih halus pada turunannya

Lumis
sumber
mengapa tidak menggunakan b-splines?
GriffinHeart
4
Jika Anda tahu benda itu adalah lingkaran dan persegi panjang, mengapa tidak menyimpan lingkaran dan persegi panjang? Dan dalam bentuk umum - input apa pun yang dihasilkan benda Anda mungkin merupakan format yang masuk akal untuk menyimpannya. Jika Anda mencari skema kompresi yang tampak seperti pertanyaan yang berbeda (atau setidaknya kita membutuhkan lebih banyak info tentang sumber data) untuk membantu).
Jeff Gates
Ini bisa berupa bentuk tak terduga seperti yang saya katakan di kalimat pertama - lingkaran dan kotak di sini hanyalah contoh uji.
Lumis
@ Lumum Anda benar-benar harus melihat ke dalam b-splines, itu untuk apa mereka. Ada alasan untuk mencoba menerapkan solusi Anda sendiri?
GriffinHeart
1
Kelas path yang baik akan membangun kurva tersebut dengan splines sehingga Anda sudah menggunakannya. Saya punya saran lain, kurang berorientasi matematika: alih-alih menyimpan poin, simpan input pengguna (pola perintah) dan putar ulang untuk membangun "gambar" yang sama.
GriffinHeart

Jawaban:

6

Saya pikir Anda memiliki dua masalah:

Titik kontrol non-simetris

Awalnya Anda mulai dengan jarak yang sama antara p0 ke p1 dan p1 ke p2. Jika sudut toleransi antara segmen garis tidak terpenuhi, Anda bergerak p1 dan p2 ke depan, tetapi tetap p0 di tempat itu. Ini meningkatkan jarak antara p0 ke p1 sambil menjaga jarak antara p1 ke p2 sama. Ketika Anda membuat kurva menggunakan p1 sebagai titik kontrol, itu bisa sangat bias terhadap p2 tergantung pada berapa banyak iterasi yang telah berlalu sejak kurva terakhir. Jika Anda akan memindahkan p2 dua kali jumlah dari p1, Anda akan mendapatkan jarak genap di antara titik-titik.

Kurva kuadratik

Seperti disebutkan dalam jawaban lain juga, kurva kuadratik tidak terlalu baik untuk kasus ini. Kurva yang berdekatan yang Anda buat harus berbagi titik kontrol dan garis singgung . Ketika data input Anda hanya poin, Catmull-Rom Spline adalah pilihan yang baik untuk tujuan itu. Ini adalah kurva Hermit kubik, di mana garis singgung untuk titik kontrol dihitung dari titik sebelumnya dan berikutnya.

Path API di Android mendukung kurva Bézier, yang sedikit berbeda dari kurva Hermite mengenai parameter. Untungnya kurva Hermite dapat dikonversi menjadi kurva Bézier. Berikut adalah contoh kode pertama yang saya temukan ketika Googling. Jawaban Stackoverflow ini juga tampaknya memberikan rumus.

Anda juga menyebutkan masalah ujung yang tajam. Dengan data input yang Anda miliki, tidak mungkin untuk mendeteksi apakah ada sudut tajam yang sebenarnya atau hanya kurva yang sangat curam. Jika ini menjadi masalah, Anda dapat membuat iterasi lebih adaptif dengan menambah / mengurangi langkah saat dibutuhkan.

Sunting: Setelah berpikir lebih lanjut kurva kuadrat dapat digunakan setelah semua. Alih-alih menggambar kurva kuadratik dari p0 ke p2 menggunakan p1 sebagai titik kontrol, gambarlah dari p0 ke p1 menggunakan titik baru p0_1 sebagai titik kontrol. Lihat gambar di bawah ini. Poin kontrol baru

Jika p0_1 berada di persimpangan garis singgung dalam p0 dan p1, hasilnya harus halus. Lebih baik lagi, karena PathMeasure.getPosTan()pengembalian juga bersinggungan sebagai parameter ketiga, Anda dapat menggunakan garis singgung akurat yang sebenarnya alih-alih perkiraan dari titik yang berdekatan. Dengan pendekatan ini Anda perlu sedikit perubahan pada solusi yang ada.

Berdasarkan jawaban ini , titik persimpangan dapat dihitung dengan rumus berikut:

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;

Namun solusi ini hanya berfungsi jika u dan v tidak negatif. Lihat gambar kedua: Sinar tidak berpotongan

Di sini sinarnya tidak berpotongan meskipun garis-garisnya akan, karena kamu negatif. Dalam hal ini tidak mungkin untuk menggambar kurva kuadrat yang dengan lancar akan terhubung ke yang sebelumnya. Di sini Anda membutuhkan kurva bézier. Anda bisa menghitung titik kontrol untuk itu baik dengan metode yang diberikan sebelumnya dalam jawaban ini atau berasal langsung dari garis singgung. Memproyeksikan p0 ke sinar tangen p0 + u * t0 dan sebaliknya untuk sinar lainnya memberikan kedua titik kontrol c0 dan c1. Anda juga dapat menyesuaikan kurva dengan menggunakan titik mana saja antara p0 dan c0 alih-alih c0 selama itu terletak pada sinar tangen.

Sunting2: Jika posisi gambar Anda di p1, Anda dapat menghitung titik kontrol bezier ke p2 dengan kode pseudo berikut:

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

Dengan ini, Anda dapat menambahkan path dari p1 ke p2:

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

Ganti operasi vektor dengan operasi per komponen pada array float [ 2 ] untuk mencocokkan kode Anda. Anda mulai dengan menginisialisasi p1 = start;dan p2 dan p3 adalah poin berikutnya. p0 awalnya tidak terdefinisi. Untuk segmen pertama di mana Anda belum memiliki p0, Anda dapat menggunakan kurva kuadratik dari p1 ke p2 dengan cp2 sebagai titik kontrol. Hal yang sama untuk ujung jalur di mana Anda tidak memiliki p3, Anda dapat menggambar kurva kuadratik dari p1 ke p2 dengan cp1 sebagai titik kontrol. Atau Anda dapat menginisialisasi p0 = p1 untuk segmen pertama dan p3 = p2 untuk segmen terakhir. Setelah setiap segmen, Anda menggeser nilainya p0 = p1; p1 = p2; and p2 = p3;saat bergerak maju.

Ketika Anda menyimpan path, Anda hanya menyimpan semua poin p0 ... pN. Tidak perlu menyimpan titik kontrol cp1 dan cp2, karena mereka dapat dihitung sesuai kebutuhan.

Sunting3: Karena tampaknya sulit untuk mendapatkan nilai input yang bagus untuk pembuatan kurva, saya mengusulkan pendekatan lain: Gunakan serialisasi. Android Path tampaknya tidak mendukungnya, tetapi untungnya kelas Region melakukannya. Lihat jawaban ini untuk kodenya. Ini akan memberi Anda hasil yang tepat. Mungkin diperlukan beberapa ruang dalam bentuk serial jika tidak dioptimalkan, tetapi dalam kasus itu harus dikompres dengan sangat baik. Kompresi mudah di Android Java menggunakan GZIPOutputStream .

msell
sumber
Kedengarannya menjanjikan. Namun itu bukan p0 tetapi p1, p2, p3 yang digunakan, p0 hanya untuk menyimpan titik-titik pasti baru ketika mereka dihitung dan demi garis lurus, sehingga mereka tidak dijadikan sampel setiap langkah. Bisakah Anda membantu saya cara menghitung x, y untuk titik kontrol baru?
Lumis
Saya bisa melakukannya nanti, tetapi sementara itu periksa stackoverflow.com/questions/2931573/… . Dengan u dan v Anda bisa mendapatkan titik persimpangan.
msell
Terima kasih atas bantuannya, saya ingin mencoba ini, tetapi perlu ditulis dalam Java untuk Android. Tidak ada vector2 dan t1 dan p1 dll array float jadi saya tidak bisa melakukan operasi langsung pada mereka seperti t1 = -t1, atau u * t0. Saya berasumsi bahwa t1 = -t1 berarti t1.x = -t1x; t1.y = -t1.y dll, kan?
Lumis
Ya, itu hanya kode semu untuk membuatnya lebih ringkas dan mudah dibaca.
msell
Nah, plotnya semakin tebal. Karena persimpangan wilayah dua jalur di Android mengembalikan jalur yang TIDAK anti-alias, garis singgung berada di tempat itu. Jadi solusi yang tepat adalah menggerakkan beberapa kurva halus melalui titik-titik yang diberikan pertama dan kemudian sampel itu. Kode Anda berfungsi dengan baik pada jalur anti-alias, menghasilkan titik kontrol yang tepat.
Lumis
13

Apa yang akan dilakukan W3C?

Internet memiliki masalah ini. The World Wide Web Consortium melihat. Ini memiliki solusi standar yang direkomendasikan sejak 1999: Scalable Vector Graphics (SVG) . Ini adalah format file berbasis XML yang dirancang khusus untuk menyimpan bentuk 2D.

"Dapat diukur-apa? "

Grafik Vektor yang Dapat Dikukur !

  • Dapat diskalakan : Ini dimaksudkan untuk menskala dengan halus ke ukuran berapa pun.
  • Vektor : Ini didasarkan pada gagasan matematika vektor .
  • Grafik . Itu dimaksudkan untuk membuat gambar.

Berikut spesifikasi teknis untuk SVG versi 1.1.
(Jangan takut dengan namanya; Ini sebenarnya menyenangkan untuk dibaca.)

Mereka telah menuliskan dengan tepat bagaimana bentuk dasar seperti lingkaran atau persegi panjang harus disimpan. Misalnya, persegi panjang memiliki sifat ini: x, y, width, height, rx, ry. (The rxdan rydapat digunakan untuk sudut bundar.)

Inilah contoh persegi panjang mereka dalam SVG: (Ya, dua benar - satu untuk garis besar kanvas.)

<?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>

Inilah yang diwakilinya:

persegi panjang kuning dengan garis biru

Seperti spesifikasi mengatakan, Anda bebas untuk meninggalkan beberapa properti jika Anda tidak membutuhkannya. (Misalnya, rxdan ryatribut tidak digunakan di sini.) Ya, ada satu ton cruft di bagian atas DOCTYPEyang tidak Anda perlukan hanya untuk gim Anda. Mereka juga opsional.

Jalan

Jalur SVG adalah "jalur" dalam arti bahwa jika Anda meletakkan pensil di atas kertas, memindahkannya dan akhirnya mengangkatnya lagi , Anda memiliki jalur. Mereka tidak memiliki harus ditutup , tapi mereka mungkin.

Setiap jalur memiliki datribut (saya suka berpikir itu singkatan dari "draw"), yang berisi data path , urutan perintah untuk dasarnya hanya meletakkan pena ke kertas dan memindahkannya .

Mereka memberi contoh segitiga:

<?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>

sebuah segitiga merah

Lihat datribut di path?

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

Ini Madalah perintah untuk Pindah ke (diikuti oleh koordinat), Lyaitu untuk Baris ke (dengan koordinat) dan zmerupakan perintah untuk menutup jalur (yaitu menarik garis kembali ke lokasi pertama; yang tidak memerlukan koordinat).

Garis lurus itu membosankan? Gunakan perintah Bézier kubik atau kuadrat !

beberapa Béziers kubik

Teori di balik kurva Bézier dibahas dengan baik di tempat lain (seperti di Wikipedia ), tetapi inilah ringkasan eksekutif: Béziers memiliki titik awal dan titik akhir, dengan kemungkinan banyak titik kontrol yang memengaruhi ke mana kurva di antaranya bergerak.

melacak Bézier kuadratik

Spesifikasi ini juga memberikan instruksi untuk mengubah sebagian besar bentuk dasar menjadi jalur jika Anda mau.

Mengapa dan kapan harus menggunakan SVG

Putuskan dengan hati-hati jika Anda ingin turun jalan ini (pun intended), karena itu benar-benar sangat rumit untuk mewakili bentuk 2D sewenang-wenang dalam teks! Anda dapat membuat hidup Anda jauh lebih mudah jika Anda mis membatasi diri Anda hanya pada jalur yang terbuat dari (mungkin sangat banyak) garis lurus.

Tetapi jika Anda memutuskan ingin bentuk yang sewenang-wenang, SVG adalah caranya: Alat ini memiliki dukungan alat yang hebat: Anda dapat menemukan banyak pustaka untuk parsing XML di tingkat rendah dan alat editor SVG di tingkat tinggi.

Apapun, standar SVG memberikan contoh yang baik.

Anko
sumber
Pertanyaannya adalah tentang mengubah kurva menjadi poin, bukan menyimpannya. Tapi terima kasih untuk referensi ini, ada baiknya untuk mengetahui tentang standar SVG.
Lumis
@Lumis Judul dan konten menyarankan sebaliknya. Pertimbangkan mengulangi pertanyaannya. (Atau, sekarang yang ini cukup mapan, meminta yang lain.)
Anko
4

Kode Anda mengandung komentar yang menyesatkan:

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

Sebuah kurva Bezier kuadrat tidak tidak pergi melalui titik kedua. Jika Anda ingin melewati titik kedua, Anda memerlukan jenis kurva yang berbeda, seperti kurva hermite . Anda mungkin bisa mengubah kurva hermite menjadi beziers sehingga Anda bisa menggunakan kelas Path.

Saran lain adalah alih-alih mengambil sampel poin, gunakan rata-rata poin yang Anda lewatkan.

Saran lain adalah alih-alih menggunakan sudut sebagai ambang, gunakan perbedaan antara kurva aktual dan kurva perkiraan. Sudut bukan masalah sebenarnya; masalah sebenarnya adalah ketika set poin tidak cocok dengan kurva bezier.

Saran lain adalah menggunakan bezier kubik, dengan garis singgung satu cocok dengan garis singgung berikutnya. Kalau tidak (dengan kuadrat) saya pikir kurva Anda tidak akan cocok dengan lancar.

amitp
sumber
Anda benar, titik kedua hanya "menarik" kurva ke arah itu. CubicTo membutuhkan dua titik kontrol dan bukan satu sebagai quadTo. Masalahnya tentu saja bagaimana mendapatkan titik kontrol yang benar. Perhatikan bahwa saya tidak ingin kehilangan sudut tajam karena Path sumber dapat berupa kombinasi bentuk apa pun lurus atau bulat - pada dasarnya saya membuat alat pemilihan gambar di mana saya dapat menyimpan jalur yang dipilih.
Lumis
4

Untuk mendapatkan persimpangan dua jalur yang lebih mulus, Anda bisa memperbesarnya sebelum persimpangan dan menurunkannya setelahnya.

Saya tidak tahu apakah ini solusi yang baik, tetapi itu bekerja dengan baik untuk saya. Ini juga cepat. Dalam contoh saya, saya memotong lintasan bulat dengan pola yang saya buat (garis-garis). Ini terlihat bagus bahkan ketika ditingkatkan.

Ini kode saya:

    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);
}

masukkan deskripsi gambar di sini

Terlihat masih mulus saat melakukan zooming dengan canvas.scale (): masukkan deskripsi gambar di sini

pengguna1344545
sumber
Terima kasih kepada siapa yang pernah mengeluarkan saya 10 reputasi untuk menambahkan gambar :-)
user1344545
1
Hebatnya, trik sederhana ini memecahkan dua masalah: pertama membuat jalan persimpangan atau gabungan yang dihasilkan halus dan kedua kode saya dalam pertanyaan ketika mengambil sampel jalur peningkatan yang sama ini menghasilkan hasil yang sangat mulus. Solusi yang tidak terduga dan sederhana, terima kasih!
Lumis
Mengedit pengguna @ gratis. Untuk pengguna <2k-rep, sebenarnya +2.
Anko
@Lumis Saya agak bingung - saya pikir Anda bertanya bagaimana cara menyimpan jalur?
Anko
1
Sayangnya, setelah pengujian lebih lanjut saya menemukan bahwa karena Region menggunakan piksel yang akan ditempati jalur saat digambar, aplikasi dengan mudah kehabisan memori jika penskalaan Path besar dan dilakukan berulang kali. Jadi solusi ini terbatas & berisiko, tetapi bagus untuk diingat.
Lumis
3

Lihatlah interpolasi poligon ( http://en.wikipedia.org/wiki/Polynomial_interpolation )

Pada dasarnya, Anda mengambil n equispaced node (interpolasi yang optimal tidak equispaced, tetapi untuk kasus Anda harus cukup baik dan mudah diimplementasikan)

Anda berakhir dengan poligon orde n yang mengurangi kesalahan antara kurva Anda jika (<- besar jika) garis Anda cukup halus .

Dalam kasus Anda, Anda melakukan interpolasi linier (urutan 1).

Kasus lain (seperti yang disarankan GriffinHeart ) adalah menggunakan Splines ( http://en.wikipedia.org/wiki/Spline_interpolation )

Kasus mana pun akan memberi Anda beberapa bentuk polinomial yang cocok untuk kurva Anda.

Yuuta
sumber
2

Jika tujuan konversi hanya untuk penyimpanan, dan saat Anda mengembalikannya di layar, Anda memerlukannya agar lancar, maka penyimpanan dengan kesetiaan tertinggi dapat Anda peroleh, sementara masih meminimalkan total penyimpanan yang diperlukan untuk bertahan pada kurva tertentu. untuk benar-benar menyimpan atribut lingkaran (atau busur, lebih tepatnya) dan menggambar ulang sesuai permintaan.

Asal. Radius. Mulai / hentikan sudut untuk menggambar busur.

Jika Anda perlu mengonversi lingkaran / lengkung menjadi titik-titik untuk dirender, maka Anda dapat melakukannya setelah memuatnya dari penyimpanan, sambil selalu menyimpan hanya atribut-atributnya.

usaha keras
sumber
Jalur / kurva sumber dapat berupa apa saja termasuk gambar garis bebas. Saya telah mempertimbangkan solusi yang harus menyimpan masing-masing komponen secara terpisah dan kemudian menggabungkan mereka ketika dimuat tetapi membutuhkan sejumlah besar pekerjaan dan itu akan memperlambat manipulasi objek yang kompleks karena setiap transformasi harus diterapkan ke masing-masing komponennya agar dapat menyimpannya kembali.
Lumis
2

Apakah ada alasan untuk membuat kurva yang bertentangan dengan garis lurus? Garis lurus lebih mudah untuk dikerjakan, dan dapat diterjemahkan secara efisien dalam perangkat keras.

Pendekatan lain yang layak dipertimbangkan adalah untuk menyimpan beberapa bit per piksel, menyatakan apakah itu di dalam, di luar atau pada garis bentuk. Ini harus dikompres dengan baik, dan mungkin lebih efisien daripada garis untuk pemilihan kompleks.

Anda juga mungkin menemukan artikel ini menarik / bermanfaat:

Adam
sumber
1

Lihatlah interpolasi kurva - ada beberapa jenis yang dapat Anda terapkan yang akan membantu memperlancar kurva Anda. Semakin banyak poin yang bisa Anda dapatkan di lingkaran itu, semakin baik. Penyimpanan cukup murah - jadi jika mengekstraksi 360 node tutup cukup murah (bahkan pada 8 byte untuk posisi; 360 node hampir tidak mahal untuk disimpan).

Anda dapat menempatkan beberapa sampel interpolasi di sini hanya dengan empat poin; dan hasilnya cukup baik (favorit saya adalah Bezier untuk kasus ini, meskipun yang lain mungkin berpadu tentang solusi efektif lainnya).

Anda dapat bermain-main di sini juga.

Vaughan Hilts
sumber