Clipping de linhas e polígonos O que é clipar? Viewport O que é clipar? Cortar o excesso de modo a ficar apenas com o que interessa! Cohen-Sutherland Clipping 1001 1000 0001 0000 0010 0101 0100 0110 1010 1º passo: Codificar cada vértice segundo sua região... Cohen-Sutherland Clipping Cálculo do código de um vértice: #define #define #define #define LEFT RIGHT BOTTOM TOP 1 2 4 8 //0001 //0010 //0100 //1000 unsigned char CompCode(double x, double y, double xmin, double xmax, double ymin, double ymax) { unsigned char code=0; if (y > ymax) code += TOP; else if(y < ymin) code += BOTTOM; if (x > xmax) code += RIGHT; else if(x < xmin) code += LEFT; return code; } 1001 1000 1010 0001 0000 0010 0101 0100 0110 • AND binário dos códigos dos dois vértices de uma aresta: se diferente de zero pode ser descartado. • OR binário dos códigos dos dois vértices de uma aresta: se igual a zero pode ser aceito (desenhado). • SENÃO, calcula-se interseção com borda, usando AND binário para saber qual das bordas está sendo vazada. void CohenSutherlandLineClipAndDraw(double x0, double y0, double x1, double y1, double xmin, double xmax, double ymin, double ymax, int value) { unsigned char code0, code1, outcodeOut; int accept = 1; // TRUE code0 = CompCode(x0, y0, xmin, xmax, ymin, ymax); code1 = CompCode(x1, y1, xmin, xmax, ymin, ymax); while ( code0 | code1 ) { if( code0 & code1 ) { code0 = code1 = accept = 0; } /* trivial reject and else { if ( code0 ) { if ( code0 & TOP ) { x0 = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0); y0 = else if( code0 & BOTTOM ) { x0 = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0); y0 = else if( code0 & RIGHT ) { y0 = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0); x0 = else if( code0 & LEFT ) { y0 = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0); x0 = code0 = CompCode(x0, y0, xmin, xmax, ymin, ymax); } else { if ( code1 & TOP ) { x1 = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0); y1 = else if( code1 & BOTTOM ) { x1 = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0); y1 = else if( code1 & RIGHT ) { y1 = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0); x1 = else if( code1 & LEFT ) { y1 = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0); x1 = code1 = CompCode(x1, y1, xmin, xmax, ymin, ymax); } } } if (accept) DrawLineReal(x0, y0, x1, y1, value); } exit */ ymax; } ymin; } xmax; } xmin; } ymax; } ymin; } xmax; } xmin; } void CohenSutherlandLineClipAndDraw(double x0, double y0, double x1, double y1, double xmin, double xmax, double ymin, double ymax, int value) { unsigned char code0, code1, outcodeOut; int accept = 1; // TRUE Enquanto pelo menos um dos vértices estiver fora do viewport... code0 = CompCode(x0, y0, xmin, xmax, ymin, ymax); code1 = CompCode(x1, y1, xmin, xmax, ymin, ymax); while ( code0 | code1 ) { if( code0 & code1 ) { code0 = code1 = accept = 0; } /* trivial reject and else { if ( code0 ) { if ( code0 & TOP ) { x0 = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0); y0 = else if( code0 & BOTTOM ) { x0 = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0); y0 = else if( code0 & RIGHT ) { y0 = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0); x0 = else if( code0 & LEFT ) { y0 = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0); x0 = code0 = CompCode(x0, y0, xmin, xmax, ymin, ymax); } else { if ( code1 & TOP ) { x1 = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0); y1 = else if( code1 & BOTTOM ) { x1 = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0); y1 = else if( code1 & RIGHT ) { y1 = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0); x1 = else if( code1 & LEFT ) { y1 = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0); x1 = code1 = CompCode(x1, y1, xmin, xmax, ymin, ymax); } } } if (accept) DrawLineReal(x0, y0, x1, y1, value); } exit */ ymax; } ymin; } xmax; } xmin; } ymax; } ymin; } xmax; } xmin; } void CohenSutherlandLineClipAndDraw(double x0, double y0, double x1, double y1, double xmin, double xmax, double ymin, double ymax, int value) { unsigned char code0, code1, outcodeOut; int accept = 1; // TRUE Se os vértices tiverem um bit em comum, significa que ambos estão externos ao viewport pelo mesmo lado! code0 = CompCode(x0, y0, xmin, xmax, ymin, ymax); code1 = CompCode(x1, y1, xmin, xmax, ymin, ymax); while ( code0 | code1 ) { if( code0 & code1 ) { code0 = code1 = accept = 0; } /* trivial reject and else { if ( code0 ) { if ( code0 & TOP ) { x0 = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0); y0 = else if( code0 & BOTTOM ) { x0 = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0); y0 = else if( code0 & RIGHT ) { y0 = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0); x0 = else if( code0 & LEFT ) { y0 = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0); x0 = code0 = CompCode(x0, y0, xmin, xmax, ymin, ymax); } else { if ( code1 & TOP ) { x1 = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0); y1 = else if( code1 & BOTTOM ) { x1 = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0); y1 = else if( code1 & RIGHT ) { y1 = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0); x1 = else if( code1 & LEFT ) { y1 = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0); x1 = code1 = CompCode(x1, y1, xmin, xmax, ymin, ymax); } } } if (accept) DrawLineReal(x0, y0, x1, y1, value); } exit */ ymax; } ymin; } xmax; } xmin; } ymax; } ymin; } xmax; } xmin; } void CohenSutherlandLineClipAndDraw(double x0, double y0, double x1, double y1, double xmin, double xmax, double ymin, double ymax, int value) { unsigned char code0, code1, outcodeOut; int accept = 1; // TRUE code0 = CompCode(x0, y0, xmin, xmax, ymin, ymax); code1 = CompCode(x1, y1, xmin, xmax, ymin, ymax); Se o vértice0 tem o bit TOP ligado... while ( code0 | code1 ) { if( code0 & code1 ) { code0 = code1 = accept = 0; } /* trivial reject and else { if ( code0 ) { if ( code0 & TOP ) { x0 = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0); y0 = else if( code0 & BOTTOM ) { x0 = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0); y0 = else if( code0 & RIGHT ) { y0 = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0); x0 = else if( code0 & LEFT ) { y0 = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0); x0 = code0 = CompCode(x0, y0, xmin, xmax, ymin, ymax); } else { if ( code1 & TOP ) { x1 = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0); y1 = else if( code1 & BOTTOM ) { x1 = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0); y1 = else if( code1 & RIGHT ) { y1 = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0); x1 = else if( code1 & LEFT ) { y1 = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0); x1 = code1 = CompCode(x1, y1, xmin, xmax, ymin, ymax); } } } if (accept) DrawLineReal(x0, y0, x1, y1, value); } exit */ ymax; } ymin; } xmax; } xmin; } ymax; } ymin; } xmax; } xmin; } void CohenSutherlandLineClipAndDraw(double x0, double y0, double x1, double y1, double xmin, double xmax, double ymin, double ymax, int value) { unsigned char code0, code1, outcodeOut; int accept = 1; // TRUE code0 = CompCode(x0, y0, xmin, xmax, ymin, ymax); code1 = CompCode(x1, y1, xmin, xmax, ymin, ymax); while ( code0 | code1 ) { if( code0 & code1 ) { code0 = code1 = accept = 0; } /* trivial reject and else { if ( code0 ) { if ( code0 & TOP ) { x0 = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0); y0 = else if( code0 & BOTTOM ) { x0 = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0); y0 = else if( code0 & RIGHT ) { y0 = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0); x0 = else if( code0 & LEFT ) { y0 = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0); x0 = code0 = CompCode(x0, y0, xmin, xmax, ymin, ymax); } else { if ( code1 & TOP ) { x1 = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0); y1 = else if( code1 & BOTTOM ) { x1 = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0); y1 = else if( code1 & RIGHT ) { y1 = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0); x1 = else if( code1 & LEFT ) { y1 = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0); x1 = code1 = CompCode(x1, y1, xmin, xmax, ymin, ymax); } } } if (accept) DrawLineReal(x0, y0, x1, y1, value); } exit */ ymax; } ymin; } xmax; } xmin; } Desloca o ponto para a interseção da reta com o lado vazado. ymax; } ymin; } xmax; } xmin; } void CohenSutherlandLineClipAndDraw(double x0, double y0, double x1, double y1, double xmin, double xmax, double ymin, double ymax, int value) { unsigned char code0, code1, outcodeOut; int accept = 1; // TRUE code0 = CompCode(x0, y0, xmin, xmax, ymin, ymax); code1 = CompCode(x1, y1, xmin, xmax, ymin, ymax); while ( code0 | code1 ) { if( code0 & code1 ) { code0 = code1 = accept = 0; } /* trivial reject and else { if ( code0 ) { if ( code0 & TOP ) { x0 = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0); y0 = else if( code0 & BOTTOM ) { x0 = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0); y0 = else if( code0 & RIGHT ) { y0 = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0); x0 = else if( code0 & LEFT ) { y0 = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0); x0 = code0 = CompCode(x0, y0, xmin, xmax, ymin, ymax); } else { if ( code1 & TOP ) { x1 = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0); y1 = else if( code1 & BOTTOM ) { x1 = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0); y1 = else if( code1 & RIGHT ) { y1 = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0); x1 = else if( code1 & LEFT ) { y1 = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0); x1 = code1 = CompCode(x1, y1, xmin, xmax, ymin, ymax); } } } if (accept) DrawLineReal(x0, y0, x1, y1, value); } exit */ ymax; } ymin; } xmax; } xmin; } ymax; } ymin; } xmax; } xmin; } A codificação deve ser recalculada, uma vez que um novo bit pode ser aceso. Algoritimo de Cyrus-Beck (Liang-Barsky) Para qualquer polígono convexo! N Ei P0 N Ei Pt PEi 0 P1 N Ei Pt PEi 0 N Ei Pt PEi 0 PEi Pt P0 ( P1 P0 ) t N Ei P t PEi 0 t N Ei P0 PEi N Ei P1 P0 Obs: Produto Escalar de Vetores Entrando ou Saindo ? Ei P0 P1 N Ei N Ei P1 P0 0 PE Ei P1 P0 N Ei N Ei P1 P0 0 PL Cálculo das interseções PL PE PE PE PL PL PL PE Cyrus-Beck - caso geral { precalculate Ni and select a PEi for each edge Ambos os vértices estão fora do polígono pelo lado da aresta corrente! for( each line segment to be clipped ){ if(P1 = P0) line is degenerate so clip as a point; else{ tE = 0; tL = 1; for( each candidate intersection with a clip edge ){ if( Ni . (P1-P0) !=0 ){ /* edges not parallel to line */ calculate t; use sign of Ni . (P1-P0) to categorize as PE or PL; if( PE )tE = max(tE, t); if( PL )tL = min(tL, t); } else if(Ni . (P0-PEi) > 0) return nil; } if(tE > tL) return nil; else return P(tE) and P(tL) as true clip intersections; } } } Liang e Barsky - caso particular - y ymax ymin xmin Ei left: x = xmin xmax x NEi PEi t (-1, 0) (xmin, y) -(x0-xmin) (x1-x0) right: x = xmax (1, 0) (xmax, y) (x0-xmax) -(x1-x0) bottom: y = ymin (0,-1) (x, ymin) -(y0-ymin) (y1-y0) top: y = ymax (0, 1) (x, ymax) (y0-ymax) -(y1-y0) Observe que sempre que o denominador for positivo, trata-se de um ponto de entrada (se negativo, ponto de saída) Algoritimo de Liang e Barsky boolean Clip2D(double *x0, double *y0, double *x1, double *y1, double xmin, double max, double ymin, double ymax) { double dx = *x1 - *x0; double dy = *y1 - *y0; boolean visible = FALSE; if (dx==0)&&(dy==0)&&ClipPoint(*x0,*y0,xmin,xmax,ymin,ymax) visible=TRUE; else { double tE=0.0, tL=1.0; O teste será feito para cada um dos 4 lados, sempre atualizando TE e TL if (Clipt(dx,xmin-*x0,&tE,&tL) ) if (Clipt(-dx,*x0-xmax,&tE,&tL) ) if (Clipt(dy,ymin-*y0,&tE,&tL) ) if (Clipt(-dy,*y0-ymax,&tE,&tL) ) { visible=TRUE; if (tL<1.0) { (*x1)=(*x0)+tL*dx; (*y1)=(*y0)+tE*dy; } if (tE>0.0) { (*x0)=(*x0)+tE*dx; (*y0)=(*y0)+tE*dy; } } } return visible; } Atualiza os valores dos vértices Liang e Barsky Cálculo da interseção em uma fronteira boolean Clipt(double den, double num, double *tE, double *tL) { double t; if (den > 0) { t = num/den; if (t > *tL) return FALSE; else if (t > *tE) *tE = t; } else if (den < 0) { t=num/den; if (t < *tE) return FALSE; else if (t < *tL) *tL = t; } else if (num > 0) return FALSE; return TRUE; } /* intersecao PE */ /* tE > tL */ /* intersecao PL */ /* tL < tE */ /* linha esta fora */ Clipping de polígonos (Hodgman & Suterland) • Clip contra uma aresta (plano) de cada vez Clipping de polígonos (Hodgman & Suterland) • Para cada aresta (plano) teste um segmento de cada vez • Quatro situações diferentes mostradas abaixo (dados o vértice de origem S e o vértice destino D de cada aresta do polígono) D S D S S store D D I store I I S store I, D D Clipping de polígonos (Exemplo 1) 2 A 3 1 6 4 B 5 S P Ação 1 2 3 4 5 6 2 3 4 5 6 1 x store A,3 store 4 store 5 store B x Clipping de polígonos (Exemplo 2) 2 Ax 3 6 x x D 1 x B S 1 2 3 4 5 6 C 4 5 B A D 1 C 5 4 P 2 3 4 5 6 1 Ação store A x store B,4 store 5 store C store D,1 Clipping de polígonos (Exemplo 2) Ax x x D 1 x C B x E x 5 F 4 B, E, F, 5, C, D, 1, A S A B 4 5 C D 1 P B 4 5 C D 1 A Ação store B store E store F,5 store C store D x store 1, A 2 Ax 3 6 x x B 3 C D 1 Ax x Clipping de polígonos (Concatenação de planos) 6 5 4 4 5 x x D x B C x E x 1 5 F 4 1 D C 5 4 B A 1 Ax x x D 1 x C B x E 2 first[0] S[0] P[0] first[1] S[1] P[1] x 5 F 1, A, B, E, F, 5, C, D Produto Escalar de Vetores • Dados dois vetores, v1[x1,y1] e v2[x2,y2] definidos no 2, o produto escalar v1•v2 é: v1•v2 = x1x2 + y1y2 • Podemos afirmar também a seguinte identidade: v1•v2 = |v1| |v2| cos , onde é o ângulo formado pelos vetores. • Se o resultado for positivo, o ângulo é agúdo, senão é obtuso. • Portanto o ângulo entre dois vetores pode ser calculado por: ângulo (v1,v2) = arc cos (v1•v2 / |v1| |v2|) • O ângulo retornado é um valor entre [0,]. • Se forem vetores unitários não é necessária a divisão pelo módulo • Em inglês se chama Dot Product