Conversione di una curva 2D in punti per l'archiviazione dei dati


12

Ho creato un algoritmo che converte qualsiasi curva, vale a dire il percorso in un numero minimo di punti in modo da poterlo salvare in un file o database.

Il metodo è semplice: sposta tre punti in passi uguali e misura l'angolo tra le linee che formano questi punti. Se l'angolo è maggiore della tolleranza, crea una nuova curva cubica a quel punto. Quindi sposta le linee in avanti e misura di nuovo l'angolo ...

Per chi conosce Android Path Class - Nota che dstPath è una classe personalizzata, che registra i punti in un array in modo che io possa salvare i punti in un secondo momento, mentre srcPath è il risultato di un'unione delle regioni e quindi non ha punti chiave per me salvare.

Il problema è che il cerchio non sembra liscio come puoi vedere in questa immagine, prodotta dal codice qui sotto, in cui il percorso di origine è costituito da un cerchio e un rettangolo perfetti. Ho provato a cambiare l'angolo di tolleranza e la lunghezza dei passi, ma nulla aiuta. Mi chiedo se puoi suggerire qualche miglioramento a questo algoritmo o un approccio diverso.

EDIT: ora ho pubblicato l'intero codice per coloro che utilizzano Android Java, in modo che possano facilmente provare e sperimentare.

inserisci qui la descrizione dell'immagine

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

}

Ecco un confronto tra l'originale e ciò che il mio algoritmo produce:

confronto tra percorsi;  angoli notevolmente più fluidi sulla derivata


perché non usare le b-spline?
GriffinHeart,

4
se sai che la cosa è un cerchio e un rettangolo, perché non memorizzare un cerchio e un rettangolo? E in forma generalizzata - qualsiasi input generato la tua cosa è probabilmente un formato ragionevole per memorizzarla. Se stai cercando uno schema di compressione che sembra una domanda diversa (o almeno avremmo bisogno di molte più informazioni sui dati di origine per essere utile).
Jeff Gates,

Può essere qualsiasi forma imprevedibile, come ho detto nella prima frase: il cerchio e la retta qui sono solo un esempio di prova.
Lumis,

@Lumis dovresti davvero guardare in b-splines, è per quello che sono. Qualche motivo per provare a implementare la tua soluzione?
GriffinHeart,

1
Bene, la classe path costruirà quelle curve con spline in modo che tu lo stia già utilizzando. Ho un altro suggerimento, meno orientato alla matematica: invece di salvare punti, salva l'input dell'utente (modello di comando) e riproducilo per creare la stessa "immagine".
GriffinHeart,

Risposte:


6

Penso che tu abbia due problemi:

Punti di controllo non simmetrici

Inizialmente si inizia con distanze uguali tra p0 a p1 e p1 a p2. Se l'angolo di tolleranza tra i segmenti di linea non viene raggiunto, si sposta in avanti p1 e p2, ma si mantiene p0 dov'era. Ciò aumenta la distanza tra p0 e p1 mantenendo allo stesso tempo la distanza tra p1 e p2. Quando si crea una curva usando p1 come punti di controllo, può essere fortemente distorta verso p2 a seconda di quante iterazioni sono passate dall'ultima curva. Se spostassi p2 il doppio della quantità di p1, otterrai distanze pari tra i punti.

Curve quadratiche

Come menzionato anche in altre risposte, la curva quadratica non è molto buona in questo caso. Le curve adiacenti create devono condividere un punto di controllo e una tangente . Quando i dati di input sono solo punti, Catmull-Rom Spline è una buona scelta a tale scopo. È una curva cubica di Hermite, in cui le tangenti per i punti di controllo vengono calcolate dai punti precedenti e successivi.

L'API Path in Android supporta le curve di Bézier, che sono leggermente diverse rispetto alle curve di Hermite per quanto riguarda i parametri. Fortunatamente le curve di Hermite possono essere convertite in curve di Bézier. Ecco il primo codice di esempio che ho trovato su Google. Anche questa risposta StackOverflow sembra dare la formula.

Hai anche menzionato il problema degli spigoli vivi. Con i dati di input che hai, è impossibile rilevare se c'è un vero angolo acuto o solo una curva molto ripida. Se questo diventa un problema, puoi rendere l'iterazione più adattabile aumentando / diminuendo il passaggio al volo secondo necessità.

Modifica: dopo aver riflettuto ulteriormente, le curve quadratiche potrebbero essere utilizzate dopo tutto. Invece di disegnare una curva quadratica da p0 a p2 usando p1 come punto di controllo, disegnala da p0 a p1 usando un nuovo punto p0_1 come punti di controllo. Vedi l'immagine sotto. Nuovi punti di controllo

