2014-07-10 15 views
9

Ho bisogno di un algoritmo che può essere (un po ') più lento dello Bresenham line drawing algorithm ma deve essere molto più preciso. Con "esatto" intendo: ogni pixel sfiorato dovrebbe essere stampato. Non più, ma anche niente di meno! Ciò significa che l'utilizzo di una linea più spessa o simile non è un'opzione in quanto saranno coinvolti troppi pixel. Inoltre non ho bisogno di un quadro grafico o simile come fosse askedbefore, ho bisogno dell'algoritmo! L'applicazione non è propriamente nella 'grafica' è nello geography area dove i pixel sono 'tessere'.Algoritmo di disegno della linea subpixel preciso (algoritmo di rasterizzazione)

Il problema principale per me è che ho bisogno di precisione subpixel, il che significa che una linea potrebbe iniziare a 0,75/0,33 e non solo a 0/0, come nel caso dei valori interi. Ho provato a creare una soluzione funzionante per le ultime ore ma non riesco a farlo funzionare - ci sono troppi casi limite.

primo momento ho pensato una versione anti-aliasing, come l'algoritmo da Wu dovrebbe fare, ma esso stampa troppi pixel (soprattutto per i punti iniziali e finali) e, in alcuni casi manca ancora qualche pixel per esempio per linee molto brevi

Poi ho provato a far funzionare Bresenham dove ho sostituito il secondo 'se' con 'else if' come indicato here, ed è più vicino ma non ancora lì. Poi ho provato a spostare il Bresenham da intero a precisione a virgola mobile che ha provocato un loop infinito (mentre i valori x, y saltavano sopra la condizione di fine if (y1 == y2 && x1 == x2)).

Posso usare la soluzione naive line drawing ma quale delta devo usare? Per esempio. se uso 0.1 mi mancheranno ancora alcuni pixel e usando valori più piccoli probabilmente impiegheranno troppo tempo (e mancheranno ancora i pixel).

Una soluzione di lavoro in C/Java/... sarebbe apprezzata. Almeno dovrebbe funzionare per l'ottante 1, ma una soluzione completa sarebbe ancora più bella.

Aggiornamento: Mi è venuta in mente la seguente idea: utilizzando la rasterizzazione della linea naive è possibile calcolare 4 candidati pixel per ogni punto. Quindi controlla quei 4 pixel se la linea li incrocia davvero. Ma non sono sicuro che l'intersezione di linea/riquadro possa essere abbastanza veloce.

+0

"ogni pixel toccato deve essere stampato" anche se 0.01 di un pixel o meno si interseca con la linea? Quale forma prendono entrambe le estremità della linea (rotonda, concava, convessa, piatta)? – Tarik

+0

sì, se è presente un'intersezione matematico dovrebbe essere inclusa (ovviamente possiamo supporre l'errore di arrotondamento comune). Le estremità della linea dovrebbero essere piatte (niente lanugine o antialiasing, solo la fine "matematica") – Karussell

+0

e il colore? è la costante del colore della linea o deve essere interpolata in base all'area utilizzata del pixel come fa l'anti-aliasing? – Spektre

risposta

4

Se la linea è sottile e pixel sono rettangolari (quadrato):

enter image description here

poi considerare l'utilizzo di algoritmi di attraversamento griglia voxel, per esempio, vedere l'articolo "Fast Voxel Traversal Algorithm..." di Woo e Amanatides.

Practical implementation (nella griglia trasversale sezione)

Risposta al commento:
inizializzazione corretta per coordinata X variabili (uguale per Y)

DX = X2 - X1 
    tDeltaX = GridCellWidth/DX 
    tMaxX = tDeltaX * (1.0 - Frac(X1/GridCellWidth)) 
    //Frac if fractional part of float, for example, Frac(1.3) = 0.3 

esempio nella mia risposta here

+0

Grazie! Questo sembra buono - indagherò! I miei pixel sono rettangolari ma non quadratici quindi penso che questo possa funzionare. La tua immagine suggerisce che il punto iniziale o finale può essere solo al centro - è possibile anche un inizio o una fine arbitrari? – Karussell

+1

Si inizia nel riquadro contenente la posizione di partenza arbitraria e si termina nel riquadro contenente la posizione finale arbitraria. L'attraversamento avviene lungo la linea che interseca i punti iniziale e finale. Quindi, sì, è possibile un inizio e una fine arbitrari. – Kaganar

+0

Sì, sono possibili posizioni arbitrarie. – MBo

4

