Tomasz Hebisz
Grafika komputerowa
Instytut Sterowania i Systemów Informatycznych
1. Transformacje obiektów
Mówiąc o grafice komputerowej należy dużą uwagę zwrócić na przekształcenia figur
geometrycznych na płaszczyznie i w przestrzeni. Ważnym zagadnieniem jest sposób
realizacji transformacji obiektów graficznych. Najczęściej obiekty takie są opisywane
zbiorem wyróżnionych punktów, dlatego też omawiane transformacje będą dotyczyły
pojedynczych punktów, a nie np. równań algebraicznych opisujących krzywe.
1.1. Przekształcenia punktów na płaszczyznie
(przekształcenia 2D)
1.1.1. Przesunięcie (translacja)
Dowolny punkt na płaszczyznie, opisany za pomocą pary (x, y), można przesunąć
na nową pozycję dodając do współrzędnych punktu wielkość przesunięcia. Obrazem
punktu P = (x, y) po przesunięciu o wektor t = [tx, ty] jest punkt P = (x , y ) taki,
że
x = x + tx, y = y + ty.
Jeżeli zdefiniuje się wektory kolumnowe:
x x tx
P = , P = , T = ,
y y ty
to równanie translacji można zapisać również macierzowo:
x x tx
= + albo P = P + T
y y ty
1.1.2. Skalowanie
Figury, opisane za pomocą punktów na płaszczyznie, mogą być skalowane poprzez
mnożenie odciętych przez współczynnik sx (wzdłuż osi x) oraz mnożenie rzędnych przez
współczynnik xy (wzdłuż osi y):
x = sxx, y = syy,
x sx 0 x
= · albo P = S · P.
y 0 sy y
1. Transformacje obiektów 2
Skalowanie odbywa się względem początku układu współrzędnych. Gdy współczynniki
skalowania są mniejsze od 1 (sx < 1 i sy < 1), to wówczas obiekt zmniejsza się i przybliża
się do środka układu współrzędnych. W przypadku, gdy współczynniki skalowania są
większe od 1 (sx > 1 i sy > 1), to wówczas obiekt zwiększa się i oddala od środka układu
współrzędnych.
Jeżeli współczynniki skalowania posiadają równą wartość (sx = sy), wówczas proporcje
obiektu nie ulegają zmianie i mówi się o skalowaniu jednorodnym. Jeżeli natomiast
współczynniki skalowania są różne (sx = sy), to proporcje skalowanego obiektu ulegną
zmianie. W takim przypadku ma siÄ™ do czynienia z tzw. skalowaniem niejednorodnym.
1.1.3. Obroty
Punkty mogÄ… być obracane o kÄ…t Õ wokół poczÄ…tku ukÅ‚adu współrzÄ™dnych. Najlepiej
jest wtedy przedstawić obiekt w układzie współrzędnych biegunowych. Wtedy
współrzÄ™dnymi punktu P bÄ™dÄ… odlegÅ‚ość r od poczÄ…tku ukÅ‚adu i kÄ…t È miÄ™dzy osiÄ… x,
a prostą OP . Współrzędne kartezjańskie punktu P są określone zależnościami
trygonometrycznymi
x = r · cos (È), y = r · sin (È).
Obrazem punktu P po obrocie o kÄ…t Õ wokół poczÄ…tku ukÅ‚adu jest punkt P
o współrzÄ™dnych biegunowych (r, È + Õ). StÄ…d
x = r · cos (È + Õ) = r · cos (È) · cos (Õ) - r · sin (È) · sin (Õ),
y = r · sin (È + Õ) = r · cos (È) · sin (Õ) - r · sin (È) · cos (Õ),
zatem ostatecznie otrzymuje siÄ™
x = x · cos (Õ) - y · sin (Õ),
y = x · sin (Õ) + y · cos (Õ).
W postaci macierzowej przekształcenie takie można zapisać następująco
x cos (Õ) - sin (Õ) x
= · albo P = R · P.
y sin (Õ) cos (Õ) y
1.2. Aączenie przekształceń
Jeżeli zachodzi potrzeba dokonania transformacji obiektu względem punktu P0 =
(x0, y0), który nie jest środkiem układu współrzędnych, to takie przekształcenie można
złożyć z poprzednich. W tym celu trzeba najpierw przesunąć punkt P = (x, y)
o wektor [-x0, -y0], następnie obrócić punkt P = (x - x0, y - y0), a następnie wykonać
przesunięcie o wektor [x0, y0].
Na podstawie powyższego przykładu można zauważyć, że byłoby wygodnie traktować
wszystkie przekształcenia w sposób jednolity, co pozwalałoby na łatwe łączenie różnych
przekształceń.
1. Transformacje obiektów 3
Reprezentacje macierzowe przekształceń przesunięcia, skalowania i obrotu mają
następującą postać
P = T + P,
P = R · P,
P = S · P.
Niestety przesunięcie (translacja) jest traktowane inaczej niż skalowanie lub obrót i nie
da się tego przekształcenia przedstawić w formie mnożenia wektora przez macierz, jeżeli
punkty traktuje się, w sposób naturalny, jako składowe wektora dwuwymiarowego. Można
jednak przejść do współrzędnych jednorodnych, uznając punkty z R" jako punkty z R ,
leżące na płaszczyznie z = 1, a więc o trzech współrzędnych (x, y, 1).
1.2.1. Translacja
îÅ‚ Å‚Å‚ îÅ‚ Å‚Å‚ îÅ‚ Å‚Å‚
x 1 0 tx x
ïÅ‚ śł ïÅ‚ ïÅ‚ śł
y = 0 1 ty śł · y , albo
ðÅ‚ ûÅ‚ ðÅ‚ ûÅ‚ ðÅ‚ ûÅ‚
1 0 0 1 1
îÅ‚ Å‚Å‚
1 0 tx
ïÅ‚
P = T(tx, ty) · P gdzie T(tx, tx) = 0 1 ty śł .
ðÅ‚ ûÅ‚
0 0 1
Zatem złożenie dwóch operacji translacji można przedstawić następująco
P = T(tx1, ty1) · P,
P = T(tx2, ty2) · P ,
P = T(tx2, ty2) · (T(tx1, ty1) · P) = (T(tx2, ty2) · T(tx1, ty1)) · P,
czyli, w postaci macierzowej wynikową wartość translacji można zapisać następująco
îÅ‚ Å‚Å‚ îÅ‚ Å‚Å‚ îÅ‚ Å‚Å‚
1 0 tx1 1 0 tx2 1 0 tx1 + tx2
ïÅ‚ ïÅ‚ ïÅ‚
ðÅ‚ 0 1 ty1 śł · 0 1 ty2 śł = 0 1 ty1 + ty2 śł .
ûÅ‚ ðÅ‚ ûÅ‚ ðÅ‚ ûÅ‚
0 0 1 0 0 1 0 0 1
1.2.2. Skalowanie
îÅ‚ Å‚Å‚ îÅ‚ Å‚Å‚ îÅ‚ Å‚Å‚
x sx 0 0 x
ïÅ‚ śł ïÅ‚ śł ïÅ‚ śł
y = 0 sy 0 · y , albo
ðÅ‚ ûÅ‚ ðÅ‚ ûÅ‚ ðÅ‚ ûÅ‚
1 0 0 1 1
îÅ‚ Å‚Å‚
sx 0 0
ïÅ‚ śł
P = S(sx, sy) · P gdzie S(sx, sx) = 0 sy 0 .
ðÅ‚ ûÅ‚
0 0 1
Zatem złożenie dwóch operacji skalowania można przedstawić następująco
P = S(sx1, ts1) · P,
1. Transformacje obiektów 4
P = S(sx2, sy2) · P ,
P = S(sx2, sy2) · (S(sx1, sy1) · P) = (S(sx2, sy2) · S(sx1, sy1)) · P,
czyli, w postaci macierzowej wynikową wartość skalowania można zapisać następująco
îÅ‚ Å‚Å‚ îÅ‚ Å‚Å‚ îÅ‚ Å‚Å‚
sx1 0 0 sx2 0 0 sx1sx2 0 0
ïÅ‚ śł ïÅ‚ śł ïÅ‚ śł
ðÅ‚ 0 sy1 0 · 0 sy2 0 = 0 sy1sy2 0 .
ûÅ‚ ðÅ‚ ûÅ‚ ðÅ‚ ûÅ‚
0 0 1 0 0 1 0 0 1
1.2.3. Rotacja
îÅ‚ Å‚Å‚ îÅ‚ Å‚Å‚ îÅ‚ Å‚Å‚
x cos (Õ) - sin (Õ) 0 x
ïÅ‚ śł ïÅ‚ śł ïÅ‚ śł
y = sin (Õ) cos (Õ) 0 · y , albo
ðÅ‚ ûÅ‚ ðÅ‚ ûÅ‚ ðÅ‚ ûÅ‚
1 0 0 1 1
îÅ‚ Å‚Å‚
cos (Õ) - sin (Õ) 0
ïÅ‚ śł
P = R(Õ) · P gdzie R(Õ) = sin (Õ) cos (Õ) 0 .
ðÅ‚ ûÅ‚
0 0 1
Zatem w przypadku złożenia dwóch operacji rotacji, wynikową wartość rotacji można
zapisać następująco
îÅ‚ Å‚Å‚ îÅ‚ Å‚Å‚ îÅ‚ Å‚Å‚
cos (Õ) - sin (Õ) 0 cos (Ä…) - sin (Ä…) 0 cos (Õ + Ä…) - sin (Õ + Ä…) 0
ïÅ‚ śł śł ïÅ‚ śł
ðÅ‚ sin (Õ) cos (Õ) 0 ·ïÅ‚ sin (Ä…) cos (Ä…) 0 = sin (Õ + Ä…) cos (Õ + Ä…) 0 .
ûÅ‚ ðÅ‚ ûÅ‚ ðÅ‚ ûÅ‚
0 0 1 0 0 1 0 0 1
1.2.4. Przekształcenia pochylające
Pochylenie wzdłuż osi x
îÅ‚ Å‚Å‚
1 a 0
ïÅ‚ śł
SHx = 0 1 0 .
ðÅ‚ ûÅ‚
0 0 1
Pochylenie wzdłuż osi x
îÅ‚ Å‚Å‚
1 0 0
ïÅ‚ śł
SHy = b 1 0 .
ðÅ‚ ûÅ‚
0 0 1
1.2.5. Aączenie różnych przekształceń
Podstawowym celem składania kilku przekształceń w jedno, jest fakt, że można
wówczas zastosować jedną macierz złożoną.
Przykładowo, aby obrócić obiekt wokół pewnego punktu P1 można zadanie podzielić
na trzy Å‚atwe podzadania:
1. Przesunięcie tak, aby punkt P1 znalazł się w srodku układu współrzędnych.
1. Transformacje obiektów 5
2. Obrót.
3. Przesunięcie tak, aby punkt znajdujący się w środku układu współrzędnych
znalazł się w punkcie P1.
W postaci zależności matematycznych można takie złożenie zapisać następująco
îÅ‚ Å‚Å‚ îÅ‚ Å‚Å‚ îÅ‚ Å‚Å‚
1 0 x1 cos (Õ) - sin (Õ) 0 1 0 -x1
ïÅ‚ śł
T(x1, y1)·R(Õ)·T(-x1, -y1) = 0 1 y1 śł·ïÅ‚ sin (Õ) cos (Õ) 0 ·ïÅ‚ 0 1 -y1 śł =
ðÅ‚ ûÅ‚ ðÅ‚ ûÅ‚ ðÅ‚ ûÅ‚
0 0 1 0 0 1 0 0 1
îÅ‚ Å‚Å‚
cos (Õ) - sin (Õ) x1 1 - cos (Õ) + y1 sin (Õ)
ïÅ‚ śł
ïÅ‚ śł
ïÅ‚ śł
=
ïÅ‚ śł
sin (Õ) cos (Õ) y1 1 - cos (Õ) + x1 sin (Õ)
ðÅ‚ ûÅ‚
0 0 1
1.3. Współrzędne obiektów na ekranie
W większości zastosowań, obiekty umieszczane są w rzeczywistym układzie
kartezjańskim. Przy wizualizacji ich na urządzeniu graficznym staje się konieczne przejście
od układu rzeczywistego do układu współrzędnych danego urządzenia wizualizującego.
Odwzorowanie rzeczywistych współrzędnych na współrzędne układu urządzenia
zaczyna się od określenia tzw. okna współrzędnych świata, czyli obszaru prostokątnego
we współrzędnych świata rzeczywistego oraz odpowiadającego mu obszaru prostokątnego
we współrzędnych ekranu, zwanego polem wizualizacji.
Odwzorowywanie można określić jako złożenie czterech przekształceń:
1. Określenie okna we współrzędnych świata rzeczywistego.
2. Przesunięcie okna do początku układu współrzędnych.
3. Skalowanie okna, aby dopasować je do wymiaru pola wizualizacji.
4. Przesunięcie pola wizualizacji do właściwej pozycji na ekranie.
Powyższe odwzorowanie można zapisać następująco
umax - umin vmax - vmin
Mwv = T(umin, vmin) · S , · T(-xmin, -ymin) =
xmax - xmin ymax - ymin
îÅ‚ Å‚Å‚ îÅ‚ Å‚Å‚ îÅ‚ Å‚Å‚
0 0
1 0 umin umax-umin 1 0 -xmin
xmax-xmin
ïÅ‚ ïÅ‚ śł ïÅ‚
vmax-vmin
= 0 1 vmin śł · 0 0 0 1 -ymin śł =
·
ðÅ‚ ûÅ‚ ðÅ‚ ûÅ‚ ðÅ‚ ûÅ‚
ymax-ymin
0 0 1 0 0 1
0 0 1
îÅ‚ Å‚Å‚
umax-umin umax-umin
0 -xmin · + umin
xmax-xmin xmax-xmin
ïÅ‚
vmax-vmin vmax-vmin
0 -ymin · + vmin śł .
ðÅ‚ ûÅ‚
ymax-ymin ymax-ymin
0 0 1
Zatem powymnożeniu punktu P przez Mwv otrzymuje się P = Mwv[x, y, 1]T czyli
îÅ‚ Å‚Å‚
umax-umin
(x - xmin) · + umin
xmax-xmin
ïÅ‚
vmax-vmin
(y
P = - ymin) · + vmin śł
ðÅ‚ ûÅ‚
ymax-ymin
1
1. Transformacje obiektów 6
Jeżeli okno i pole wizualizacji nie mają takich samych stosunków wysokości
do szerokości, to ma miejsce skalowanie niejednorodne.
1.4. Reprezentacja macierzowa przestrzeni 3D
Podobnie, jak przekształcenia 2D mogą być opisywane za pomocą macierzy o wymiarze
3 × 3, przeksztaćenia 3D mogÄ… być reprezentowane za pomocÄ… macierzy 4 × 4, jeżeli
wykorzystane zostaną również współrzędne jednorodne do reprezentowania punktów
z przestrzeni trójwymiarowej.
1.4.1. Przesunięcie w 3D
Przesunięcie w 3D jest zwykłym rozszerzeniem przesunięcia w 2D
îÅ‚ Å‚Å‚
1 0 0 tx
ïÅ‚
0 1 0 ty śł
ïÅ‚ śł
T(tx, ty, tz) = ïÅ‚ śł ,
ðÅ‚ ûÅ‚
0 0 1 tz
0 0 0 1
to znaczy
T(tx, ty, tz) · [x, y, z, 1]T = [x + tx, y + ty, z + tz, 1]T
1.4.2. Skalowanie w 3D
Podobnie, jak przesunięcie, skalowanie w 3D jest rownież zwykłym rozszerzeniem
skalowania w 2D
îÅ‚ Å‚Å‚
sx 0 0 0
ïÅ‚ śł
0 sy 0 0
ïÅ‚ śł
S(sx, sy, sz) = ïÅ‚ śł ,
ðÅ‚ ûÅ‚
0 0 sz 0
0 0 0 1
to znaczy
S(sx, sy, sz) · [x, y, z, 1]T = [sxx, syy, szz, 1]T
1.4.3. Obrót w 3D
Obrót 2D jest szczególnym przypadkiem obrotu w 3D, to znaczy obrotem wokół osi
z. Obrót ten oraz pozostałe obroty wokół innych osi można przedstawić następująco
îÅ‚ Å‚Å‚
cos (Õ) - sin (Õ) 0 0
ïÅ‚ śł
sin (Õ) cos (Õ) 0 0
ïÅ‚ śł
Rz(Õ) = ïÅ‚ śł
ðÅ‚ ûÅ‚
0 0 1 0
0 0 0 1
îÅ‚ Å‚Å‚
1 0 0 0
ïÅ‚ śł
0 cos (Õ) - sin (Õ) 0
ïÅ‚ śł
Rx(Õ) = ïÅ‚ śł
ðÅ‚ ûÅ‚
0 sin (Õ) cos (Õ) 0
0 0 0 1
1. Transformacje obiektów 7
îÅ‚ Å‚Å‚
cos (Õ) 0 sin (Õ) 0
ïÅ‚ śł
0 1 0 0
ïÅ‚ śł
Ry(Õ) = ïÅ‚ śł
ðÅ‚ - sin (Õ) 0 cos (Õ) 0
ûÅ‚
0 0 0 1
1.4.4. Pochylanie w przestrzeni 3D
Pochylenie w płaszczyznie z
îÅ‚ Å‚Å‚
1 0 shx 0
ïÅ‚ śł
0 1 shy 0
ïÅ‚ śł
SHxy(shx, shy) = ïÅ‚ śł
ðÅ‚ ûÅ‚
0 0 1 0
0 0 0 1
P = [x + shxz, y + shyz, z, 1]T
Pochylenie w płaszczyznie y
îÅ‚ Å‚Å‚
1 shx 0 0
ïÅ‚ śł
0 1 0 0
ïÅ‚ śł
SHxz(shx, shz) = ïÅ‚ śł
ðÅ‚ ûÅ‚
0 shz 1 0
0 0 0 1
P = [x + shxy, y, z + shzy, 1]T
Pochylenie w płaszczyznie x
îÅ‚ Å‚Å‚
1 0 0 0
ïÅ‚ śł
shy 1 0 0
ïÅ‚ śł
SHyz(shy, shz) = ïÅ‚ śł
ðÅ‚ ûÅ‚
shz 0 1 0
0 0 0 1
P = [x, y + shyx, z + shzx, 1]T
2. Grafika żółwia
2.1. Współrzędne biegunowe grafika żółwia
Grafika żółwia jest techniką rysowania krzywych, w której zastosowano pojęcie
względnego opisu ruchu oraz rysowania we współrzędnych biegunowych. Takie połączenie
pozwala na osiągnięcie efektywnego narzędzia do rysowania wielokątów na płaszczyznie,
a także w przestrzeni 3D. Proces rysowania obiektów opisany jest tutaj za pomocą
sprecyzowania kierunku oraz odległości nowopowstałych odcinków. Program rysujący
przechowuje bieżący kierunek. Podczas rysowania nowego odcinka, najpierw ustala się
parametr zmiany kierunku, a następnie definiuje się długość nowokreślonego odcinka.
Taką zasadę tworzenia rysunków zastosowano również w urządzeniach kreślących, takich
jak plottery.
W grafice żółwia zdefiniowane są następujące operacje:
RysujWprzód(dystans) operacja rysuje z bieżącej pozycji w ustalonym kierunku
linię o długości określonej wartością parametru dystans.
PrzesuńWprzód(dystans) realizuje operację taką samą, jak operacjaRysujWprzód,
ale bez rysowania linii.
ObrótWPrawo(kąt) obraca żółwia w prawo o kąt określony wartością parametru
kąt. Obrót w lewo realizowany jest za pomocą ujemnej wartości parametru kąt.
Każdy ruch żółwia jest opisany za pomocą współrzędnych biegunowych (tzn. za
pomocą kierunku i odległości). Dodatkowymi operacjami, które można zdefiniować, są
operacjeObrótWLewo(kąt),ObróćNaKierunek(kąt)orazRysujWtył(dystans).
Rysunek 2.1 pokazuje typowy wygląd ekranu, na którym mały trójkąt w kole oznacza
położenie i kierunek żółwia.
Przykład z rysunku 2.1 został wygenerowany za pomocą następujących komend:
Procedure Dom; begin
RysujWprzód(20); { lewa ściana }
ObrótWPrawo(45); { obrót w prawo o~45 stopni }
RysujWprzód(28.2); { lewa część dachu }
ObrótWPrawo(90); { itd... }
RysujWprzód(28.2);
ObrótWPrawo(45);
RysujWPrzód(20);
ObrótWPrawo(90);
RysujWprzód(40); { ostatnia linia }
ObrótWPrawo(90); { obrót żółwia do wyjściowej pozycji }
2. Grafika żółwia 9
Rysunek 2.1: Przykład grafiki żółwia
end;
Procedura rysująca dom, zwraca żółwia ustawionego w pozycji wyjściowej. Takie
ustawienie żółwia jest zwykle stosowane w przypadku procedur rysujących takie elementy
graficzne, jak np. rys. 2.2.
Rysunek 2.2: Wielokrotne zastosowanie procedury Dom
2. Grafika żółwia 10
Rys. 2.2 narysowany został za pomocą następującego kodu programu
for i:=1 to 8 do begin
Dom;
ObrótWPrawo(45);
end;
Procedury posługujące się grafiką żółwia są bardzo łatwe w implementacji. Globalna
zmienna CD oznacza kierunek i jest definiowana w zastosowaniu ze zmiennÄ… CP . CD jest
kątem mierzonym w stopniach, w kierunku przeciwnym do ruchu wskazówek zegara od
osi x. Rysunek 2.3 pokazuje, jak, za pomocÄ… elementarnej trygonometrii, obliczana jest
wartość współrzędnych kartezjańskich nowego punktu, po wykonaniu operacji
RysujWprzód(dystans).
Ä„·CD
dist · sin
dist
180
CD
CP
Rysunek 2.3: Efekt proceduryRysujWprzód()
Procedurę RysujWprzód() można zrealizować zatem za pomocą następującego kodu
programu:
const TWOPI = 6.283185308; { 2 pi }
type point = record
x: real;
y: real;
end;
var CD: real;
CP: point;
Procedure RysujWprzód(dist: real); var angle: real;
p: point;
begin
angle := TWOPI * CD / 360.0; { konwersja stopni na radiany }
p.x := CP.x + dist * cos(angle);
p.y := CP.y + dist * sin(angle);
2. Grafika żółwia 11
LineTo(p.x, p.y);
CP := p
end;
ProceduraObrótWPrawo()może więc wyglądać następująco:
Procedure ObrótWPrawo(angle: real); begin
CD := CD - angle
end;
ProceduraPrzesuńWprzód()jest prawie identyczna z procedurąRysujWprzód().
W praktyce zamiast używać procedurRysujWprzód(), czyPrzesuńWprzód(), częściej
stosuje się procedury SchowajŻółwia() oraz PokażŻółwia(). Wówczas do
przemieszczania żółwia stosuje się funkcjiNaprzód(). Funkcja ta przemieszcza żółwia
w kierunku CD w sposób widoczny (z opuszczonym piórem) lub w sposób niewidoczny
(z podniesionym piórem), w zależności od wartości pewnej globalnej
zmiennej widać-żółwia. Zmienna ta jest typu logicznego i określa tryb rysowania.
Procedury SchowajŻółwia() oraz PokażŻółwia() modyfikują wartość
zmiennej widać-żółwia.
Rysunek 2.4: Przykład rysunku narysowanego za pomocą grafiki żółwia
2.1.1. Zastosowanie grafiki żółwia spirale
Grafika żółwia dostarcza dobrego narzędzia do rysowania dużej i różnorodnej rodziny
krzywych zwanych spiralami (polyspiral).
2. Grafika żółwia 12
Aby narysować spiralę, należy zacząć od bieżącej pozycji i kreślić w pewnym
początkowym kierunku odcinek o długości dist. następnie należy skręcić o jakiś kąt (angle)
i kreślić kolejny odcinek o długości większej lub mniejszej od poprzedniej. Zmiana długości
odcinka określana jest najczęściej pewną zmienną incr. Za pomocą tych trzech parametrów
można zbudować olbrzymi zbiór różnorodnych obrazków. Rys. 2.5 pokazuje kilka spiral
z ich parametrami (dist, angle, incr). Jeśli incr posiada wartość 0, wówczas rysunek
nie zwiększa ani nie zmniejsza swoich rozmiarów. Jeśli dodatkowo kąt angle posiada
wartość 360ć%/k, wówczas figura powtórzy się po k segmentach.
Rysunek 2.5: Przykłady spiral
3. Mapowanie współrzędnych
Bardzo często zachodzi potrzeba przeliczenia współrzędnych rzeczywistych na
współrzędne ekranu. Rys. 3.1 pokazuje okno W we współrzędnych rzeczywistych oraz
okno ekranowe (viewport) V w tzw. znormalizowanych jednostkach urzÄ…dzenia (normalized
device coordintates).
y
dy
W
Wt Vt
(x, y) (dx, dy)
Wb Vb
x
dx
Wl Wr Vl Vr
Rysunek 3.1: Współrzędne rzeczywiste i wspoółrzędne okna ekranowego
Lewą, prawą, dolną oraz górną pozycję okna W oznaczono przez Wl, Wr, Wt oraz Wb.
Podobne oznaczenia przyjęto dla okna ekranowego. Dla dowolnego punktu (x, y) okna
W , należy obliczyć odpowiadające mu współrzędne w oknie ekranowym V , np. (dx, dy).
Aby odległości od punktów pozostały proporcjonalne, do opisania współrzędnych można
zastosować równania liniowe
dx = sxx + tx,
dy = syy + ty,
w których należy ustalić odpowiednie wartości współczynników sx, sy, tx oraz ty. Jeżeli
x = Wl, wówczas dx = Vl, zatem pierwsze równanie przyjmie postać Vl = sxWl + tx.
Podobnie, gdy x = Wr, to dx = Vr, zatem Vr = sxWr + tx. W ten sam sposób można
przeanalizować współrzędne y. Ostatecznie otrzymuje się
Vr - Vl
sx = ,
Wr - Wl
Vt - Vb
sy = ,
Wt - Wb
VlWr - WlV r
tx = ,
Wr - Wl
VbWt - WbV t
ty = .
Wt - Wb
3. Mapowanie współrzędnych 14
3.1. Programowa realizacja mapowania
Programowa realizacja mapowania nie powinna sprawić wiele kłopotów. W pierwszej
kolejności należy zdefiniować typ danych, przechowujący współrzędne prostokąta rect.
Przechowuje on lewą l, prawą r, górną t oraz dolną b współrzędną brzegów prostokąta.
type
rect = record
l, t, r, b: real;
end;
procedure SetRect(left, top, right, bott:real; var r:rest); begin
with r do begin
l := left; t := top; r := right; b := bottom
end
end;
Okno rzeczywistych współrzędnych oraz okno ekranowe przedstawione są za pomocą jako
var W,V: rect. Następnie należy obliczyć wartości zmiennych sx, sy, tx oraz ty. Można
to zrealizować np. za pomocą procedury MapRect(W,V: rect; sx,tx,sy,ty:Real);.
W celu zwiększenia efektywności, zmienne te mogą zostać zdefiniowane globalnie. Ich
wartości można używać w procedurach tworzących rysunek, takich jak Line() itp.
Umożliwia to mapowanie rzeczywistych współrzędnych pWorld:Point na współrzędne
ekranowe pNDC:point, np.:
pNDC.x := sx * pWorld.x + ts; pNDC.y := sy * pWorld.y + ty;
Procedura kreśląca linię, która uwzględnia takie mapowanie, może wyglądać
następująco:
procedure Line(p1,p2:point); var pNDC1,pNDC2:point; begin
{ ewentualna modyfikacja współrzędnych związana z kadrowaniem }
pNDC1.x := sx * p1.x + tx;
pNDC1.y := sy * p1.y + ty;
pNDC2.x := sx * p2.x + tx;
pNDC2.y := sy * p2.y + ty;
LineNCD(pNDC1,pNDC2)
end;
Powyższa procedura może zostać uzupełniona o algorytm kadrowania (clipping), który
zostanie opisany w dalszej części.
Podobną analizę można przeprowadzić dla współrzędnych biegunowych.
3.2. Kadrowanie linii
Z kadrowaniem linii mamy do czynienia w przypadku, gdy kreślona krawędz
rzeczywistego obiektu nie mieści się w całości w oknie. Operację kadrowania zrealizować
3. Mapowanie współrzędnych 15
można w procedurze Cli[p(var p1,p2:points; var vis:boolean);. Procedura ta
przycina segmenty linii pomiędzy p1 oraz p2, zapisanych w rzeczywistych współrzędnych,
do rozmiaru okna. Jeżeli odcinek częściowo wystaje za obszar okna ekranowego, to
współrzędne jednego z konńcowych punktów (p1 lub p2) zostają obliczone na nowo tak,
aby punkt ten znajdował się na granicy okna, a wartość zmiennej vis zostaje ustawiona
na True. W przypadku, gdyby cały odcinek znalazł się poza oknem, zmiennej vis zostanie
przypisana wartość False. Rys. 3.2 pokazuje typowe sytuacje, obejmujące zakres działania
proceduryClip(). ZatemClip()wykonuje jedną z czterech czynności dla każdej linii,
z której zbudowany jest rysunek:
Nic nie robi, jeśli cała linia mieści się w oknie (segment CD).
Eliminuje całą linię, gdy ta w całości znajduje się poza oknem (segment AB).
Przycina jeden z końców, gdy jeden z punktów końcowych znajduje się poza
granicami okna (segment ED).
Przycina obydwa końce odcinka, gdy obydwa punkty końcowe znajdują się poza
oknem, ale część odcinka znajduje się wewnątrz okna ekranowego (segment AE).
A
B
C
E D
Rysunek 3.2: Kadrowanie krzywych do rozmiaru okna
Ze względu na fakt, że istnieje wiele możliwych ustawień odcinnka względem okna,
należy zastanowić się nad efektywnym podejściem do problemu kadrowania. Od procedury
kadrującej wymaga się dużej efektywności, gdyż w typowym rysunku mogą znalezć się
setki lub tysiące segmentów, a każdy musi zostać poddany operacji przycinania. Algorytm
Cohena-Sutherlanda stosuje w tym celu algorytm dziel i rzÄ…dz.
3.2.1. Algorytm Cohena-Sutherlanda
Algorytm Cohena-Sutherlanda szybko wykrywa i reaguje na dwa podstawowe
przypadki. Pierwszym jest sytuacja, w której obydwa końcowe punkty odcinka zawierają
się w oknie. Wówczas cały odcinek musi znajdować się wewnątrz okna i nie ma nic do
odrzucenia. Drugim przypadkiem jest sytuacja, w której obydwa końcowe punkty leżą
poza jedną z krzywych ograniczających okno. W takim przypadku cały odcinek leży poza
3. Mapowanie współrzędnych 16
oknem i powinien zostać w całości odrzucony. Sytuację taką przedstawia rysunek 3.3.
Taka sytuacja występuje najczęściej, gdy zastosowane jest małe okno do przedstawienia
rysunku składającego się z dużej liczby segmentów.
y
C
W
Wt
B
A
D
Wb
x
Wl Wr
Rysunek 3.3: Akceptacja bÄ…dz odrzucenie segmentu
Do określenia, czy końcowe punkty segmentu leżą wewnąrz lub na zewnątrz okna,
można zastosować podejście polegające na sprawdzeniu dla każdego położenia punktu
względem tzw. granicy półprzestrzeni. Całą przestrzeń dzieli się na dwie części, a granica
pomiędzy nimi ustalana jest prostą stanowiącą jednocześnie krawędz okna. Jeżeli obydwa
końce segmentu znajdują się w części, w której nie występuje okno, to oznacza, że segment
ma zostać odrzucony. Jeżeli dla wszystkich czterech krawędzi okna obydwa końce
segmentu zawierają się w tej częśi przestrzeni, w której leży okno, wówczas cały segment
zawiera się w oknie. Każdą krawędz okna można przedstawić w postaci nieskończonej
prostej. Do przechowywania wartości określających położenie punktów względem krawędzi
okna można zdefiniować następujący typ danych:
type
half_space_code = record
l, t, r, b: boolean
end;
Przypomina ona strukturę rect, ale zamiast wartości typu Real przechowuje ona wartości
logiczne. Składowe przyjmują wartość False, gdy punkty znajdują się po właściwej stronie
krzywej określającej odpowiednią krawędz okna lub True w przeciwnym przypadku.
Gdyby np. punkt leżał wewnątrz okna W , wówczas struktura half_space_code
zawierałaby wartości (false, false, false, false). Powyższych obliczeń można dokonać
poprzez wywołanie dla każdego punktu procedury:
procedure Encode(p: point; var c: half_space_code); begin
c.l := (p.x < W.l);
c.r := (p.x > W.r);
c.b := (p.y < W.b);
c.t := (p.y > W.t)
end;
3. Mapowanie współrzędnych 17
Rysunek 3.4 przedstawia sposób określania wartości składowych struktury
half_space_code.
false
c.l true
c.t W true
false
false
c.b
true
false c.r
true
Rysunek 3.4: Określanie wartości składowych strukturyhalfspacecode
Punkt p1 z wartościami strukturyhalfspacecode, przechowywanymi w zmiennej c1
będzie znajdował się wewnątrz okna, jeśli wszystkie wartości struktury c1 będą
równe false, co może być podsumowane w jednej zmiennej logicznej in1:
in1 := (not c1.l) and (not c1.t) and (not c1.r) and (not c1.b);
Zatem segment kreślony od punktu p1 do p2 będzie w całości narysowany jeśli obydwa
końcowe punkty będą znajdowały się wewnątrz okna ((in1 and in2) = true). Natomiast
odrzucenie nastÄ…pi, gdy zmienna
reject := (c1.l and c2.l) or (c1.r and c2.r) or (c1.t and c2.t)
or (c1.b and c2.b);
przyjmie wartość true.
Jeśli nie zajdzie żaden z powyższych warunktów, to segment musi być analizowany
dalej. W tym celu zastosowano metodÄ™ dziel i rzÄ…dz (divide and conquer). Segment jest
dzielony w miejscu przecięcia z którąś z krawędzi okna, i test jest wykonywany powtórnie
dla nowopowstałego odcinka. Jeżeli odcinek i tym razem nie spełni warunku odrzucenia
oraz akceptacji, wówczas znów dzieli się go, lecz tym razem stosując inną krawędz okna
i znów powtarza się test.
Rysunek 3.5 pokazuje przypadek, w którym segment jest dzielony prawą krawędzią
okna W . Współrzędne punktu A muszą zostać obliczone. Jego współrzędna x jest równa
współrzędnej prawej krawędzi okna (Ax = W.r). Współrzędna Ay wymaga skorygowania
p1.y o wartość d, lecz d jest równe e pomnożone przez nachylenie odcinka m. Zatem nową
wartość punktu p1 oblicza się ze wzoru:
p1.y := p1.y + (W.r - p1.x)*m;
3. Mapowanie współrzędnych 18
y
P1
W
Wt
d
A
e
Wb P2
x
Wl Wr
Rysunek 3.5: Przycinanie segmentu w miejscu przecięcia z krawędią okna
Jeśli odcinek jest pionowy, wówczs jego nachylenie nie może zostać obliczone (m = ").
Zatem przed obliczeniem m należy sprawdzić czy p1.x = p2.x. Jeśli ten warunek jest
spełniony, wówczas algorytm przycinania odcinka znacznie się upraszcza.
Powyższy algorytm umieszczony został w procedurzeClip(). Warunkiem stopu dla
algorytmu jest albo zaakceptowanie odcinka, albo odrzucenie go. Poniżej przedstawiono
kod proceduryClip()w języku Pascal
procedure Clip(var p1,p2:point, var vis:boolean); var
c1,c2,tmp_cd: half_space_code;
tmp_pt: point;
m: real; {pochylenie linii}
int1, in2, done: boolean;
procedure Encode(p: point; var c: half_space_code);
begin
c.l := (p.x < W.l);
c.r := (p.x > W.r);
c.b := (p.y < W.b);
c.t := (p.y > W.t)
end;
begin {Clip}
done := false;
vis := false;
repeat
Encode(p1,c1);
Encode(p2,c2);
in1 := (not c1.l) and (not c1.t) and (not c1.r) and (not c1.b);
in2 := (not c2.l) and (not c2.t) and (not c2.r) and (not c2.b);
if in1 and in1 then {odcinek zaakceptowany}
begin
done := true;
vis := true
3. Mapowanie współrzędnych 19
end
else if (c1.l and c2.l) or (c1.r and c2.r)
or (c1.t and c2.r) or (c1.b and c2.b) then
begin {odcinek odrzucony}
done := true;
vis := false
end
else begin {przynajmniej jeden punkt w środku}
if in1 then
begin { zamiana p1, p2 i c1,c2 }
tmp_cd := c1; c1 := c2; c2 := tmp_cd;
tmp_pt := p1; p1 := p2; p2 := tmp_pt
end;
if p2.x = p1.x then {linia pionowa}
begin
if c1.t then
p1.y := W.t {p1 jest powyżej}
else if c1.b then
p1.y := W.b {p1 jest poniżej}
end
else begin { linia nie jest pionowa }
m := (p2.y - p1.y)/(p2.x - p1.x); {nachylenie}
if c1.l then
begin
p1.y := p1.y + (W.l - p1.x) * m;
p1.x := w.l
end
else if c1.r then
begin
p1.y := p1.y + (W.r - p1.x) * m;
p1.x := W.r
end
else if c1.b then
begin
p1.x := p1.x + (W.b - p1.y)/m;
p1.y := W.b
end
else if c1.t then
begin
p1.x := p1.x + (W.t - p1.y)/m;
p1.y := W.t
end
end {linia pionowa}
end {przynajmniej jeden punkt w środku}
3. Mapowanie współrzędnych 20
until done
end; {Clip}
4. Wykresy liniowe i słupkowe
Prezentacja danych jest często bardzo istotnym elementem prezentacji
multimedialnych. Z wielu form wykresów, wykresy liniowe oraz słupkowe są najczęściej
stosowane. SÄ… one skonstruowane na podstawie listy lub tabeli danych liczbowych. Wykres
jest wizualnÄ… prezentacjÄ… tych danych w przyjemnej dla oka i Å‚atwej w zrozumieniu
i interpretacji formie.
Dane wejściowe mogą pochodzić z trzech głównych zródeł:
Dane otrzymane z pomiarów. Na przykład, gdy obserwuje się pewien eksperyment
i zapisuje się wartości wskazywane przez termometr, woltomierz lub inne urządzenie
pomiarowe. Innym przykładem jest akwizycja danych przez ankietera od losowo
wybranych osób i stworzenie listy zapisanych numerycznie odpowiedzi.
Dane obliczone z równań matematycznych. W tym przypadku zródłem danych może
być wartość funkcji matematycznej, dla różnej wartości parametrów wejściowych, np.
f(x) = sin(2Ä„x) + cos5(7Ä„x)/x3.
Próba zinterpretowania przebiegu takiej funkcji w pamięci może być trudna, natomiast
przedstawienie go w postaci wykresu z pewnością sprawę ułatwia.
Dane pochodzące z symulacji komputerowej. Wiele programów komputerowych jest
zaprojektowanych tak, aby symulować przebieg jakiegoś procesu. Proces ten jest często
modelowany poprzez zbiór równań, które opisują sposób zachowania się projektu.
4.1. Reprezentacja zródła danych
Dla każdego zródła danych, kreślona jest wynikowa sekwencja y1, y2, . . . , yn. Aby
przechowywać powyższe wartości, można zdefiniować typ danych sequence:
type
sequence = record
num = 0..maximum;
values: array[1..maximum] of real
end;
Dane mogą być prezentowane w różny sposób. Najczęściej, w przypadku wykresów
liniowych lub słupkowych, wartości yi, gdzie i = 1, . . . , n, są traktowane jako współrzędne
y i umieszczane w równych odstępach wzdłuż osi x. Wartości x dobierane są zgodnie z
równaniem
xi = i.
4. Wykresy liniowe i słupkowe 22
Różnice pomiędzy wykresami liniowymi oraz słupkowymi polegają na sposobie prezentacji
i łączenia poszczególnych punktów wykresu.
4.2. Wybór okna
Istotnym problemem jest dobór rozmiaru okna oraz ustalenie jego wewnętrznych
współrzędnych. Rozmiar okna powinien być tak dobrany, aby pomieścić całą krzywą
wykresu oraz pewien obszar marginesu. Krzywa wykresu rozciÄ…ga siÄ™ od 1 do n oraz
od ymin do ymax, określonych jako najmniejsza i największa wartość danych wejściowych.
Te ekstremalne wartości można obliczyć z pomocą funkcji
procedure Extremes(s: sequence; var min, max: real); var i:
Integer; begin
with s do begin
if num=0 then writeln( Error! No data! )
else begin
min := values[1]; max := min;
if num > 1 then for i := 2 to num do
begin
if values[i] < min then min := values[i];
if values[i] > max then max := values[i]
end {for}
end {else}
end {with}
end;
Po wyznaczeniu wartości ymin oraz ymax, można ustalić i przechować rozmiar okna W ,
używając funkcjiSetRect(1.0-bord,ymax+bord,data.num+bord,ymin-bord);.
4.3. Wykresy liniowe
Mając określony rozmiar okna można przystąpić do rysowania wykresu liniowego.
W tym celu można posłużyć się następującą funkcją
procedure LineGraph(data: sequence); const bord=0.3; var
ymin, ymax: real;
i: integer;
begin
if data.num > 1 then begin
Extremes(data,ymin,ymax);
SetRect(1-bord,ymax+bord,data.num+bord,ymin-bord,W);
.
. {użytkownik wybiera okno na ekranie V}
.
MapRects(W,V,sx,tx,sy,ty); {Ustawienie mapowania z okna W na V}
4. Wykresy liniowe i słupkowe 23
with data do
begin
MoveTo_(1.0,values[1]);
for i := 2 to num do LineTo_(i+0.0,values[i])
end;
Line_(1.0,ymin,1.0,ymax); {oÅ› pionowa}
Line_(1.0,0.0,data.num,0.0) {oÅ› pozioma}
end
end;
Rysunek 4.1: Przykład działania proceduryLineGraph()
4.4. Wykresy słupkowe
Wykresy słupkowe (bar graphs) oferują alternatywny format wyświetlania sekwencji
danych. Na wykresie tym każda wartość wejściowa określa wysokość słupka.
Aby wykonać prosty wykres słupkowy można zastosować proceduręBarGraph(data:
sequence; width: real). i-ty słupek zaczyna się we współrzędnych (i, 0). Zmienna
width oznacza szerokość słupka. Każdy słupek reprezentowany jest poprzez prostokąt
oparty na osi x.
Rysunek 4.2: Przykład działania proceduryBarGraph()
Trochę więcej kłopotów może sprawić rysowanie wykresu słupkowego, na którym
słupki rysowane są tak, jakby były trójwymiarowe. Na rysunku 4.3 przedstawiono
przykładowy wykres oraz sposób konstruowania pojedynczego słupka. W przypadku
takiego wykresu, zamiast zwykłego prostokąta, każdy słupek ma trzy ściany, które
4. Wykresy liniowe i słupkowe 24
reprezentują front, bok oraz górę bloku . Wysokość bloku określona jest wartością danej
wejściowej. Dolna krawędz boku rozpoczyna się w punkcie (i + width, 0.0) i biegnie
przekÄ…tnie pod kÄ…tem ¸ do punktu (i + 1, (1 - width) " tan(¸)). GórnÄ… pÅ‚aszczyznÄ™ można
skonstruować podobnie.
¸
vi
width
1-width
i i + 1
Rysunek 4.3: Trójwymiarowy wykres słupkowy
Wyszukiwarka
Podobne podstrony:
Wstep do pracy z grafiką i programem PAINT paliwoda10 Wstep do prawoznawstwaWstęp do pakietu algebry komputerowej Maple2006 06 Wstęp do Scrum [Inzynieria Oprogramowania]Wstęp do magiiRenesans Wstęp do epoki Podłoże społeczno polityczne ~5C5Wstęp do psychopatologiiBT Wstęp do Pierwszego Listu św Piotra apostołaWstęp do projektowania 2014 15 wykład 6,72009 10 27 Wstęp do SI [w 04]id&835BT Wstęp do Księgi MądrościWstęp Do Systemu Zabezpieczeń W Windows 2000więcej podobnych podstron