Rysowanie lini VGA
Nad wymyśleniem tej funkcji pracowałem jakieś 2 miesiące. Jakoś nie mogłem wpaść jak to zrobić. Ale do rzeczy.Jest to troszkę zagmatwane ale działa.
- Sprawdzamy czy odcinek nie jest pionowy.
- Tak:
- Wiemy że tangens jest równy nieskończoność.
- Sprwdzamy czy przypadkiem nie jest poziomy.
- Tak:
- Jeżeli są spełnione oba warunki to punkt końca i początka odcinka się pokrywa. Czyli rysujemy punkt.
Nie: - Jeżeli warunek poziomu nie jest spełniony to rysujemy pionową linię. Uzywamy funkcji lini używającej cotangensa.
- Jeżeli są spełnione oba warunki to punkt końca i początka odcinka się pokrywa. Czyli rysujemy punkt.
- Wiemy że tangens istnieje, czyli możemy go obliczyć. Napewno nie będziemy mieli błędu dzielenia przez zero.
- Obliczamy tangens
- Jeżeli tangens kąta jest większy od 1 i mniejszy od -1 to do rysowania odcinka dokładniejsza będzie funkcja używająca cotangensa.
- W przeciwnym wypadku używamy funkcji z tangensem.
A tak wygląda szkielet funkcji.
void inline linia(float x1, float y1, float x2, float y2,char kolor=0x0F) { float tga; if((x2-x1)==0) { if((y2-y1)==0) { punkt(x1, x2, kolor); return; } else { ctg(x1, y1, x2, y2, kolor); return; } } tga=(float)(y2-y1)/(x2-x1); if(tga>=1||tga<=-1) { ctg(x1, y1, x2, y2, kolor); return; } else { tg(x1, y1, x2, y2, kolor); return; } }
Dlaczego jest podział na funkcje używające tangensa i cotangensa. Zakładam że wiesz jak wyglądają te funkcje. Do 45 stopni czyli gdy tangens ma wartość 1 dokładniejszy w obliczeniach jest tangens jeżeli byśmy zastosowali przy moim algorytmie funkcię z tangensem powyżej 45 stopni to w odcinku były by dziury. czym większy kont tym większe dziury. A pionowego odcinka byśmy nie narysowali wogóle bo tg 90 = nieskończoność
A proste rysujemy następująco:
- Obliczamy sobie tangens z tych dwóch punktów.
- Sprawdzamy który punkt jest wyżej i od niego kolejno piksel po pikselu wyznaczamy współżędną poziomą, kożystając z wcześniej obliczonego tangensa.
Podobnie robi się dla cotangensa z tym że idzie się w poziomie obliczając współżędną pionową.
Istnieje jeszcze jeden problem do rozwiązania. Komputerm przy przekształceniach liczb dziesiętnych na całkowite obcina końcówkę a nie przybliża. No i przez tę niedogodność wychodzą odcinki troszkę przesunięte po osi x albo y. Ale oczywiście jest to do rozwiązania. Zrobimy sobie funkcję do zaokrąglania. Wykożystamy w tym celu właściwość która była nam niewygodą. przypisujemy do zmiennej całkowitej a liczbę z ułamkiem x. I w zależności czy jest to liczba ujemna czy dodatnia odpowiednio dodajemy lub odejmujemy do naszego ułamka tak że pozostaje nam liczba mniejsza od 1 i większa od -1 i teraz nic prostszego sprawdzić czy jest mniejsza czy większa od 0.5 i odpowiednio zaokrąglić.
int inline zaok(float x) { int a=x; if(x>0) { x-=a; if(x<0.5) return a; else { a+=1; return a; } } else { x+=a; if(x> -0.5) return a; else { a-=1; return a; } } } void tg(float x1, float y1, float x2, float y2, char kolor) { int register i; float tga=(y2-y1)/(x2-x1); if(x1<=x2) for(i=x1; i<=x2; i++) punkt(i, zaok(tga*(i-x1)+y1), kolor); else for(i=x2; i<=x1; i++) punkt(i, zaok(tga*(i-x1)+y1), kolor); } void ctg(float x1, float y1, float x2, float y2, char kolor) { int register i; float ctga=(x2-x1)/(y2-y1); if(y1<=y2) for(i=y1; i<=y2; i++) punkt(zaok(ctga*(i-y1)+x1), i, kolor); else for(i=y2; i<=y1; i++) punkt(zaok(ctga*(i-y1)+x1), i, kolor); }
Oczywiście definicjia funkcji linia(...) powinna być za tymi trzema funkcjami. W przeciwnym wypadku się to nie skompiluje.
No to przebrneliśmy przez linie, no to możemy iść dalej.