Wykład 15: Grafika (moduł Graph)
Transkrypt
Wykład 15: Grafika (moduł Graph)
Podstawy Programowania
Wykład piętnasty:
Grafika (moduł Graph) – część druga
1.Przykładowe programy
Znając większość użytecznych elementów modułu graph możemy napisać kilka
prostych programów pracujących w trybie graficznym i prezentujących kilka
ciekawych efektów wizualnych.
2. Pierwszy przykład – losowe okręgi
Pierwszy program rysuje na ekranie okręgi o losowym położeniu i kolorze.
Kończy swoje działanie z chwilą naciśnięcia przez użytkownika dowolnego klawisza na klawiaturze.
program grafika;
uses
graph,crt;
procedure inicjalizuj;
var
grdriver,grmode:integer;
begin
grdriver:=detect;
initgraph(grdriver,grmode,'c:\tp\bgi');
if graphresult<>grOK then halt(1);
end;
procedure DrawRandomCircle;
var
x,y:integer;
r,kolor:word;
begin
x:=random(640);
y:=random(480);
r:=random(50);
kolor:=random(15);
setcolor(kolor);
2
circle(x,y,r);
end;
begin
inicjalizuj;
randomize;
while keypressed do readkey;
repeat
DrawRandomCircle;
until keypressed;
closegraph;
end.
Program ten zawiera dwie procedury. Pierwszą z nich jest procedura inicjalizuj,
która jest uproszczoną wersją procedury omawianej na poprzednim wykładzie.
Procedura DrawRandomCircle (tak jak poprzedniczka jest bezparametrowa) losuje współrzędne środka okręgu, zapisuje je do zmiennych x i y, oraz losuje numer koloru, który przypisuje zmiennej kolor. Należy zauważyć, że zarówno numeracja kolorów, jak i współrzędnych zaczyna się od zera, a więc argumentami
wywołania funkcji random mogą być ilości pikseli w pionie i w poziomie dla
danego trybu graficznego, oraz liczba kolorów1. Losowana jest również długość
promienia okręgu (od 0 do 49 pikseli) i zapisywana w zmiennej r. Kolor jest
ustawiany za pomocą procedury setcolor , natomiast okrąg jest rysowany za pomocą procedury circle. W bloku głównym programu najpierw wywoływana jest
procedura inicjalizuj, następnie jest inicjalizowany generator liczb pseudolosowych za pomocą procedury randomize. Po wykonaniu tej czynności czyszczony jest bufor klawiatury (pętla while) i w pętli repeat wywoływana jest procedura DrawRandomCircle. Warunkiem zakończenia tej pętli jest naciśnięcie
przez użytkownika dowolnego klawisza na klawiaturze. Po zakończeniu pętli
wywoływana jest procedura closegraph zamykająca tryb graficzny.
3. Przykład drugi – fraktal
Fraktale są obiektami geometrycznymi, które cechuje powtarzalność struktury2. Istnieje wiele metod tworzenia takich obiektów. Zaliczają się do nich tzw.
1 Procedura initgraph w wyniku autodetekcji użyje sterownika karty VGA oraz trybu VGAHi.
2 Matematyczne wprowadzenie do fraktali można znaleźć w książce Jacka Kudrewicza „Fraktale i
chaos”.
3
L-Systemy, układy odwzorowań afinicznych (IFS) oraz równania rekurencyjne
i zespolone3. Fraktale są używane w grafice komputerowej do tworzenia
obiektów przypominających obiekty naturalne, takie jak: chmury, pagórki
porośnięte trawą, itd. Poniższy program prezentuje użycie układu odwzorowań
do wygenerowania ciekawego fraktala:
program fraktal;
uses
crt,graph;
var
x,y:real;
procedure inicjalizuj;
var
grdriver,grmode:integer;
begin
grdriver:=detect;
initgraph(grdriver,grmode,'c:\tp\bgi');
end;
procedure draw(x,y:real);
var
choice:integer;
i:longint;
begin
for i:=0 to MaxLongInt do begin
choice:=random(4);
case choice of
0: begin
x:=-0.678*x-0.02*y;
y:=-0.18*x+0.81*y+10;
end;
1: begin
3 Niektóre z tych metod są opisane na tej stronie:
http://www.student.kuleuven.ac.be/~m0216922/CG/
4
x:=0.4*x+0.4*y;
y:=-0.1*x+0.4*y;
end;
2: begin
x:=-0.4*x-0.4*y;
y:=-0.1*x+0.4*y;
end;
3: begin
x:=-0.1*x;
y:=0.44*x+0.44*y-2;
end;
end;
if keypressed then
if readkey = #27 then exit;
putpixel(319-trunc(x),239-trunc(y),green);
end;
end;
begin
randomize;
inicjalizuj;
x:=0;
y:=0;
draw(x,y);
closegraph;
end.
Fraktal ten rysowany jest „punkt po punkcie”. Współrzędne każdego następnego punktu są wyliczane na podstawie współrzędnych poprzedniego punktu,
za pomocą następującego układu równań:
x' = a*x + b*y +c
y' = d*x+e*y+f
gdzie x' i y' są współrzędnymi nowego punktu, x, y współrzędnymi poprzedniego punktu. Istnieją cztery zestawy współczynników a,b,c,d,e i f. Każdy z nich
jest wybierany na drodze losowej przed wyliczeniem współrzędnych następnego
punktu. Oto zestawy tych współczynników: a = -0,67, b = -0,02, c = 0, d = -0,18,
5
e = 0,81, f =10; a = 0,4, b = 0,4, c = 0, d = -1, e = 0,4, f = 0; a = -0,4, b = -0,4, c =
0, d = -0,1, e = 0,4, f = 0; a = -0,1, b = 0, c = 0, d = 0,44, e = 0,44, f = -2. Im
więcej punktów wyliczymy i narysujemy, tym efekt wizualny będzie lepszy. Kod
programu rysującego omawiany fraktal składa się z uproszczonej procedury
inicjalizuj oraz z procedury draw posiadającej dwa parametry formalne, będące
współrzędnymi pierwszego punktu fraktala (aczkolwiek nie jest on rysowany).
Kolejne punkty są wyliczane wewnątrz pętli for. Najpierw wyliczany jest układ
równań, który zostanie użyty do obliczenia współrzędnych nowego punku,
następnie wyliczane są te współrzędne. Do narysowania odpowiadającego im
piksela używana jest procedura putpixel. Niestety, nie istnieje jednoznaczne
odwzorowanie punktów na piksle, gdyż punkty mają współrzędne rzeczywiste,
piksle całkowite. Musimy więc użyć funkcji trunc do przeliczenia współrzędnych
punktu na współrzędne piksela. Tak wyliczone współrzędne są odejmowane od
319 (rzędna) i 239 (odcięta), co sprawia, że obraz jest tworzony na środku
ekrany i nie jest odwrócony. Wszystkie piksle otrzymane w ten sposób są
zaznaczane na ekranie na zielono, bowiem rysowany fraktal nazywany jest
„choinką” i przypomina rzeczywistą choinkę. Pętla for jest wykonywana ponad
dwa miliardy razy. Można przerwać jej wykonanie naciskając klawisz Escape.
Badanie, czy użytkownik nacisnął taki klawisz wykonywane jest w instrukcjach
if umieszczonych we wnętrzu pętli. Przed wykonaniem tych instrukcji nie jest
wykonywane czyszczenie bufora klawiatury. Należy zauważyć, że badanie stanu
klawiatury spowalnia wykonanie pętli, dlatego warto kilka razy w ciągu
wykonania programu nacisnąć np.: klawisz spacji.
4.Przykład trzeci – funkcja sinus
Kolejny program rysuje na ekranie jeden okres funkcji sinus.
program sinus;
uses
graph;
procedure inicjalizuj;
var
grdriver,grmode,kod:integer;
begin
grdriver:=detect;
initgraph(grdriver,grmode,'c:\tp\bgi');
6
kod:=graphresult;
if kod<>grOK then
begin
grapherrormsg(kod);
readln;
halt(1);
end;
end;
procedure drawsinus;
const
deg2rad = 2*pi/360;
var
x:integer;
y:real;
begin
moveto(0,240);
setcolor(green);
for x:=0 to 360 do
begin
y:=50*sin(x*deg2rad);
lineto(x,240-trunc(y));
end;
end;
begin
inicjalizuj;
drawsinus;
readln;
closegraph;
end.
W skład kodu źródłowego wchodzi procedura inicjalizuj, tym razem w takiej
postaci, jaka została przedstawiona na poprzednim wykładzie i procedura
drawsinus. Rysowanie wykresu funkcji odbywa się za pomocą tej ostatniej
procedury. Stała deg2rad zdefiniowana wewnątrz procedury jest używana do
7
przeliczenia stopni na radiany. Rzędne punktów są przechowywane w zmiennej
x typu integer, a odcięte w zmiennej y typu real. Pierwszą czynnością
wykonywaną w procedurze jest umieszczenie kursora w punkcie
o współrzędnych (0,240), tak aby pierwszy punkt wykresu leżał pośrodku
lewego brzegu ekranu. Następnie ustalany jest za pomocą procedury setcolor
kolor wykresu (zielony). Jako licznik pętli for użyta jest zmienna x, której
wartości zmieniają się od 0 do 360 (stopnie). Wewnątrz tej pętli wyliczana jest
wartość y dla każdego x. Do wyliczenia tej wartości użyta jest funkcja sin. Jej
parametrem wywołania jest wyrażenie x*deg2rad, gdyż wartość tego parametru
jest traktowana przez funkcję sin jako wartość kąta wyrażona w radianach. Aby
wykres był widoczny na ekranie wartość zwrócona przez sin jest mnożona przez
50. Wykres jest rysowany przy pomocy procedury lineto, co sprawia, że jest on
ciągły, a nie składa się z pojedynczych punktów.
5.Przykład czwarty – przybliżona wartość liczby pi
Istnieje wiele sposobów wyliczenia wartości liczby PI. Jednym z nich jest
zastosowanie metody należącej do zbioru algorytmów określanych wspólnym
mianem metod Monte Carlo. Odkrycie tych metod zawdzięczamy polskiemu
matematykowi, pracującemu w Stanach Zjednoczonych, Stanisławowi
Ulamowi. Działanie tych metod opiera się na liczbach losowych i są one
używane do otrzymywania przybliżonych wartości rozwiązań równań, których
nie można rozwiązać w sposób analityczny, lub rozwiązanie to byłoby zbyt
czasochłonne. Do takich metod4 zaliczana jest opisana niżej metoda na
obliczenie przybliżonej wartości liczby PI. Załóżmy, że mamy koło wpisane
w kwadrat. Bok kwadratu jest oczywiście dwa razy dłuższy niż promień koła.
. Losujemy punkty
Stosunek pola koła do pola kwadratu wynosi
wewnątrz kwadratu i sprawdzamy, czy leżą one również wewnątrz koła. Niech
M będzie liczbą punktów, które leżą wewnątrz koła, a N liczbą wszystkich
wylosowanych punktów. Przybliżona wartość liczby pi będzie wynosiła
. Oto program, który ilustruje użycie tej metody:
program pi_monte_carlo;
uses
graph,crt;
4 Opis tej i innych metod znajduje się na stronie prowadzonej przez Eve Astrid Anderson:
http://www.eveandersson.com/pi/
8
procedure inicjalizuj;
var
sterownik,tryb,kod:integer;
begin
sterownik:=detect;
initgraph(sterownik,tryb,'c:\tp\bgi');
kod:=GraphResult;
if kod <> grOK then
begin
grapherrormsg(kod);
halt(1);
end;
setwritemode(xorput);
end;
procedure lpi;
type
naturalne = 0..maxlongint;
const
r=200;
xs=250;
ys=250;
var
n,m:naturalne;
x,y:integer;
s:string;
xr,yr,pi:real;
begin
m:=0;
outtextxy(2,2,'Przybliżona wartość pi: ');
outtextxy(2,14,'Ilość iteracji: ');
for n:=1 to maxlongint do
begin
xr:=-r+random(2*r)+randomize;
yr:=-r+random(2*r)+randomize;
9
x:=trunc(xr);
y:=trunc(yr);
if sqrt(sqr(xr)+sqr(yr))<=r then
begin
putpixel(x+xs,y+ys,red);
inc(m);
end
else putpixel(x+xs,y+ys,green);
if keypressed then
if readkey=#27 then break;
pi:=(4*m)/n;
str(pi:0:20,s);
setviewport(200,0,300,11,true);
clearviewport;
outtextxy(0,0,s);
str(n,s);
delay(5);
setviewport(200,14,300,24,true);
clearviewport;
outtextxy(0,0,s);
setviewport(0,0,getmaxx,getmaxy,true);
end;
while keypressed do readkey;
readkey;
end;
begin
randomize;
inicjalizuj;
lpi;
closegraph;
end.
Najważniejsze operacje wykonywane w programie zawarte są w procedurze lpi.
Zdefiniowany jest tam typ naturalne, który obejmuje swym zakresem liczby
10
naturalne od 0 do maxlongint, oraz trzy stałe: r = 200 oznaczająca długość
promienia koła, oraz xs=250 i ys=250 oznaczające współrzędne środka
wyświetlanego obrazu. Zmienne x i y są współrzędnymi zaznaczanych pikseli,
zmienna s typu string będzie wykorzystywana podczas konwersji liczb na
łańcuchy znaków. Zmienne xr i yr będą przechowywać współrzędne
wylosowanych punktów. Zmienna pi będzie przechowywała wyliczoną,
przybliżoną wartość liczby pi. Wykonanie procedury rozpoczyna się inicjalizacji
zmiennej m, której przypisywana jest wartość 0 i od wypisania dwóch
komunikatów tekstowych, następnie w pętli for, której zmienną sterującą jest
n losujemy współrzędne punktu leżącego wewnątrz kwadratu, zakładając że
środek kwadratu będzie miał współrzędne (0,0). Kolejną czynnością
wykonywaną w tej pętli, jest konwersja współrzędnych rzeczywistych punktu,
na współrzędne całkowite piksela. W instrukcji warunkowej if sprawdzamy, czy
wylosowany punkt należy do wnętrza koła. Korzystamy z faktu, że wszystkie
punkty należące do wnętrza koła leżą, względem jego środka, w odległości
mniejszej lub równej długości promienia tego koła i że środek koła znajduje się
w punkcie o współrzędnych (0,0). Odległość punktu od środka koła obliczamy
ze wzoru Euklidesa . Jeśli punkt należy do wnętrza koła, to punkt
mu odpowiadający jest rysowany na czerwono i zwiększana jest wartość m, jeśli
nie piksel jest zaznaczany na zielono5. W pętli znajdują się dwie instrukcje if
pozwalające przerwać ich wykonanie po naciśnięciu klawisza Escape.
Wyliczana jest również bieżąca, przybliżona wartość pi, według podanego wyżej
wzoru. Ta wartość konwertowana jest na łańcuch znaków, tak aby wyświetlane
było 20 miejsc po przecinku. Po konwersji otwierane jest okno za miejscem na
ekranie, gdzie znajduje się pierwszy komunikat, czyszczona jest jego zawartość
i wypisywana jest w nim bieżąca wartość pi. Po 5 milisekundowej przerwie
wykonywany jest podobny ciąg operacji, ale tym razem wyświetlana jest ilość
wyświetlonych punktów, czyli ilość powtórzeń pętli for. Ostatnią operacją
wykonywaną w tej pętli jest ustawienie okna, tak aby obejmowało cały ekran.
6.Przykład piąty – prosta animacja
Do tworzenia animacji możemy użyć mechanizmu stron. Ponieważ w przypadku
kart VGA więcej niż jedna strona jest dostępna w niższych rozdzielczościach,
będziemy musieli zmienić kod procedury inicjalizuj. Poniżej znajduje się kod
źródłowy programu, który wyświetla przesuwający się po ekranie, ze strony
lewej na prawą, czerwony prostokąt. Oto kod źródłowy tego programu:
5 Do współrzędnych pikseli dodawane są współrzędne środka wyświetlanego obrazu, tak aby było
on widoczny w całości na ekranie, a nie jego część. Warto zauważyć, że ten obraz jest
odwrócony, ale ponieważ jest symetryczny, to nie ma to większego znaczenia.
11
program animate;
uses graph,crt;
procedure inicjuj;
var
sterownik,tryb:integer;
begin
sterownik:=VGA;
tryb:=VGALo;
initgraph(sterownik,tryb,'c:\tp\bgi');
if GraphResult <> grOK then halt(1);
end;
procedure draw;
var
i:word; x:byte;
begin
i:=0;
x:=0;
setcolor(red);
repeat
x:=(x+1) mod 4;
setactivepage(x);
delay(5);
cleardevice;
rectangle(10+i mod getmaxx,100,20+i mod getmaxx,110);
inc(i);
setvisualpage(x);
until keypressed;
end;
begin
inicjuj;
draw;
closegraph;
12
end.
W wersji procedury inicjalizuj użytej w tym programie, procedura initgraph nie
wykonuje detekcji numeru sterownika i trybu graficznego. Sami przypisujemy
zmiennym sterownik i tryb odpowiednie wartości. Użyjemy sterownika karty
VGA i trybu o najmniejszej rozdzielczości, jaki ten sterownik oferuje. Zaletą
tego trybu jest to, że można w nim korzystać z czterech stron. Rysowanie
kolejnych klatek animacji odbywa się w bezparametrowej procedurze draw.
Zmienna x przechowuje numer strony, wartość zmiennej i jest dodawana do
składowych poziomych współrzędnych lewego górnego i prawego dolnego rogu
rysowanego prostokąta. Na początku swego działania procedura draw
inicjalizuje zmienne x i i nadając im wartość początkową 0, a następnie ustala
kolor rysowanych elementów na czerwony za pomocą wywołania procedury
setcolor. W pętli repeat obliczany jest numer nowej strony, poprzez dodanie do
wartości zmiennej x jedynki i obliczenie reszty z dzielenia tej sumy przez cztery.
Ponieważ strony są numerowane od 0, operacja modulo zapewnia, że zawsze
uzyskamy prawidłowy numer strony, tzn. liczbę ze zbioru {0,1,2,3}. Po
opóźnieniu wykonania programu na pięć milisekund czyszczona jest zawartość
aktywnej strony i rysowany jest na niej prostokąt, za pomocą wywołania
procedury rectangle. Do wartości poziomych współrzędnych odpowiednich
wierzchołków tego prostokąta jest dodawana wartość zmiennej i i z tak
otrzymanej sumy obliczana jest reszta zdzielenia przez wartość zwróconą
przez funkcję getmaxx. Dzięki temu prostokąt na kolejnych klatkach jest
przesunięty w prawo, a kiedy osiągnie prawy brzeg ekranu jest na nowo
rysowany od lewej strony. Ostatnimi operacjami wykonywanymi w pętli są:
zwiększenie wartości zmiennej i oraz ustawienie strony o bieżącym numerze jak
strony wyświetlanej. Pętla repeat kończy się z chwilą naciśnięcia przez
użytkownika dowolnego klawisza. Niestety użycie funkcji keypressed powoduje,
w pewnym momencie spowolnienie animacji.
7.Przykład szósty – bardziej złożona animacja
Program, który zostanie zaprezentowany w tym rozdziale tworzy trochę bardziej
skomplikowana animację. Rysuje on czerwoną piłkę, która odbija się od
brzegów ekranu zgodnie z regułą kąta padania i odbicia, która jest znana
z fizyki. Oto kod źródłowy tego programu:
program animate;
uses graph,crt;
13
type
ball = record
x,y:integer;
r:byte;
dx,dy:shortint;
end;
var
ba:ball;
procedure initialize;
var
grdriver,grmode,code:integer;
begin
grdriver:=VGA;
grmode:=VGALo;
initgraph(grdriver,grmode,'c:\tp\bgi');
code:=graphresult;
if code <> grOK then
begin
grapherrormsg(code);
readln;
halt(1);
end;
end;
procedure setball(var b:ball);
begin
randomize;
b.x:=random(getmaxx div 2)+16;
b.y:=random(getmaxy div 2)+16;
b.r:=10;
b.dx:=2;
b.dy:=-2;
end;
14
procedure draw(var b:ball);
var
x:byte;
a:char;
begin
setcolor(red);
x:=0;
repeat
if keypressed then a:=readkey;
x:=(x+1) mod 4;
setactivepage(x);
delay(5);
cleardevice;
circle(b.x,b.y,b.r);
b.x:=b.x+b.dx;
b.y:=b.y+b.dy;
if (b.x-b.r<=0) or (b.x+b.r>=getmaxx) then b.dx:=-b.dx;
if (b.y-b.r<=0) or (b.y+b.r>=getmaxy) then b.dy:=-b.dy;
setvisualpage(x);
until a=#27;
end;
begin
initialize;
setball(ba);
draw(ba);
closegraph;
end.
Piłka, o której była mowa we wstępie jest oczywiście okręgiem. Aby program
mógł wykonać przedstawione zadanie musi znać wszystkie niezbędne
informacje dotyczące stanu piłki. W programie został zdefiniowany typ
rekordowy o nazwie ball. Pola tego typu rekordowego będą przechowywały
wspomniane informacje. Pola x i y przechowują informacje o bieżącym
położeniu środka piłki, pole r przechowuje długość promienia piłki, natomiast
pola dx i dy przechowują, odpowiednio składową poziomą i pionową wektora
prędkości piłki. Procedura initialize włącza ten sam tryb graficzny, co jej
15
odpowiedniczka z poprzedniego przykładu. Procedura setball, jako argument
swojego wywołania przyjmuje zmienną typu ball. Współrzędne środka piłki są
losowane, ale w ten sposób, aby piłka całym swoim obwodem znajdowała się na
ekranie. Długość promienia piłki jest ustalana na 10, natomiast składowa
pozioma wektora prędkości na 2, a pionowa na -2. Ponieważ wartości
bezwzględne obu składowych są równe, to piłka będzie zderzała się z krawędzią
ekranu pod kątem 45 stopni i pod takim samym kątem zostanie odbita6.
Składowa pionowa jest również ujemna, a pozioma dodatnia, więc do
pierwszego odbicia piłka będzie się poruszała się w górę i w prawo. Animacja
piłki odbywa się w procedurze ball. Pierwszą czynnością wykonywaną w niej
jest ustawienie koloru rysowanego okręgu na czerwony, następnie wykonywana
jest pętla repeat, która kończy się po naciśnięciu przez użytkownika klawisza
Escape. Badanie, czy użytkownik nacisnął klawisz wykonywane jest wewnątrz
pętli, w pierwszej instrukcji warunkowej. Jeśli klawisz został naciśnięty, to kod
ASCII tego klawisza jest zapamiętywany w zmiennej a. Wewnątrz pętli
wykorzystywany jest również mechanizm stron do rysowania kolejnych klatek
animacji. Część kodu tu użytego była już opisana w poprzednim przykładzie,
więc teraz jej opis zostanie pominięty. Okrąg rysowany jest za pomocą
procedury circle. Współrzędne i promień okręgu odczytywane są
z odpowiednich pól rekordu. Po narysowaniu okręgu do odpowiednich
współrzędnych jego środka dodawane są składowe wektora prędkości,
a następnie wykonywana jest detekcja kolizji piłki z brzegiem ekranu. Badanie
to jest wykonywane w dwóch instrukcjach warunkowych. W pierwszej z nich
sprawdzamy, czy piłka nie przesunęłaby się za lewy lub za prawy brzeg ekranu.
W pierwszym przypadku współrzędna pozioma środka okręgu, pomniejszona
o długość promienia byłaby mniejsza lub równa zero, w drugim przypadku ta
sama składowa powiększona o długość promienia byłaby większa lub równa
współrzędnej poziomej leżącego najbardziej na lewo piksela. Jeśli któryś z tych
warunków zachodzi, to zmieniamy znak poziomej składowej wektora prędkości
piłki. Podobne badanie wykonujemy dla współrzędnej pionowej środka okręgu.
8. Przykład ósmy – rysowanie za pomocą operatorów.
Dosyć często w grafice komputerowej zachodzi konieczność wypełnienia
pewnego obszaru obrazu wzorcem (teksturą). Na poprzednim wykładzie
poznaliśmy podprogramy z modułu graph, które pozwalają narysować niektóre
prymitywy z określonym wypełnieniem. Jeśli chcemy wypełnić tło lub jego
fragment ciekawym wzorcem, to zamiast stosować te podprogramy możemy
użyć prostszej techniki polegającej na wyliczeniu koloru danego piksela na
podstawie współrzędnych jego położenia, przy użyciu takich operatorów, jak or,
6 Można oczywiście nadać piłce inną prędkość.
16
xor, not, and, +, -, *. Taka możliwość została zademonstrowana w poniższym
programie:
program efekty;
uses
crt,graph;
procedure inicjalizuj;
var
grdriver,grmode,kod:integer;
begin
grdriver:=detect;
initgraph(grdriver,grmode,'c:\tp\bgi');
kod:=graphresult;
if kod<>grOK then
begin
grapherrormsg(kod);
readln;
halt(1);
end;
settextstyle(DefaultFont, HorizDir, 10);
end;
procedure xordraw;
var
x,y:integer;
begin
for x:=0 to getmaxx do
for y:=0 to getmaxy do
putpixel(x,y,x xor y);
outtextxy(getmaxx div 2 - textwidth('xor') div 2, getmaxy div 2 - textheight('xor') div 2,'xor');
end;
17
procedure anddraw;
var
x,y:integer;
begin
for x:=0 to getmaxx do
for y:=0 to getmaxy do
putpixel(x,y,x and y);
outtextxy(getmaxx div 2 - textwidth('and') div 2, getmaxy div 2 - textheight('and') div 2,'and');
end;
procedure ordraw;
var
x,y:integer;
begin
for x:=0 to getmaxx do
for y:=0 to getmaxy do
putpixel(x,y,x or y);
outtextxy(getmaxx div 2 - textwidth('or') div 2, getmaxy div 2 - textheight('or') div 2,'or');
end;
procedure muldraw;
var
x,y:integer;
begin
for x:=0 to getmaxx do
for y:=0 to getmaxy do
putpixel(x,y,x * y);
outtextxy(getmaxx div 2 - textwidth('*') div 2, getmaxy div 2 - textheight('*') div 2,'*');
end;
18
procedure sierpinski;
var
x,y:integer;
begin
for x:=0 to getmaxy+32 do
for y:=0 to getmaxy+32 do
if x and y = 0 then putpixel(x,y,white);
end;
function getkey:char;
begin
while keypressed do readkey;
getkey:=readkey;
end;
begin
inicjalizuj;
xordraw;
getkey;
cleardevice;
anddraw;
getkey;
cleardevice;
ordraw;
getkey;
cleardevice;
muldraw;
getkey;
cleardevice;
sierpinski;
getkey;
closegraph;
end.
19
W procedurze initcjalizuj oprócz ustawienia trybu graficznego ustawiany jest
również rozmiar wyświetlanych znaków przy pomocy procedury SetTextStyle.
Cztery kolejne procedury wypełniają ekran monitora pikselami, których kolor
wyliczany jest odpowiednio jako: różnica symetryczna współrzędnej poziomej
i pionowej piksela (procedura xordraw), iloczyn logiczny tych współrzędnych
(procedura anddraw), suma logiczna (procedura ordraw), oraz zwykły iloczyn
(procedura muldraw). Ostatnia procedura tworzy tzw. efekt mojry. Niestety, ze
względu na małą liczbę wyświetlanych równocześnie kolorów w trybach
oferowanych przez Turbo Pascal, te efekty są mało widoczne. Każda z procedur
wypisuje na ekranie operator, który został użyty do uzyskania wyświetlonego
obrazu. Dokonywane jest to za pomocą procedury outtextxy. Współrzędne
wyświetlanego napisu są wyliczane tak, aby znajdował się on na środku
ekranu, tzn. od współrzędnych środka ekranu (getmaxx div 2 i getmaxy div 2)
odejmowana jest odpowiednio połowa szerokości wypisywanego tekstu (np.:
textwidth('xor') div 2) i połowa wysokości tego tekstu (np.: texthight('xor') div 2).
Ostatnia ze zdefiniowanych procedur (sierpinski) rysuje fraktal nazywany od
nazwiska jego odkrywcy trójkątem Sierpińskiego. Algorytm generowania takiego
obrazu okazuje się być bardzo prosty: jeśli iloczyn logiczny współrzędnych
piksela jest równy zero, to taki piksel rysujemy na biało, w przeciwnym
przypadku pozostawiamy go bez zmian. Zakresy liczników pętli for
w tej procedurze zostały tak dobrane, aby na ekranie ukazał się tylko jeden
trójkąt Sierpińskiego. W programie jest zdefiniowana również funkcja getkey.
Zwraca ona wartość typu char, ale podczas jej wywołania w bloku głównym
programu ta wartość jest ignorowana. Funkcja ta służy do zatrzymania pracy
programu, do czasu aż użytkownik naciśnie dowolny klawisz, który może być
również klawiszem funkcyjnym lub klawiszem kursora.
20