Preenchimento de Polígonos Computação Gráfica Rodrigo Toledo Polygon Filling Como preencher um polígono arbitrário? Quais pixels devem ser acesos? Começando com um retângulo Quais pixels devem ser acesos? Esse é o resultado esperado? O que aconteceu com os pixels da linha superior e da coluna direita? Isso é bom por que? Nenhum pixel deve pertencer a dois polígonos simultaneamente! • Eficiência não ascendendo duas vezes o mesmo pixel! • Interpretação Se os polígonos são de cores diferentes, a cor final irá depender da ordem em que são desenhados! Idéias gerais sobre Polygon Fill • Ascender pixels nas bordas inferior e esquerda, não ascender nas bordas superior e direita. • Se a borda estiver entre pixels em uma determinada linha, arredondar para o inteiror (arredondar a esquerda se for uma borda direita e arredondar a direita se for uma borda a esquerda). • O algoritmo deverá computar as interseções das bordas com cada scan line horizontal, ascendendo os pixels que estiverem entre os pares de interseções. Primeiro rascunho do algoritmo y ymax ys 1 i1 dados: {x0, x1, x2, x3, x4} {y0, y1, y2, y3, y4} 4 i0 i4 i3 acha ymax e ymin Para cada ys[ymax , ymin] 0 ymin Para cada aresta 2 3 0 xi1 xi0 xi4 vx= {xi1 , xi0 , xi4 , xi3} xi3 calcula as interseções x ordena interseções desenha linhas horizontais Scan line cruzando vértices y 1 0 i0 ys i1 i2 i4 5 i3 3 2 4 0 x inclui vértices: i0-i1, i2-i3, i4-? não inclui vértices: i0-? y y 1 ys i0 i1 i2 3 0 1 i4 i3 5 ys 3 L 5 L 2 2 4 0 0 i0 4 x 0 x Tratando cruzamento com vértices y 1 ys i0 0 i1 i2 3 i4 5 i3 2 4 0 x só inclui vértices de menor y: i0-i4 J Excluindo bordas superiores ou só inclui vértices de maior y: i0-i1, i2-i3 J Bordas horizontais devem respeitar a regra da paridade! Regra da paridade (even-odd parity rule) •Um ponto é considerado dentro de um polígono se uma raio vindo do infinito cruzar um número par de bordas! A B •Ao se respeitar a regra de “só inclui vértices de menor y” e fazendo o controle de paridade, naturalmente as bordas horizontais inferiores serão desenhadas e as superiores não! Cuidado! Algoritmo void FillPolygon (int np, int x[], int y[]) { /* declarações */ . . . /* calcula y max e min dos vértices*/ . . . for(ys=ymim; ys<ymax; ys--) /* para cada linha de scan (exceto a última)*/ { num_inters = 0; for(i=0; i<np; i++) /* para cada aresta */ { yi = y[i]; //y do vert. inicial da aresta yf = y[(i+1)%np]; //y do vert. final da aresta if (yi!=yf && ys >= MIN(yi,yf) && ys < MAX(yi,yf) ) { vxs[num_inters] = x[i] + (ys-yi)*(x[(i+1)%np]-x[i])/(yf-yi)); num_inters++; } } ordena(vxs,0,num_inters-1); /* ordena as interseções */ for (i=0;i<num_inters;i+=2) if (vxs[i]+1 < vxs[i+1]) HLine(vxs[i],vxs[i+1],ys); } // Exceto o ultimo pixel Algoritmo void FillPolygon (int np, int x[], int y[]) { /* declarações */ . . . Exclui linha superior /* calcula y max e min dos vértices*/ . . . for(ys=ymim; ys<ymax; ys--) /* para cada linha de scan (exceto a última)*/ { num_inters = 0; for(i=0; i<np; i++) /* para cada aresta */ { yi = y[i]; //y do vert. inicial da aresta yf = y[(i+1)%np]; //y do vert. final da aresta if (yi!=yf && ys >= MIN(yi,yf) && ys < MAX(yi,yf) ) { vxs[num_inters] = x[i] + (ys-yi)*(x[(i+1)%np]-x[i])/(yf-yi)); num_inters++; } } ordena(vxs,0,num_inters-1); /* ordena as interseções */ for (i=0;i<num_inters;i+=2) if (vxs[i]+1 < vxs[i+1]) HLine(vxs[i],vxs[i+1],ys); } // Exceto o ultimo pixel Algoritmo void FillPolygon (int np, int x[], int y[]) { /* declarações */ . . . /* calcula y max e min dos vértices*/ . . . o vértice final da última aresta é o vértice inicial da primeira for(ys=ymim; ys<ymax; ys--) /* para cada linha de scan (exceto a última)*/ { num_inters = 0; for(i=0; i<np; i++) /* para cada aresta */ { yi = y[i]; //y do vert. inicial da aresta yf = y[(i+1)%np]; //y do vert. final da aresta if (yi!=yf && ys >= MIN(yi,yf) && ys < MAX(yi,yf) ) { vxs[num_inters] = x[i] + (ys-yi)*(x[(i+1)%np]-x[i])/(yf-yi)); num_inters++; } } ordena(vxs,0,num_inters-1); /* ordena as interseções */ for (i=0;i<num_inters;i+=2) if (vxs[i]+1 < vxs[i+1]) HLine(vxs[i],vxs[i+1],ys); } // Exceto o ultimo pixel Algoritmo void FillPolygon (int np, int x[], int y[]) { /* declarações */ . . . /* calcula y max e min dos vértices*/ . . . for(ys=ymim; ys<ymax; ys--) /* para cada linha de scan (exceto a última)*/ { num_inters = 0; for(i=0; i<np; i++) /* para cada aresta */ { yi = y[i]; //y do vert. inicial da aresta yf = y[(i+1)%np]; //y do vert. final da aresta Δx/Δy if (yi!=yf && ys >= MIN(yi,yf) && ys < MAX(yi,yf) ) { vxs[num_inters] = x[i] + (ys-yi)*(x[(i+1)%np]-x[i])/(yf-yi)); num_inters++; } } ordena(vxs,0,num_inters-1); /* ordena as interseções */ for (i=0;i<num_inters;i+=2) if (vxs[i]+1 < vxs[i+1]) HLine(vxs[i],vxs[i+1],ys); } // Exceto o ultimo pixel Otimizações do algoritmo de fill y ymax x = x+dx ys+1 ys dy = 1 ymin x0 Lista de Arestas struct edge { int y_max; int y_min; float xs; x1 x (ou z) dx = (x1-x0)/(ymax-ymin) /* maior y da aresta */ /* menor y da aresta */ /* x correspondente a ys */ /* (no início é o correspondente a y_max) */ float delta_xs; /* incremento de xs entre duas linhas de scan */ }; Algoritmo de Fill de Polígonos (Parte 1-Alocação de Memória) #define MAX(a,b) #define MIN(a,b) (((a) > (b)) ? (a) : (b)) (((a) < (b)) ? (a) : (b)) void fill (int np, int *x, int *y) { static struct edge *aresta=NULL; /* vetor de arestas */ static int *vxs=NULL; /* vetor de interseções */ static int old_np=0; /* número de pontos da última chamada */ int int int int int ymax, ymin; num_inters; num_arestas; ys; i; /* /* /* /* limites do polígono */ num. de interseções */ num. de arestas */ ordenada da reta de scan */ /* realoca os vetores de arestas e de interseções */ if (np > old_np) { old_np=np; if (vxs) free (vxs); if (aresta) free (aresta); vxs=(int *) malloc ((np-1)*sizeof(int)); /* max num. De inters.*/ aresta=(struct edge *) malloc (np*sizeof(struct edge)); } /* CONTINUA NA PARTE 2 */ Algoritmo de Fill de Polígonos (Parte 2-Lista de Arestas) /* PARTE 1*/ /* calcula y max e min e monta o vetor de arestas */ ymax = y[0]; ymin = y[0]; num_arestas = 0; for(i=0;i<np;i++) { int i1=(i+1)%np; if (y[i] != y[i1]) { aresta[num_arestas].y_max = MAX(y[i],y[i1]); aresta[num_arestas].y_min = MIN(y[i],y[i1]); aresta[num_arestas].delta = ((float)(x[i1]-x[i])/(float)(y[i1]-y[i])); if (aresta[num_arestas].y_mim == y[i]) aresta[num_arestas].xs = x[i]; else aresta[num_arestas].xs = x[i1]; ymax = MAX( ymax, aresta[num_arestas].y_max ); ymin = MIN( ymin, aresta[num_arestas].y_min ); num_arestas++; } } /* CONTINUA NA PARTE 3 */ Algoritmo de Fill de Polígonos (Parte 2-Lista de Arestas) /* PARTE 1*/ Ignorando arestas horizontais /* calcula y max e min e monta o vetor de arestas */ ymax = y[0]; ymin = y[0]; num_arestas = 0; for(i=0;i<np;i++) { int i1=(i+1)%np; if (y[i] != y[i1]) { aresta[num_arestas].y_max = MAX(y[i],y[i1]); aresta[num_arestas].y_min = MIN(y[i],y[i1]); aresta[num_arestas].delta = ((float)(x[i1]-x[i])/(float)(y[i1]-y[i])); if (aresta[num_arestas].y_mim == y[i]) aresta[num_arestas].xs = x[i]; else aresta[num_arestas].xs = x[i1]; ymax = MAX( ymax, aresta[num_arestas].y_max ); ymin = MIN( ymin, aresta[num_arestas].y_min ); num_arestas++; } } /* CONTINUA NA PARTE 3 */ Algoritmo de Fill de Polígonos (Parte 2-Lista de Arestas) /* PARTE 1*/ min e max da aresta /* calcula y max e min e monta o vetor de arestas */ ymax = y[0]; ymin = y[0]; num_arestas = 0; for(i=0;i<np;i++) { int i1=(i+1)%np; if (y[i] != y[i1]) { aresta[num_arestas].y_max = MAX(y[i],y[i1]); aresta[num_arestas].y_min = MIN(y[i],y[i1]); aresta[num_arestas].delta = ((float)(x[i1]-x[i])/(float)(y[i1]-y[i])); if (aresta[num_arestas].y_mim == y[i]) aresta[num_arestas].xs = x[i]; else aresta[num_arestas].xs = x[i1]; ymax = MAX( ymax, aresta[num_arestas].y_max ); ymin = MIN( ymin, aresta[num_arestas].y_min ); num_arestas++; } } /* CONTINUA NA PARTE 3 */ Algoritmo de Fill de Polígonos (Parte 2-Lista de Arestas) /* PARTE 1*/ min e max do polígono /* calcula y max e min e monta o vetor de arestas */ ymax = y[0]; ymin = y[0]; num_arestas = 0; for(i=0;i<np;i++) { int i1=(i+1)%np; if (y[i] != y[i1]) { aresta[num_arestas].y_max = MAX(y[i],y[i1]); aresta[num_arestas].y_min = MIN(y[i],y[i1]); aresta[num_arestas].delta = ((float)(x[i1]-x[i])/(float)(y[i1]-y[i])); if (aresta[num_arestas].y_mim == y[i]) aresta[num_arestas].xs = x[i]; else aresta[num_arestas].xs = x[i1]; ymax = MAX( ymax, aresta[num_arestas].y_max ); ymin = MIN( ymin, aresta[num_arestas].y_min ); num_arestas++; } } /* CONTINUA NA PARTE 3 */ Algoritmo de Fill de Polígonos (Parte 3-Varredura) /* PARTES 1 E 2 */ for(ys=ymin; ys<ymax; ys++) /* para cada linha de scan */ { num_inters = 0; for(i=0; i<num_arestas; i++) { if (aresta[i].y_max < ys) /* retira da lista de arestas */ { aresta[i] = aresta[num_arestas-1]; num_arestas--; } if((ys>=aresta[i].y_min)&&(ys<aresta[i].y_max)) /* intersepta */ { vxs[num_inters] = aresta[i].xs; aresta[i].xs += aresta[i].delta; /* atualiza o xs */ num_inters++; } } /* for */ ordena(vxs,0,num_inters-1); for(i=0;i<num_inters;i+=2) if (vxs[i]+1 <= vxs[i+1]) hline(vxs[i],vxs[i+1],ys,0xff); } } /* fill */ /* FIM */ /* ordena as interseções */ Algoritmo de Fill de Polígonos (Parte 3-Varredura) /* PARTES 1 E 2 */ for(ys=ymin; ys<ymax; ys++) /* para cada linha de scan */ { num_inters = 0; for(i=0; i<num_arestas; i++) { if (aresta[i].y_max < ys) /* retira da lista de arestas */ { aresta[i] = aresta[num_arestas-1]; num_arestas--; } Descarta definitivamente as arestas que já foram scaneadas if((ys>=aresta[i].y_min)&&(ys<aresta[i].y_max)) /* se intersepta */ { vxs[num_inters] = aresta[i].xs; /* guarda o x da intersecao */ aresta[i].xs += aresta[i].delta; /* atualiza o xs */ num_inters++; } } /* for */ ordena(vxs,0,num_inters-1); for(i=0;i<num_inters;i+=2) if (vxs[i]+1 <= vxs[i+1]) hline(vxs[i],vxs[i+1],ys,0xff); } } /* fill */ /* FIM */ /* ordena as interseções */ Algoritmo de Fill de Polígonos (Parte 3-Varredura) /* PARTES 1 E 2 */ for(ys=ymin; ys<ymax; ys++) /* para cada linha de scan */ { num_inters = 0; for(i=0; i<num_arestas; i++) { if (aresta[i].y_max < ys) /* retira da lista de arestas */ { aresta[i] = aresta[num_arestas-1]; num_arestas--; } Aceita interseção com vértice se for ymin, mas não se ymax if((ys>=aresta[i].y_min)&&(ys<aresta[i].y_max)) /* se intersepta */ { vxs[num_inters] = aresta[i].xs; /* guarda o x da intersecao */ aresta[i].xs += aresta[i].delta; /* atualiza o xs */ num_inters++; } } /* for */ ordena(vxs,0,num_inters-1); for(i=0;i<num_inters;i+=2) if (vxs[i]+1 <= vxs[i+1]) hline(vxs[i],vxs[i+1],ys,0xff); } } /* fill */ /* FIM */ /* ordena as interseções */ Stipples, patterns e imagens Stipple void SetPixel(int x,int y) { int i=(x-x0)%w; int j=(y-y0)%h; if (stipple[i][j]) { Pixel(x,y,foreground); } else { if (backopacity) Pixel(x,y,background); } h=5 w=5 } Pattern void SetPixel(int x,int y) { color = pattern[(x-x0)%w][(y-y0)%h] Pixel(x,y,color); } Comentários • Os triângulos podem ser tratados como um caso especial (mais eficiente). y A yA c b yB B aa yC • Slivers: triângulos muito finos. C xl • Polígonos simétricos podem não ser desenhados simetricamente! • Pintar somente o interior quando as arestas já tiverem sido desenhadas. • Existem situações em que um pixel pode pertencer a dois polígonos.(?) • Como melhorar este algoritmo? AET (Active Edge Table) • Foley: excelente para Bresenham, poly fill e clipping (próxima aula). xr x Active Edge Table Algorithm .. . 0 1 2 3 4 5 6 7 4 3 2 1 0 y=0 ET 4 3 2 1 0 AET 2 3 -1 1/2 4 0 0 4 6 0 4 3 -3/2 2 3 4 3 3/2 1 1/2 Draw from 0, 3-3 (not including 3) Aka: Do nothing Active Edge Table Algorithm .. . 0 1 2 3 4 5 6 7 4 3 2 1 0 y=1 ET 4 3 2 1 0 AET 2 1 1/2 -1 1/2 Draw from 1, 2-5 (not inclusive) 4 0 0 4 6 0 4 3 -3/2 4 3 3/2 2 4 1/2 1 1/2 Active Edge Table Algorithm .. . 0 1 2 3 4 5 6 7 4 3 2 1 0 y=2 ET 4 3 2 1 0 AET 4 0 4 0 3 Draw from 2, 0-3 and 3,6 1 1/2 4 3 4 -1 1/2 6 0 Active Edge Table Algorithm .. . 0 1 2 3 4 5 6 7 4 3 2 1 0 y=3 ET 4 3 2 1 0 AET 4 0 0 4 4 1/2 1 1/2 Draw from 3, 0-2 and 5,6 4 1 1/2 -1 1/2 4 6 0 Active Edge Table Algorithm .. . 0 1 2 3 4 5 6 7 4 3 2 1 0 y=4 ET 4 3 2 1 0 AET Done!