Se hai bisogno di un colore costante (non interpolato dall'area di pixel utilizzata), utilizza DDA:

void line_DDA_subpixel(int x0,int y0,int x1,int y1,int col) // DDA subpixel -> thick 
    { 
    int kx,ky,c,i,xx,yy,dx,dy; 
    x1-=x0; kx=0; if (x1>0) kx=+1; if (x1<0) { kx=-1; x1=-x1; } x1++; 
    y1-=y0; ky=0; if (y1>0) ky=+1; if (y1<0) { ky=-1; y1=-y1; } y1++; 
    if (x1>=y1) 
    for (c=x1,i=0;i<x1;i++,x0+=kx) 
     { 
     pnt(x0,y0,col); // this is normal pixel the two below are subpixels 
     c-=y1; if (c<=0) { if (i!=x1-1) pnt(x0+kx,y0,col); c+=x1; y0+=ky; if (i!=x1-1) pnt(x0,y0,col); } 
     } 
    else 
    for (c=y1,i=0;i<y1;i++,y0+=ky) 
     { 
     pnt(x0,y0,col); // this is normal pixel the two below are subpixels 
     c-=x1; if (c<=0) { if (i!=y1-1) pnt(x0,y0+ky,col); c+=y1; x0+=kx; if (i!=y1-1) pnt(x0,y0,col); } 
     } 
    } 

dove:

void pnt(int x,int y,int col); 

è routine che rasterize pixel (x,y) con il colore col la sorgente è in C++

Penso che sia stretto in avanti, ma in ogni caso

DDA uso equazione linea parametrica y=k*x+q o x=ky+q dipendente dalla differenza (se è più grande x o y differenza quindi non ci sono buchi). Lo è dy/dx o dx/dy e l'intera divisione viene ridotta alla sottrazione + anello aggiuntivo interno (ultima riga di ciascun ciclo). Questo può essere facilmente modificato in qualsiasi numero di dimensioni (io di solito uso 7D o più con questo). Sulle macchine moderne è la velocità a volte meglio di Bresenham (dipende dalla piattaforma e dall'uso).

Questo è come sembra rispetto al semplice DDA

DDA and DDA_subpixel lines

[EDIT2] doppie coordinate // originariamente [edit1]

OK qui è nuovo codice:

void line_DDA_subpixel1(double x0,double y0,double x1,double y1,int col) // DDA subpixel -> thick 
    { 
    int i,n,x,y,xx,yy; 
    // prepare data n-pixels,x1,y1 is line dx,dy step per pixel 
    x1-=x0; i=ceil(fabs(x1)); 
    y1-=y0; n=ceil(fabs(y1)); 
    if (n<i) n=i; if (!n) n=1; 
    x1/=double(n); 
    y1/=double(n); n++; 
    // rasterize DDA line 
    for (xx=x0,yy=y0,i=0;i<=n;i++,x0+=x1,y0+=y1) 
     { 
     // direct pixel 
     pnt(x,y,col); 
     // subpixels on change in both axises 
     x=x0; y=y0; 
     if ((i<n)&&(x!=xx)&&(y!=yy)) { pnt(xx,y,col); pnt(x,yy,col); } 
     xx=x; yy=y; 
     } 
    } 

Ed è così che sembra:

DDA and DDA_subpixel lines double coordinates

angolo dovrebbe essere in double precisione, ma ora PNT (x, y, col) è ancora in interi !!!

[edit3] attraversamento griglia di pixel

void DDAf_line_subpixel(float x0,float y0,float x1,float y1,int col) // DDA subpixel -> thick 
    { 
    int i,n; float a,a0,a1,aa,b,d; 
    // end-points 
    pnt(x0,y0,col); 
    pnt(x1,y1,col); 
    // x-axis pixel cross 
    a0=1; a1=0; n=0; 
    if (x0<x1) { a0=ceil(x0); a1=floor(x1); d=(y1-y0)/(x1-x0); a=a0; b=y0+(a0-x0)*d; n=fabs(a1-a0); } else 
    if (x0>x1) { a0=ceil(x1); a1=floor(x0); d=(y1-y0)/(x1-x0); a=a0; b=y1+(a0-x1)*d; n=fabs(a1-a0); } 
    if (a0<=a1) for (aa=a,i=0;i<=n;i++,aa=a,a++,b+=d) { pnt(aa,b,col); pnt(a,b,col); } 
    // y-axis pixel cross 
    a0=1; a1=0; n=0; 
    if (y0<y1) { a0=ceil(y0); a1=floor(y1); d=(x1-x0)/(y1-y0); a=a0; b=x0+(a0-y0)*d; n=fabs(a1-a0); } else 
    if (y0>y1) { a0=ceil(y1); a1=floor(y0); d=(x1-x0)/(y1-y0); a=a0; b=x1+(a0-y1)*d; n=fabs(a1-a0); } 
    if (a0<=a1) for (aa=a,i=0;i<=n;i++,aa=a,a++,b+=d) { pnt(b,aa,col); pnt(b, a,col); } 
    } 

Infine aveva qualche tempo per questo in modo ottimizzato DDA poco ma id portare a molti if cosi cambio rasterizzazione un po '. Ora vengono calcolati tutti i passaggi a griglia dei pixel (intersezioni) e poi per ognuno viene aggiunto il sub-pixel destro.Questo è come sembra (non sbagliate sub-pixel):

line pixel crossing subpixel

Per ogni x o y linee della griglia è il primo punto croce calcolato (a,b) e step è in un asse 1 pixel e nella seconda il resto in base a dy/dx o dx/dy. Dopodiché il ciclo for riempie i sub-pixel ...

+0

se è necessaria una percentuale di area, può essere derivato dallo stato della variabile c ma l'ho usato di recente perché non ne ho bisogno. – Spektre

+1

Questo è un buon algoritmo di caso specifico per ciò che l'OP sta cercando, ma AFAICT questo non affronta i casi estremamente probabili in cui le coordinate di inizio e fine linea sono di valori non interi. – Kaganar

+0

Funzionerà se sostituisco gli argomenti int con, ad es. Doppio? – Karussell