LinkedIn YouTube Facebook
Szukaj

Wstecz
Artykuły

ZL31ARM: implementacja funkcji graficznych 2D

Rysowanie okręgu i koła

Dana operacja wymaga wykorzystania funkcji sinus i cosinus. Można je zrealizować w postaci tablicy wartości dla określonych kątów albo też wykorzystać gotowe sin() i sin(), dostępne w bibliotece z funkcjami matematycznymi math.h., i które przyjmują wartości w radianach.
W danym projekcie zostały wykorzystane gotowe funkcje, jednak jeżeli czas obliczeń jest istotny to można wykorzystać dwie tablice typu float (dostępne w przykładowym rozwiązaniu): sinus[] i cosinus[]. W tym przypadku pomocną okaże się funkcja konwertująca kąt zadawany w stopniach na indeks elementu w tablicy:

unsigned char Deg2Index(signed short angle){
	if(abs(angle) >= 360)
		angle = angle % 360;	//Na wypadek wystapienia kata >=360 stopni

	if(angle >= 0)
		angle = (unsigned char) 128*abs(angle)/360;
	else if(angle < 0)
		angle = 128 - (unsigned char) 128*abs(angle)/360;

	return angle;
}

Funkcja przyjmuje wartości (również ujemne) kąta w stopniach i w razie potrzeby konwertuje ją do przedziału od 0 do ±359 stopni. Dalej obliczany jest indeks odpowiedniego elementu w tablicy i w zależności od tego czy kąt jest ujemny czy dodatni, indeks jest brany odpowiednio od końca lub od początku tablicy.
Rysowanie okręgu/koła polega na podaniu punktu środkowego oraz promienia. Oprócz tego funkcja przyjmuje 2 dodatkowe parametry, tak jak poprzednio, kolor i styl (w przypadku przekazania 1 jest rysowane koło wypełniane zadanym kolorem). Okrąg jest budowany przez wyznaczanie kolejnych punktów na obwodzie i ich łączeniu. Natomiast rysowanie koła wykonuje się za pomocą funkcji drawLine(), która to rysuje kolejne linie zawarte na płaszczyźnie ograniczonej przez obwód koła i w efekcie otrzymuje się zapełnioną zadanym kolorem figurę. Struktura funkcji drawCircle() jest następująca:

void drawCircle(int x0, int y0, int r, unsigned int color, unsigned char style){
	unsigned short alpha = 360;	//Pelny kat poczatkowy
	unsigned int i;
	signed int xp, yp, xn, yn; 	//Zmienne tymczasowe

	switch(style){
		//Rysuj kolo z wypelnieniem zadanym kolorem
		case(1):{
			for(i=0; i < 2*r; i++){
			  yn = (int) sqrt(r*r - abs(r-i)*abs(r-i));	//Z tw. Pitagorasa
			  drawLine(x0-r+i,y0+yn,x0-r+i,y0-yn,color);
			}
		}
		//Rysuj okrag z zadanym kolorem
		default:{
			//Wartosci poczatkowe - poprzednie
			xp = r;
			yp = 0;

			//Rysuj punkty na okregu co 1 stopien
			while(alpha != 0){
			 //Nowe wartosci obroconego punktu na okregu
			 xn = (int) r*cos(alpha*6.28318/360);
			 yn = (int) r*sin(alpha*6.28318/360);

			 //Polacz punkt poprzedni i kolejny
			 drawLine(xp+x0,yp+y0,xn+x0,yn+y0,color);

			 //Zachowaj stare wspolrzedne
			 xp = xn;
			 yp = yn;

			 alpha--;	//Kolejny kat
			}
		}
	}
}

W celu zobrazowania działania funkcji można wykonać następujący przykład:

// Rysowanie pustych okregow testowych 
drawCircle(-30, 30, 30,RED);
drawCircle(  0,  0, 20,GREEN);
drawCircle( 30,-30, 10,BLUE);

lub:

// Rysowanie kolorowych kol testowych
drawCircle(-30, 30, 30,RED,1);
drawCircle(  0,  0, 20,GREEN,1);
drawCircle( 30,-30, 10,BLUE,1);

Rysowanie elipsy i owalu

Funkcja realizująca daną operację jest bardzo podobna do poprzedniej, jednak w obecnym przypadku zamiast podawania jednego promienia należy podać ich dwa. Pierwszy jest liczony w kierunku osi x (parametr a), a drugi w osi y (parametr b). Mimo takiej samej metody rysowania konturu figury jak w przypadku poprzednim (z wyjątkiem występowania dwóch promieni), sposób rysowania wraz z wypełnianiem jest nieco inne, a mianowicie różnica polega na zastosowaniu innego wzoru w postaci kanonicznej:

 

 

Niżej przedstawiono kod funkcji drawEllipse():

void drawEllipse(int x0, int y0, int a, int b, unsigned int color,
   unsigned char style){
	unsigned short alpha = 360;
	signed int i;
	signed int xp, yp, xn, yn;	//Zmienne tymczasowe

	switch(style){
		//Rysuj owal z wypelnieniem zadanym kolorem
		case(1):{
			for(i=0; i < 2*a; i++){
			 yn = (int) sqrt( b*b*( 1.0 - ( (float) (i-a)*(i-a))/(a*a) ) );
			 drawLine(x0-a+i,y0+yn,x0-a+i,y0-yn,color);
			}
		}
		//Rysuj elipse z zadanym kolorem
		default:{
			//Wartosci poczatkowe - poprzednie
			xp = a;
			yp = 0;

			//Rysuj punkty na okregu co 1 stopien
			while(alpha != 0){
			 //Nowe wartosci wspolrzednych punktu na obwodzie elipsy
			 xn = (int) a*cos(alpha*6.28318/360);
			 yn = (int) b*sin(alpha*6.28318/360);

			 //Polacz punkt poprzedni i kolejny
			 drawLine(xp+x0,yp+y0,xn+x0,yn+y0,color);

			 //Zachowaj stare wspolrzedne
			 xp = xn;
			 yp = yn;

			 alpha--;	//Kolejny kat
			}
		}
	}
}
Autor: Jan Szemiet
Tagi: ARM, BTC, GPIO, MEMS, STM32