Se p0_1 si trova nell'intersezione delle tangenti in p0 e p1, il risultato dovrebbe essere liscio. Ancora meglio, poiché i PathMeasure.getPosTan()ritorni sono anche tangenti come terzo parametro, è possibile utilizzare tangenti accurate effettive anziché approssimazioni da punti adiacenti. Con questo approccio sono necessarie meno modifiche alla soluzione esistente.

Sulla base di questa risposta , il punto di intersezione può essere calcolato con la seguente formula:

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;

Questa soluzione tuttavia funziona solo se sia u che v sono non negativi. Guarda la seconda foto: I raggi non si intersecano

Qui i raggi non si intersecano sebbene le linee lo farebbero, poiché tu sei negativo. In questo caso non è possibile tracciare una curva quadratica che si collegherebbe senza problemi alla precedente. Qui hai bisogno delle curve di Bézier. È possibile calcolare i punti di controllo per esso con il metodo indicato in precedenza in questa risposta o ricavarli direttamente dalle tangenti. Proiettando p0 sul raggio tangente p0 + u * t0 e viceversa per l'altro raggio si ottengono entrambi i punti di controllo c0 e c1. Puoi anche regolare la curva usando qualsiasi punto tra p0 e c0 anziché c0 purché si trovi sul raggio tangente.

Modifica2: se la posizione del disegno è in p1, è possibile calcolare i punti di controllo di Bezier in p2 con il seguente pseudo codice:

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

Con questi è possibile aggiungere un percorso da p1 a p2:

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

Sostituire le operazioni vettoriali con operazioni per componente su array float [ 2 ] in modo che corrispondano al codice. Si inizia inizializzando p1 = start;e p2 e p3 sono i punti successivi. p0 inizialmente non è definito. Per il primo segmento in cui non hai ancora p0, puoi usare una curva quadratica da p1 a p2 con cp2 come punto di controllo. Lo stesso per la fine del percorso in cui non hai p3, puoi disegnare una curva quadratica da p1 a p2 con cp1 come punto di controllo. In alternativa è possibile inizializzare p0 = p1 per il primo segmento e p3 = p2 per l'ultimo segmento. Dopo ogni segmento si spostano i valori p0 = p1; p1 = p2; and p2 = p3;quando si procede in avanti.

Quando si salva il percorso, è sufficiente salvare tutti i punti p0 ... pN. Non è necessario salvare i punti di controllo cp1 e cp2, poiché possono essere calcolati in base alle esigenze.

Edit3: Poiché sembra difficile ottenere buoni valori di input per la generazione della curva, propongo un altro approccio: utilizzare la serializzazione. Android Path non sembra supportarlo, ma fortunatamente la classe Region lo fa. Vedi questa risposta per il codice. Questo dovrebbe darti il ​​risultato esatto. Potrebbe non volerci un po 'di spazio in forma serializzata se non è ottimizzato, ma in tal caso dovrebbe comprimersi molto bene. La compressione è semplice in Android Java utilizzando GZIPOutputStream .


Sembra promettente. Tuttavia, non vengono utilizzati p0 ma p1, p2, p3, p0 serve solo per memorizzare nuovi punti definiti quando vengono calcolati e per motivi di linee rette, in modo che non vengano campionati ad ogni passo. Potete aiutarmi come calcolare x, y per nuovi punti di controllo?
Lumis

Potrei farlo più tardi, ma nel frattempo dai un'occhiata a stackoverflow.com/questions/2931573/… . Con u e v puoi ottenere il punto di intersezione.
msell

Grazie per l'aiuto, vorrei provare questo, ma deve essere scritto in Java per Android. Non ci sono vector2, t1 e p1 ecc. Sono array float, quindi non posso eseguire alcuna operazione diretta su di essi come t1 = -t1 o u * t0. Presumo che t1 = -t1 significa t1.x = -t1x; t1.y = -t1.y ecc., giusto?
Lumis

Sì, era solo uno pseudo codice per renderlo più compatto e leggibile.
msell

Bene, la trama si sta ispessendo. Poiché l'intersezione della regione di due percorsi in Android restituisce un percorso NON anti-alias, le tangenti sono al di sopra del luogo. Quindi la soluzione corretta sarebbe quella di guidare prima una curva liscia attraverso i punti dati e poi campionarla. Il tuo codice funziona perfettamente su un percorso anti-alias, produce punti di controllo adeguati.
Lumis

13

Cosa farebbe il W3C?

Internet ha avuto questo problema. Il World Wide Web Consortium ha notato. Ha una soluzione standard raccomandata dal 1999: Scalable Vector Graphics (SVG) . È un formato di file basato su XML appositamente progettato per la memorizzazione di forme 2D.

" Scalabile-cosa? "

