Bitmaps em AdvPL – Parte 02

Introdução

No post anterior, vimos a primeira versão de uma implementação para ler e modificar um arquivo Bitmap monocromático. Agora, vamos ver alguns detalhes de como a implementação foi projetada, e alguns aspectos de “como desenhar” no Bitmap em memória.

O array aMatrix

Criado como uma propriedade da classe ZBITMAP, ele é um array bidimensional de números inteiros. Ele é acessado por dois índices, o primeiro pelo número da linha, e o segundo pela coluna, e seu conteúdo é o número da cor correspondente daquele ponto (pixel) na imagem. Como a convenção adotada para a imagem começa na linha 0 e coluna 0, e os Arrays em AdvPL são “base 1” — o primeiro elemento é 1, e não 0 — os métodos que encapsulam o acesso somam 1 na linha e coluna.

// -------------------------------------------------
// Retorna a cor de um pixel 
// linha e coluna, base 0 
METHOD GETPIXEL(nRow,nCol) CLASS ZBITMAP
Return ::aMatrix[nRow+1][nCol+1]

Cada número corresponde a uma cor. Em um Bitmap monocromático, 0 (zero) corresponde a cor preta, e 1 (um) a cor branca. Já em um Bitmap de 16 cores — 4 bits per pixel — a cor branca é 15 (quinze), e as demais cores usadas estão em uma tabela gravada após o header do arquivo. Veja abaixo a representação da tabela de cores para um Bitmap

Desenhando um círculo

Já vamos começar do exemplo um pouco mais complexo — e ver que não é tão complexo assim. Para desenhar um círculo, é necessário saber no mínimo quais são as coordenadas do centro do círculo, e a medida do raio do círculo a ser usada. Com estas informações, e usando a constante PI ( 3.14159265 ), descobrir o tamanho da circunferência a ser traçada.

Uma vez que o raio é informado em pontos (ou pixels), a medida da circunferência também será em pixels. Por exemplo, para desenhar um círculo de raio 20 serão necessários pelo menos 126 pontos (125,663694 arredondado para cima), lembrando que a fórmula de cálculo da circunferência é 2 x Pi x R , onde R é o raio da circunferência, e Pi é a constante 3.14159265 — usada na implementação com 8 casas decimais de precisão.

Seno e Cosseno

Em um círculo, a partir de seu centro, ao traçar duas retas perpendiculares cruzando o centro do círculo, vamos obter quatro “quadrantes”. Em cada reta, que vamos chanar de eixos (x e y), usando as funções de Seno e Cosseno de um ângulo — representado em radianos (frações ou múltiplos de PI), obtemos em cada função ( Seno e Cosseno ) um número decimal entre 0 e 1 , que corresponde a posição de um ponto relativo no eixo entre o centro (0,0) e uma das extremidades — formadas com o cruzamento do círculo com o eixo. Veja o exemplo abaixo:

Seno-Cosseno

Para um ângulo a formado entre uma semi-reta — traçada a partir do centro do círculo — e o eixo X do lado direito do círculo trigonométrico — que corresponde ao ângulo 0 (Zero), em um determinado ponto esta semi-reta cruza com um ponto do círculo ( realçado na cor roxa). Para descobrirmos exatamente em que posição este ponto se encontra em relação ao ponto do centro do círculo, a função trigonométrica de Cosseno() — ou  COS() em AdvPL — retorna um número decimal entre 0 e 1, onde 0 é o centro do círculo e 1 é o ponto de cruzamento do eixo X com o círculo. A distância entre estes dois pontos é o raio da circunferência. Para descobrir o ponto relativo no eixo Y (vertical), usamos a função Seno() — ou SIN() em AdvPL.

Números e números

Um ângulo pode ser representado em graus, onde uma circunferência pode ser dividida em 360 graus e suas frações. Os ângulos também podem ser representados em radianos, que representam frações de PI. Uma circunferência possui 2 x PI ângulos. Logo, podemos converter ângulos entre graus e radianos, partindo da proporção que 360 graus está para 2 x PI como n Graus está para r Radianos.

Vamos por exemplo calcular a posição relativa de um ponto no círculo, que corresponde ao cruzamento de uma semi-reta em um círculo de 20 pontos de raio, formando um angulo de 30 graus entre a semi-reta e o eixo X. Primeiro, as funções SIN() e COS() — seno  e cosseno — em AdvPL recebem como parâmetro um ângulo em radianos. Logo:

20 graus = r radianos
360 graus = 2 x PI 

( multiplica em cruz ) 
360 x r = 2 x PI x 20 
r = ( 2 x 20 x PI) / 360
r = 0.34906585   // 30 graus em radianos

Cos(r) = 0.93969262 // posição no eixo X 
Sin(r) = 0.34202014 // posição no eixo Y

// Lembrando que a posição relativa parte que o centro é a posição 0
// e a extremidade é a posição 1, então precisamos multiplicar os 
// resultados pelo raio (20)

nX = 20 x 0.93969262 = 18.7938524 ( arredonda para 19 ) 
nY = 20 x 0.34202014 = 6.8404028  ( arredonda para 7 )  

// Logo, o ponto correspondente no círculo está na coordenada 7,19 
// em relação ao centro do círculo ( coordenada 0,0 ). Se o círculo está 
// traçado com o centro nas coordenadas 80,80 (linha,coluna), o ponto 
// está nas coordenadas 80-7 (73) e 80+19 (99)

Agora fica fácil

Determinado o comprimento do círculo em pontos, sabemos quantos “cruzamentos” serão necessários para desenhar os pontos que compõe a circunferência. Pegamos o valor de 2 x PI ( correspondendo aos ângulos de um circulo inteiro) e dividimos a quantidade de pontos a traçar por este valor, e chegamos a uma fração de ângulo, que ao ser usada como incremento a partir do ângulo zero, para cada ponto a ser traçado, corresponde ao ângulo deste ponto. Dessa forma, o método de traçar um círculo plotando ponto a ponto fica assim:

METHOD Circle(nL , nC , nRadius , nColor, nPen ) CLASS ZBITMAP
Local nRow , nCol
Local nAngle
Local nPoints
Local nStep
Local nI,nR

If nPen = NIL
	nPen := ::nPenSize
Endif

For nR := 1 to nPen
	// Seno e cosseno em Radianos
	// Faz o loop de 0 a 2*PI para calcular as coordenadas
	// dos pontos para desenhar o círculo
	nAngle := 0
	nPoints := 2 * PI * nRadius
	nStep   := (2*PI) / nPoints
	For nI := 0 to nPoints
		nRow := round(Sin(nAngle) * nRadius,1)
		nCol := round(Cos(nAngle) * nRadius,1)
		::SetPixel(nL-nRow,nC+nCol,nColor)
		nAngle += nStep
	Next
	nRadius--
Next

Return

Observações

As funções de Seno e Cosseno retornam também números negativos, cobrindo todos os quadrantes. Um ângulo entre 90 e 180 graus resulta em um ponto no quadrante superior esquerdo, entre 180 e 270 graus no quadrante inferior esquerdo, e entre 270 e 360 graus, no quadrante inferior direito.

Conclusão

Pode parecer um pouco complicado, mas depois que você entende as regras de uso das funções e como elas funcionam, o resultado não é complicado — apenas parece para quem não conhece.

Agradeço novamente a todos pela audiência e desejo a todos TERABYTES DE SUCESSO 😀

Referências

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s