Computação Gráfica Gerando Circunferências Há várias maneiras de gerar circunferências em um dispositivo raster (como o monitor de vídeo). Da mesma maneira que fizemos para gerar segmentos (linhas), vamos discutir dois algoritmos que irão determinar quais pixels devem ser “acesos” para representarmos um círculo. A primeira coisa que devemos saber é que uma circunferência de raio R e centro na origem (x,y)=(0,0), pode ser representada pela equação (no primeiro quadrante). Ver figura 1: Figura 1: Nos algoritmos que discutiremos a seguir, vamos nos preocupar somente com o primeiro quadrante, os demais quadrantes serão gerados por simetria (como veremos mais tarde). • Primeiro algoritmo Vamos incrementar x (inteiro) de 0 até R (um a um), determinando y (real) resolvendo a equação da circunferência para o primeiro quadrante. Aproximamos o valor de y para um valor inteiro Yn e “acendemos “ o pixel (x,Yn). Porém lembrando, o quadrante gerado terá origem em (0,0). Para desenharmos uma circunferência de raio R e centro em um ponto qualquer (Xc,Yc), devemos assim que determinarmos o pixel (x, Yn), uma translação para um novo pixel relacionado ao centro (Xc,Yc) e não mais a origem (0,0). A figura 2 a seguir ilustra essa translação: Prof Humberto B. Pinheiro Página 1 Figura 2 Para gerarmos os demais quadrantes, podemos aproveitar a simetria da circunferência (figura 3): figura 3 Assim, determinando pela equação um pixel no primeiro quadrante, podemos “acendê-lo” e ao mesmo tempo os outros três pixels simétricos. Não esqueçamos que há a necessidade de transladarmos também os pixels simétricos (que foram definidos em relação ao (0,0) ) para uma circunferência de centro em (Xc,Yc) diferente de (0,0), como mostrado na figura 4 a seguir: Prof Humberto B. Pinheiro Página 2 figura 4 A figura 4 mostra uma circunferência de raio R e centro em (0, 0) gerada pelo algoritmo, sendo transladada para a posição relativa ao centro de circunferência (Xc, Yc). Dessa forma o algoritmo fica assim: • • • • • • • • • • • • INPUT: raio R (inteiro), coordenada do centro da circunferência Xc,Yc (inteiros) x recebe 0 (x := 0) repetir até x > R (ou para x variando de 0 até R) x inteiro y := RAIZ_QUADRADA( R*R - x*x) y real yn := APROXIMAR(y) yn inteiro ACENDE_PIXEL(Xc + x, Yc + yn) { primeiro quadrante} ACENDE_PIXEL(Xc - x, Yc + yn) { segundo quadrante} ACENDE_PIXEL(Xc - x, Yc - yn) { terceiro quadrante} ACENDE_PIXEL(Xc + x, Yc - yn) { quarto quadrante} incrementa x (x:= x+1) fim de repetir fim do algoritmo *** Esse algoritmo apresenta algumas deficiências: 1 • Ineficiência computacional devido ao cálculo da raiz quadrada e operações de multiplicação. • O desenho da circunferência fica descontínuo, com largos espaços (1GAPS) a medida que x se aproxima de R. Nesse texto, GAP é o espaço entre dois pixels “acesos” que fazem partede uma mesma figura. Prof Humberto B. Pinheiro Página 3 figura 5 Na figura 5 podemos notar que os pixels se distanciam à medida que x se aproxima de R. Isso ocorre porque utilizamos uma equação que determina o y a partir do x e dessa forma, para cada x (coluna) temos um único y (linha). Prof Humberto B. Pinheiro Página 4 • Segundo algoritmo Um outro algoritmo um pouco mais eficiente, e com a vantagem de diminuir os GAPS gerados no algoritmo anterior, é fazendo x= R.cos(θ) e y= R.sen(θ), variando o ângulo θ de 0o a 90o (1o quadrante): figura 6 Nesse algoritmo não há necessidade de utilizarmos a equação da circunferência para determinarmos o valor de y, pois tanto x quanto y são definidos a partir do ângulo θ. Além disso, o incremento do ângulo (de 0o a 90o ) não precisa ser necessariamente de um em um, mas de um valor ∆θ (real) qualquer. Quanto menor for ∆θ menores são os GAPS gerados no desenho e dessa forma mais perfeita será a circunferência gerada, por outro lado mais tempo se leva para gerá-la. Ver figura 6. Da mesma forma que aconteceu com o outro algoritmo, há a necessidade de transladarmos os pontos gerados caso desejemos gerar uma circunferência de centro em (Xc,Yc) diferente da origem (0,0). Também podemos aproveitar a simetria da circunferência, como ocorria no algoritmo anterior, para determinarmos os outros 3 pontos simétricos àquele gerado no primeiro quadrante. Dessa forma o algoritmo fica assim: • INPUT: raio R (inteiro), coordenada do centro da circunferência Xc,Yc (inteiros), ∆θ (real) • • • • θ recebe 0 (θ := 0) repetir até θ > 90 (ou para θ variando de 0 até 90 ) y := R.senθ yn := APROXIMAR(y) Prof Humberto B. Pinheiro θ real y real yn inteiro Página 5 • • • • • • • • • x := R.cosθ xn := APROXIMAR(x) x real xn inteiro ACENDE_PIXEL(Xc + xn, Yc + yn) { primeiro quadrante} ACENDE_PIXEL(Xc - xn, Yc + yn) { segundo quadrante} ACENDE_PIXEL(Xc - xn, Yc - yn) { terceiro quadrante} ACENDE_PIXEL(Xc + xn, Yc - yn) { quarto quadrante} θ := θ + ∆θ (incrementa θ) fim de repetir fim do algoritmo A figura 7 mostra como seria um quarto de circunferência (1o quadrante) gerado por esse algorítmo, com um ∆θ devidamente “calibrado”. Lembremos que quanto menor for o valor de ∆θ, mais perfeita fica a circunferência (diminuição dos GAPS) , todavia maior é o custo computacional (tempo de execução) figura 7 • Melhoramentos para ambos os algoritmos Como discutimos anteriormente, aproveitamos a simetria da circunferência para que determinando os pontos (pixels) do primeiro quadrante, tivéssemos simultaneamente os respectivos pontos simétricos nos demais quadrantes. Em ambos os algoritmos podemos melhorar esse procedimento se falarmos em “octantes” e não mais em quadrantes. Na próxima figura veremos a circunferência dividida em 8 “octantes” cada um abrangendo uma área referente a 45o (90o / 2) de arco. Dessa forma, se notarmos bem, o primeiro quadrante foi dividido em dois octantes (1o e 2o ) que já possuem uma simetria entre eles, ou seja para cada ponto (x,y) no primeiro octante existe um ponto simétrico no segundo octante na coordenada (y,x). Então tendo dois pontos no 1o quadrante , (x,y) do 1o octante e (y,x) do 2o octante, basta acharmos os seus simétricos nos demais quadrantes, como mostra a figura 8. Prof Humberto B. Pinheiro Página 6 figura 8 Melhorando o primeiro algoritimo Poderíamos implementar o primeiro algoritmo variando x de 0 a R.cos(45o ) e “acendendo” 8 pixels de uma vez: • • • • • • • • • • • • • • • • INPUT: raio R (inteiro), coordenada do centro da circunferência Xc,Yc (inteiros) x recebe 0 (x := 0) repetir até x > R.cos(45o ) (ou para x variando de 0 até R.cos(45o) ) x inteiro y := RAIZ_QUADRADA( R*R - x*x) y real yn := APROXIMAR(y) yn inteiro ACENDE_PIXEL(Xc + x, Yc + yn) {octantes ímpares} ACENDE_PIXEL(Xc - x, Yc + yn) ACENDE_PIXEL(Xc - x, Yc - yn) ACENDE_PIXEL(Xc + x, Yc - yn) ACENDE_PIXEL(Xc + yn, Yc + x) {octantes pares} ACENDE_PIXEL(Xc - yn, Yc + x) ACENDE_PIXEL(Xc - yn, Yc - x) ACENDE_PIXEL(Xc + yn, Yc - x) incrementa x (x:= x+1) fim de repetir fim do algoritmo Prof Humberto B. Pinheiro Página 7 Melhorando o segundo algoritimo Poderíamos implementar o segundo algoritmo variando θ de 0o a 45o e “acendendo” 8 pixels de uma vez: • INPUT: raio R (inteiro), coordenada do centro da circunferência Xc,Yc (inteiros), ∆θ (real) • • • • • • • • • • • • • • • • • θ recebe 0 (θ := 0) repetir até θ > 45o (ou para θ variando de 0 até 45o ) y := R.senθ θ real y real yn := APROXIMAR(y) x := R.cosθ xn := APROXIMAR(x) ACENDE_PIXEL(Xc + xn, Yc + yn) yn inteiro x real xn inteiro {octantes pares} ACENDE_PIXEL(Xc - xn, Yc + yn) ACENDE_PIXEL(Xc - xn, Yc - yn) ACENDE_PIXEL(Xc + xn, Yc - yn) ACENDE_PIXEL(Xc + yn, Yc + xn) {octantes ímpares} ACENDE_PIXEL(Xc - yn, Yc + xn) ACENDE_PIXEL(Xc - yn, Yc - xn) ACENDE_PIXEL(Xc + yn, Yc - xn) θ := θ + ∆θ (incrementa θ) fim de repetir fim do algoritmo Ficou claro que em ambos os algoritmos haverá, em maior ou menor grau, espaços entre pontos consecutivos em um mesmo quadrante (GAP’s) deixando a circunferência com um aspecto descontínuo. Para minimizar essa sensação, podemos gerar linhas entre esses pontos consecutivos, que embora possa criar faces retilíneas (facetadas) na “circunferência”, acaba com a descontinuidade. Ver figura 9 Prof Humberto B. Pinheiro Página 8 figura 9 Na figura 9 é tomado o exemplo do primeiro algoritmo (ver figura 5), onde há naturalmente a formação de “GAP’s”. Os pixels pretos na figura 9 são os pixels acesos pelo primeiro algoritmo de circunferência. As linhas vermelhas são os segmentos de reta reais que unem cada um desses pixels pretos consecutivos. Os pixels em branco (vazados) são os pixels acesos por um algoritmo de geração de linhas qualquer, aplicados entre cada pixel preto. Notar que os pixels brancos são a representação discreta das linhas vermelhas e que alguns pixels brancos coincidem com pixels pretos. Dessa forma, os pixels gerados pelo algoritmo de linhas (pixels brancos) preenchem os “GAP’s” dando aspecto contínuo à circunferência. Isso faz com que a figura gerada seja completa embora deixe-a “facetada”. Lembrando que para o primeiro algoritmo essa medida é extremamente necessária pois a formação de GAP’s é inerente a ele. Para o segundo algoritmo, dependendo do ∆θ, pode não haver GAP’s, porém isso é difícil de prever levando à necessidade de implementação desse melhoria. Deixarei a cargo do aluno, alterar os dois algoritmos a fim de incluir a geração de linhas entre pixels consecutivos. Exercícios 1) Altere o primeiro algoritmo (com simetria por octante) implementando o método de eliminação de GAP’s (gerando linhas). Utilizar o procedimento LINE (xi, yi, xf, yf) para gerar os segmentos. 2) Altere o segundo algoritmo (com simetria por octante) implementando o método de eliminação de GAP’s (gerando linhas). Utilizar o procedimento LINE (xi, yi, xf, yf) para gerar os segmentos. Prof Humberto B. Pinheiro Página 9