Grafica vettoriale scalabile !

  • Scalabile : è pensato per adattarsi perfettamente a qualsiasi dimensione.
  • Vettore : si basa sulla nozione matematica di vettori .
  • Grafica . Ha lo scopo di fare foto.

Ecco le specifiche tecniche per la versione 1.1 di SVG.
(Non aver paura del nome; in realtà è piacevole da leggere.)

Hanno scritto esattamente come devono essere memorizzate le forme di base come cerchi o rettangoli . Ad esempio, rettangoli hanno queste caratteristiche: x, y, width, height, rx, ry. (Il rxe rypuò essere usato per gli angoli arrotondati.)

Ecco il loro rettangolo di esempio in SVG: (Beh, due davvero - uno per il contorno della tela.)

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

Ecco cosa rappresenta:

un rettangolo giallo con un contorno blu

Come dice la specifica, sei libero di tralasciare alcune proprietà se non ne hai bisogno. (Ad esempio, rxe gli ryattributi non sono stati usati qui.) Sì, c'è una tonnellata di innesti nella parte superiore di DOCTYPEcui non avrai bisogno solo per il tuo gioco. Sono anche opzionali.

percorsi

I percorsi SVG sono "percorsi", nel senso che se metti una matita su un foglio, lo sposti e alla fine lo sollevi di nuovo , hai un percorso. Non devono essere chiusi , ma potrebbero esserlo.

Ogni percorso ha un dattributo (mi piace pensare che significhi "disegnare"), contenente i dati del percorso , una sequenza di comandi per fondamentalmente semplicemente mettere una penna su un foglio e spostarlo .

Danno l'esempio di un triangolo:

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

un triangolo rosso

Vedi l' dattributo nel path?

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

Il Mè un comando di movimento a (seguita da coordinate), le Ls sono per Linea al (con coordinate) ed zè un comando per chiudere il percorso (cioè tracciare una linea posteriore alla prima posizione, che non ha bisogno coordinate).

Le linee rette sono noiose? Usa i comandi cubici o quadratici di Bézier!

alcuni cubi di Béziers

La teoria dietro le curve di Bézier è trattata bene altrove (come su Wikipedia ), ma ecco il riassunto esecutivo: Béziers ha un punto di inizio e di fine, con forse molti punti di controllo che influenzano dove sta andando la curva in mezzo.

tracciando un Bézier quadratico

La specifica fornisce anche istruzioni per convertire la maggior parte delle forme di base in tracciati nel caso lo desideri.

Perché e quando utilizzare SVG

Decidi attentamente se vuoi seguire questo percorso (gioco di parole intenzionale), perché è davvero abbastanza complicato rappresentare qualsiasi forma 2D arbitraria nel testo! Puoi rendere la tua vita molto più semplice se, ad esempio, ti limiti a solo percorsi fatti di (potenzialmente davvero molte) linee rette.

Ma se decidi di volere forme arbitrarie, SVG è la strada da percorrere: ha un ottimo supporto per gli strumenti: puoi trovare molte librerie per l'analisi XML a basso livello e strumenti dell'editor SVG ad alto livello.

Indipendentemente da ciò, lo standard SVG fornisce un buon esempio.


La domanda riguarda la conversione di una curva in punti, non il salvataggio. Ma grazie per questo riferimento, è bene conoscere lo standard SVG.
Lumis

@Lumis Il titolo e il contenuto suggerirebbero diversamente. Valuta di riformulare la domanda. (O, ora che questo è abbastanza stabilito, chiedendone un altro.)
Anko

4

Il tuo codice contiene un commento fuorviante:

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

Una curva di Bezier quadratica non non passare attraverso il secondo punto. Se vuoi passare attraverso il secondo punto hai bisogno di un diverso tipo di curva, come una curva eremita . Potresti essere in grado di convertire le curve dell'eremita in bezier in modo da poter usare la classe Path.

Un altro suggerimento è invece di campionare i punti, utilizzare la media dei punti su cui si sta saltando.

Un altro suggerimento è invece di utilizzare un angolo come soglia, utilizzare la differenza tra la curva effettiva e la curva approssimativa. Gli angoli non sono il vero problema; il vero problema è quando l'insieme di punti non si adatta a una curva più bezier.

Un altro suggerimento è di usare bezier cubici, con la tangente di uno corrispondente alla tangente di quello successivo. Altrimenti (con quadratica) penso che le tue curve non combacino perfettamente.


Hai ragione, il secondo punto "tira" solo la curva verso di essa. CubicTo richiede due punti di controllo anziché uno come quadTo. Il problema è ovviamente come ottenere i giusti punti di controllo. Nota che non voglio perdere spigoli vivi poiché il tracciato sorgente può essere una combinazione di qualsiasi forma diritta o rotonda - fondamentalmente sto creando uno strumento di selezione delle immagini in cui posso salvare il tracciato selezionato.
Lumis,

4

Per ottenere un'intersezione più uniforme di due percorsi, è possibile ridimensionarli prima dell'intersezione e ridimensionarli dopo.

Non so se sia una buona soluzione, ma ha funzionato bene per me. È anche veloce. Nel mio esempio, interseco un percorso arrotondato con un motivo creato (strisce). Sembra buono anche se ridimensionato.

Ecco il mio codice:

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

inserisci qui la descrizione dell'immagine

Sembra ancora liscio quando si esegue lo zoom con canvas.scale (): inserisci qui la descrizione dell'immagine


Grazie a chi mi ha mai speso 10 reputazione per aggiungere le immagini :-)
user1344545

1
Sorprendentemente, questo semplice trucco risolve due problemi: in primo luogo rende agevole il percorso risultante di intersezione o unione e in secondo luogo il mio codice nella domanda durante il campionamento di questo stesso percorso ingrandito produce un risultato perfettamente regolare. Che soluzione inaspettata e semplice, grazie!
Lumis

La modifica di @user è gratuita. Per gli utenti <2k-rep, in realtà è un +2.
Anko

@Lumis Sono un po 'confuso - Pensavo avessi chiesto come memorizzare i percorsi?
Anko

1
Sfortunatamente, dopo ulteriori test ho scoperto che, poiché Region utilizza pixel che il tracciato occuperebbe quando viene disegnato, l'app si esaurisce facilmente se il ridimensionamento del tracciato è grande e ripetuto ripetutamente. Quindi questa soluzione è limitata e rischiosa, ma buona da tenere a mente.
Lumis

3

Guarda l'interpolazione poligonale ( http://en.wikipedia.org/wiki/Polynomial_interpolation )

Fondamentalmente, prendi n nodi equispaziati (l'interpolazione ottimale non è equispaziata, ma nel tuo caso dovrebbe essere abbastanza buona e facile da implementare)

Si finisce con un poligono dell'ordine n che diminuisce l'errore tra la curva se (<- grande se) la linea è abbastanza liscia .

Nel tuo caso, stai eseguendo un'interpolazione lineare (ordine 1).

L'altro caso (come raccomandato da GriffinHeart ) era l'uso di Splines ( http://en.wikipedia.org/wiki/Spline_interpolation )

In entrambi i casi ti darebbe una qualche forma di adattamento polinomiale per la tua curva.


2

Se il punto della conversione è solo per l'archiviazione e quando si esegue il rendering sullo schermo è necessario che sia fluido, quindi l'archiviazione con la massima fedeltà che è possibile ottenere, pur riducendo al minimo l'archiviazione totale richiesta per persistere in una determinata curva potrebbe essere per memorizzare effettivamente gli attributi del cerchio (o piuttosto un arco) e ridisegnarlo su richiesta.

Origine. Raggio. Avvia / arresta gli angoli per disegnare l'arco.

Se è necessario convertire comunque il cerchio / arco in punti per il rendering, è possibile farlo su di esso caricandolo dalla memoria, conservando sempre solo gli attributi.


Il percorso / curva di origine può avere qualsiasi forma, incluso un disegno di una linea libera. Ho preso in considerazione quella soluzione che avrebbe dovuto salvare ciascun componente separatamente e poi combinarli quando caricati, ma richiede una grande quantità di lavoro e rallenterebbe la manipolazione di un oggetto così complesso come ogni trasformazione dovrebbe essere applicata a ciascuno di i suoi componenti per poterlo salvare di nuovo.
Lumis,

2

C'è un motivo per scegliere le curve anziché le linee rette? Le linee rette sono più semplici da lavorare e possono essere rese in modo efficiente nell'hardware.

L'altro approccio che vale la pena considerare è quello di memorizzare un paio di bit per pixel, indicando se è all'interno, all'esterno o sul contorno della forma. Questo dovrebbe comprimersi bene e potrebbe essere più efficiente delle linee per selezioni complesse.

Potresti anche trovare interessanti / utili questi articoli:


1

Dai un'occhiata all'interpolazione della curva: ci sono alcuni tipi diversi che puoi implementare che ti aiuteranno ad attenuare la curva. Più punti puoi ottenere in quel cerchio, meglio è. L'archiviazione è piuttosto economica, quindi se l'estrazione di 360 nodi vicini è abbastanza economica (anche a 8 byte per la posizione, 360 nodi è difficilmente costoso da memorizzare).

È possibile posizionare con alcuni campioni di interpolazione qui con solo quattro punti; e i risultati sono abbastanza buoni (il mio preferito è il Bezier per questo caso, anche se altri potrebbero intervenire su altre soluzioni efficaci).

Puoi giocare anche qui .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.