Geometrické
transformace
Úvodní
poznámky
V rovinné kartézské souřadné soustavě mějme body ; a zobrazení . Toto zobrazení lze zadat soustavou rovnic
(1)
Technická poznámka: Termín zobrazení
je zde chápán v matematickém slova smyslu a nemusí ještě znamenat vykreslení (znázornění) na papíře či na obrazovce počítače. Aby
nedocházelo k nedorozuměním, budeme místo zobrazení v
matematickém slova smyslu používat ekvivalentu transformace a místo zobrazení ve smyslu znázornění na papíře či na obrazovce počítače
používat pojem vykreslení.
Příklad 1: Naprogramujme transformaci
Zde najdete kompletní zdrojový kód a
zde spustitelný kód
Výsledek této transformace aplikované na daný trojúhelník vidíte na přiloženém obrázku. Tento obrázek však není vpořádku, o čemž se jednoduše předvědčíme. Střed úsečky má zřejmě souřadnice , střed úsečky pak . Podle transformačních rovnic se však zobrazí na bod
A nyní k výše uvedené
chybě. Ta je v předpokladu, že úsečka se transformací zobrazí
opět na úsečku. To však obecně neplatí. V těchto případech je třeba
i úsečku transformovat jako křivku:
Příklad 2: Zde je již úsečka transformována obecně:
ht:=0.005;t:=0;i:=0;
Repeat
P[i,1]:=A[1]+t*(B[1]-A[1]);
P[i,2]:=A[2]+t*(B[2]-A[2]);
Z(P[i],Q[i]);
t:=t+ht;i:=i+1
until t>1;
PolyLine(Q,i,Red,Green,Blue);
Proměnné P, Q nadeklarujeme jako TArrayOfPoints. Pole P použijeme jako množinu bodů úsečky AB, Q pak jako množinu bodů křivky s krajními body A', B'. Pole P naplníme pomocí parametrických rovnic úsečky AB, body Q dostaneme aplikací transformace na všechny body pole P: v cyklu tedy Z(P[i],Q[i]). Na pole Q pak aplikujeme proceduru PolyLine. Procedury Line, Triangle a PolyLine v jednotce Graph2D používají přístup do bitmapy pomocí obrazových řádků (ScanLine) a nepřebírají vlastnosti objektu Canvas (např. tloušťku kreslícího pera). Chceme-li kreslit např. silnější čarou, tečkovaně apod., je třeba nastavit příslušnou vlastnost kreslícího pera a použít proceduru cvLine (canvasLine), cvTriangle či cvPolyLine, které jsou v Graph2D rovněž k dispozici
Zde najdete kompletní zdrojový kód a
zde spustitelný kód
Příklad 3: Je doplněn o možnost zadávat rovnice nelineární transformace za běhu programu
Zde najdete kompletní zdrojový kód a
zde spustitelný kód
Na připojeném výstupu vidíme, že transformace úsečky značně deformuje. Toho můžeme využít k nejrůznějším efektům.
Příklad 4: Na připojeném obrázku vidíte vlajku ČR, na kterou byla aplikována transformace
kde jsou vhodně volené konstanty, jsou funkcí . Při výrobě takových obrázků musíme mít nejdříve představu o výsledku a až podle ní propočítáváme transformaci, kterou je třeba použít. Při transformacích obrazu rovněž většinou nevystačíme se zobrazováním pixelu na pixel tak, jak to bylo použito v tomto příkladě. Pak je třeba volit vhodnou interpolaci.
Zde najdete kompletní zdrojový kód a
zde spustitelný kód
Všechny transformace v rovině můžeme rozdělit do tří základních skupin. Označme transformaci, libovolné dva různé body v rovině a velikost úsečky . Hledejme nejmenší možné číslo tak, aby pro každé dva body platilo . Podle velikosti čísla - koeficientu kontrakce, můžeme všechny transformace rozdělit do tří základních skupin:
Kontrakce - - transformace zmenšuje
Shodnost
- - transformace zachovává
velikost
Extrakce - - transformace zvětšuje.
Geometrické
transformace a homogenní souřadnice
Dále se budeme zabývat transformacemi, ve kterých se úsečka zobrazí vždy na úsečku. V euklidovském prostoru jsou takové transformace vždy zadány soustavou lineárních rovnic, tj. soustava (1), která vyjadřuje vztah mezi souřadnicemi bodů , , je soustava lineárních rovnic
(2)
Transformace je tak určena maticí Z*
a vektorem
.
Příklad 1: Jsou dány body ; ; a transformace
Určeme body !
Tato transformace je vykreslena na připojeném obrázku
Zde najdete kompletní zdrojový kód a
zde spustitelný kód
Poznámka: Transformace, která přepočítává uživatelské souřadnice na souřadnice světové a kterou již delší dobu používáme ve funkcích XCoor a YCoor, je lineární transformací, neboť dle značení v soustavě (1) je
Transformaci lze s výhodou určit jedinou maticí v tzv. homogenních souřadnicích.
K tomuto vyjádření je třeba si zopakovat něco o rozšířeném prostoru. Připomeňme, že rozšířený euklidovský prostor je prostor, kde kromě „běžných“ axiomů platí ještě další dva, a sice:
Každé dvě přímky, které leží v téže rovině, mají společný bod
Každé dvě roviny mají společný bod.
Tento prostor si můžeme představit (modelovat) jako „běžný euklidovský prostor, který má však jeden rozměr „navíc“. Těmito otázkami jsme se zabývali již v v 1. semestru, zájemci mohou kliknout pro podrobnější informace sem. Zde uveˇme, že například dvojrozměrný rozšířený prostor (rozšířenou rovinu) si můžeme představit jako „běžný“ trojrozměrný prostor, bod jako euklidovskou přímku, která prochází počátkem souřadné soustavy, přímku jako rovinu, která rovněž prochází počátkem souřadné soustavy. Protneme-li náš model rovinou o rovnici (na připojených obrázcích vyznačena žlutě), představuje tato rovina běžnou euklidovskou rovinu. Dvě různoběžky mají společný bod modelovaný přímkou, jejíž libovolný směrový vektor je tvaru , přičemž . Takový společný bod dvou přímek nazýváme bodem vlastním. Dvě rovnoběžky mají rovněž společný bod. Libovolný směrový vektor přímky, která takový bod modeluje, má rovněž tvar , nyní je ovšem Takový společný bod dvou přímek nazýváme bodem nevlastním.
Souřadnice libovolného směrového
vektoru přímky, která modeluje
bod rozšířeného prostoru nazýváme homogenními souřadnicemi tohoto bodu. Každý
vlastní bod, tj,. bod, pro který je , je možné reprezentovat směrovým vektorem , jehož koncový bod leží v rovině o
rovnici a má kartézské
souřadnice . Je zřejmé, že množina všech vlastních bodů rozšířeného
prostoru „funguje“ stejně jako „běžná“ euklidovská rovina.
Transformace dvojrozměrného
rozšířeného prostoru jsou zřejmě vyjádřeny soustavou lineárních rovnic
tj.
příslušná transformace je tedy určena již jen jedinou maticí.s nenulovým determinantem.
Omezíme-li se v rozšířeném prostoru pouze na vlstní body a transformace, kde se každý vlastní bod zobrazí opět na vlastní bod, je příslušná transformace tvaru
Z toho ovšem vyplývá, že příslušná matice má tvar
tj.
takže je
(3)
Všimněme si, že ke stejnému výsledku dospějeme, přidáme-li k soustavě (2) ještě jednu "rovnici"
(4)
Soustava (4) vyjadřuje totéž, co soustava (3), jen je použito poněkud jiné označení.
Transformace tak může být určena
jedinou maticí typu 3´3.
Bude to mít své výhody především tehdy, až budeme transformace skládat. Dříve
než přikročíme k dalšímu příkladu, provedeme ještě jednu změnu - soustavu
(4) budeme používat v transponované podobě, tedy:
(5)
Důvody budou zřejmé při skládání transformací. Je zřejmé, že prvky ; můžeme označit ; .
Příklad 2.: Transformaci
z předchozího příkladu zprogramujme pomocí rovnice (3). Je zřejmé, že
pokud chceme používat homogenní souřadnice, musíme předefinovat TPoint na
třísložkové pole. Každý bod v rovině bude pak mít třetí složku jedna.
Musíme definovat typ Matice: TMatrix = array [1..3,1..3] of Double, dále proceduru,
která nastavuje transformační matici Následuje procedura, která počítá rovnici
(3). Následující procedury jsou v řešeném příkladu umístěny v jednotce Graph2D
jako metody objektu Draw2D. Místo bodů A, A' je použito označení X, Xc (bod
X transformujeme pomocí matice Z do
bodu Xc):
procedure Draw2D.SetMatrix(var
Z:TMatrix);
begin
Z[1,1]:=-2;Z[1,2]:=-1;Z[1,3]:=0;
Z[2,1]:= 1;Z[2,2]:= 2;Z[2,3]:=0;
Z[3,1]:= 6;Z[3,2]:= 5;Z[3,3]:=1;
end;
procedure Draw2D.TransOfPoint (X:TPoint;Z:TMatrix;var Xc:TPoint);
var
i,j:Byte;
begin
for
i:=1 to 3 do Xc[i]:=0;
for
j:=1 to 3 do
for
i:=1 to 3 do Xc[j]:=Xc[j]+X[i]*Z[i,j];
end;
Protože budeme často transformovat celý trojúhelník, napíšeme si příslušnou proceduru:
procedure Draw2D.TransOfTriangle(A,B,C:TPoint;Z:TMatrix;var Ac,Bc,Cc:TPoint);
begin
TransfOfPoint(A,Z,Ac);
TransOfPoint(B,Z,Bc);
TransOfPoint(C,Z,Cc);
end;
Vlastní transformace spočívá pak v nastavení transformační matice a transformaci trojúhelníka pomocí této matice: SetMatrix(Z);TransOfTriangle(A,B,C,Z,Ac,Bc,Cc). Výstup z tohoto příkladu neuvádím, protože je stejný, jako v předchozím příkladě.
Zde najdete kompletní zdrojový kód a
zde spustitelný kód
V příkladu 3, jehož zdrojový kód najdete zde a spustitelný kód je zde
je pak zpracován předchozí algoritmus obecněji. Vrcholy trojúhelníka i transformační matici může uživatel opět zadávat z ovládacího panelu.
Poznámka: v dalším budeme transformace značit zásadně psacími písmeny (,...), kdežto jejich matice písmeny tučnými (Z, R, T, H...)
Základní
transformace v rovině
V této kapitole uvedeme některé lineární transformace, které čtenář jistě zná. Ve stručném přehledu uvedeme jejich vyjádření soustavou lineárních rovnic a jejich transformační matice
Osová souměrnost podle osy x Osová souměrnost podle osy y
Označení: Označení:
Rovnice : Rovnice :
Matice Matice
Středová souměrnost podle počátku Rotace kolem počátku o úhel :
Označení: Označení:
Rovnice : Rovnice :
Matice: Matice:
Translace
o vektor
Označení:
Rovnice :
Matice :
Všechny tyto transformace jsou, jak známo, shodné, tj. zachovávají velikost úseček. Transformace je shodnost právě tehdy, je-li absolutní hodnota determinantu její matice rovna jedné. Přitom se jedná o shodnost nepřímou právě tehdy, je-li tento determinant minus jednička (osové souměrnosti), o shodnost přímou pak právě tehdy, je-li determinant jednička (ostatní shodnosti).
Poznámka: Čtenář si možná vzpomene, že na základní škole používal k ověření shodnosti průsvitky, pomocí níž přemisťoval jeden útvar na druhý tak, aby se kryly. Pokud se mu to podařilo, byly útvary shodné. U některých útvarů stačilo průsvitku vhodně posunout či natočit (u přímé shodnosti), někdy bylo třeba průsvitku převrátit naruby (u nepřímé shodnosti).
Příklad 1.: Sestavme program, který bude „umět“ výše uvedené shodné transformace. Do programu z př.3 předchozí kapitoly stačí přepsat procedury nastavující matice výše uvedených transformací, např.
Procedure Draw2D.SSym(var S:TMatrix);
begin
S[1,1]:=-1; S[1,2]:= 0; S[1,3]:=0;
S[2,1]:= 0; S[2,2]:=-1; S[2,3]:=0;
S[3,1]:= 0; S[3,2]:= 0; S[3,3]:=1;
end;
Ve vlastním programu pak sestrojíme středově souměrný trojúhelník takto:
With Draw2D do
begin
SSym(Z);
TransOfTriangle(A,B,C,Z,Ac,Bc,Cc);
Draw2D.Triangle(Ac,Bc,Cc,Red,Green,Blue);
end;
Můžeme tak provést všechny
transformace dle výstupu na přiloženém obrázku. V řešeném příkladu jsou všechny
potřebné transformace již umístěny přímo v jednotce GRAPH2D, kterou si můžete
stáhnout v každém balíčku zdrojových kódů.
Zde najdete kompletní zdrojový kód a
zde spustitelný kód
Poznámka.: Aby obrazy útvarů byly skutečně shodné se svými vzory, je třeba zajistit stejné jednotky na obou osách. To lze řešit např. tak, že zadáme „rozměry“ osy x a jeden koncový bod osy y. Druhý koncový bod pak dopočítáme:
x1:=-4;x2:=4;y2:=13;
With
Image1 do
y1:=y2-(x2-x1)*Height/Width;
Další transformace již nejsou obecně shodné:
Osová afinita s osou v ose y Osová afinita
s osou v ose x
Označení: Označení:
Rovnice : Rovnice :
Matice Matice
Stejnolehlost (homotetie) se středem v počátku
Označení:
Rovnice
:
Matice :
Stejnolehlost lze použít na zvětšování či zmenšování objektů, osovou afinitu pak k jejich zkosení.
Příklad 2.: K programu z příkladu 1 připište tři výše uvedené transformace. Situace je zcela analogická předchozímu příkladu. Vzhledem k tomu, že vlastnosti afinity nejlépe vyniknou na „pravidelných“ útvarech, zařadili jsme do programu proceduru konstrukce čtyřúhelníka a jeho transformaci. Na přiloženém obrázku vidíme obdélník zobrazený ve stejnolehlosti a osové afinitě podle osy x resp.y.
Zde najdete kompletní zdrojový kód a
zde spustitelný kód
Příklad 3.: Transformací lze pochopitelně využít i při práci s písmem a obrázky. Tento příklad otáčí písmo. Nápis si opatříme metodou TextOut. Na souřadnice jednotlivých bodů textu se ptáme pomocí GetPixel v jednotce Graph2D. Tato metoda však pracuje se světovými souřadnicemi, kdežto naše transformace se souřadnicemi uživatelskými. Převod uživatelských souřadnic na světové počítají funkce XCoor, YCoor. Nyní potřebujeme také přepočet souřadnic světových na uživatelské. V programu je provádějí funkce InvXCoor, InvYCoor - funkce inverzní k výše uvedeným. Vlastní výkonná procedura nejdříve nastaví stejné jednotky na obou osách (výše uvedeným způsobem), dále nastaví parametry rotace: Alfa:=pi/3; Rot(Alfa,Z). Dále je třeba projít celou kreslící plochu:
With Image1
do
for i:=0 to Width-1 do
for j:=0 to Height-1 do
begin
Pixel[1]:=i;Pixel[2]:=j;
GetPixel(Pixel,Red,Green,Blue);
if (Red=0) and (Green=0) and
(Blue=200)
{je-li nalezen modrý bod - písmo}
then begin
A[1]:=InvXCoor(i);
A[2]:=InvYCoor(j); {je převeden do uživatelských souřadnic}
TransOfPoint(A,Z,Ac); {orotován}
PutPoint(Ac,200,0,0); {a
vykreslen červeně}
end;
end;
Poznámka: K transformacím již nevyužíváme jednotky Transf2D, všechny potřebné transformace jsou součástí Graph2D.
Zde najdete kompletní zdrojový kód a
zde spustitelný kód
Příklad 4.: Tento příklad využívá analogickým způsobem homotetie ke zmenšování obrázků. Velký obrázek je načten ze souboru pomocí Picture. LoadFromFile. Procedura pak bere všechny body z horních dvou třetin okna, prohání je maticí stejnolehlosti s koeficientem 0.5 a poté je vykreslí. V tomto konkrétním případě se vždy čtveřice bodů původního obrázku vykreslí jako jediný bod obrázku zmen-šeného. Pokud bychom zmenšovali v obecném poměru, nastanou při tomto nejjednoduším způsobu samozřejmě potí-že. Při zvětšování tímto způsobem bychom dostali obrázek „děravý“. Změny velikosti obrazů nejrůznějších profesionálních systémů jsou samozřejmě založeny také na stejnolehlosti. Výše uvedené problémy jsou řešeny nejrůznějšími typy interpolací.
Zde najdete kompletní zdrojový kód a
zde spustitelný kód
Příklad 5: Osové afinity lze využít např. k vytvoření stínů. Tento příklad je analogií příkladu předcházejícího. Místo rotace je pochopitelně použita afinita. Na jejích parametrech (nastavení bodu P) závisí tvar stínu. Jeho barvu nastavíme podle použitého pozadí. V příkladu je použito pozadí bílé, stín je tedy lehce šedý (Red=Green=Blue=220).
Zde najdete kompletní zdrojový kód a zde spustitelný kód
Stínované písmo je zvlášť působivé ve spojení se stínovaným podkladem.
Příklad 6: Text je zde součástí osvětleného podkladu.
Zde najdete kompletní zdrojový kód a zde spustitelný kód
Příklad 7: Text vrhá stín na podklad. Stín je obrazem písma v osové afinitě s osou x.
Zde najdete kompletní zdrojový kód a
zde spustitelný kód
Jednoduché animace: Transformací lze použít k vyvolání iluze pohybu. Máme-li nějakou transformaci a chceme vytvořit iluzi, že se do „plynule“ přemisťuje, musíme transformaci rozložit do posloupnosti transformací Z1 Z2... Zn a tyto transformace postupně provést podle následujícího schématu:
Sestroj DABC
Z1:DABC®DA1B1C1
Vymaž DABC
Sestroj DA1B1C1
............................
Sestroj DAn-1Bn-1Cn-1
Z1: DAn-1Bn-1Cn-1®DA'B'C
Sestroj DA'B'C
Příklad 8: Osová souměrnost podle
osy y: Má-li se trojúhelník „překlopit“ přes osu y, je třeba postupně provádět
kolmé osové afinity. K určení této afinity použijeme bod , který je deklarován v Draw2D. Jeho počáteční poloha je . Třetí souřadnici prozatím nevyužijeme, první budeme
zmenšovat o proměnnou Koef až na -1. Prográmek je navržen tak, aby
uživatel mohl za běhu programu měnit rychlost pohybu. Docílíme toho pomocí
šoupátka (TrackBar
v liště Win32)
a časovače (Timer
v liště System).
Na šoupátku vhodně zvolíme hodnoty Min, Max a Position (tj. minimální,
maximální a aktuální hodnotu). Pomocí globální proměnné Continue typu Boolean
naprogramujeme zdržení programu:
Procedure Delay;
begin
Continue:=False;
Repeat Application.ProcessMessages;until Continue;
end;
Procedura nastaví Continue na
False a v cyklu nechává zpracovávat události tak dlouho, dokud proměnná
Continue není True. Toto nastavení zajistí časovač. V jeho vlastnostech je
Interval, který v tisícinách vteřiny určuje, jak často se „má hlásit o slovo“
událost OnTimer. Zde kromě zmíněného nastavení můžeme ještě změnit interval
časovače v závislosti na poloze šoupátka, např:
procedure
TControl_Panel.Timer1Timer(Sender: TObject);
begin
timer1.interval:=trackbar1.Position*50;
Continue:=True;
end;
Tato procedura jednak mění interval časovače
(změna o jednotku na šoupátku znamená změnu intervalu
o 50 milisekund) a jednak nastaví Continue na True, čímž umožní proceduře Delay
ukončit cyklus a tím pokračování programu. Vlastní animace
pak může vypadat např. takto:
procedure AnimYSym;
var Koef:Double;
begin
With
Draw2D do
begin
Koef:=0.02;D[1]:=1;D[2]:=0;D[3]:=1;
cvTriangle(A,B,C,Red,Green,Blue);
Ac:=A;Bc:=B;Cc:=C;
Image1.Canvas.Pen.Mode:=pmNotXor;
Repeat
D[1]:=D[1]-Koef;AfY(D,Z);
cvTriangle(Ac,Bc,Cc,Red,Green,Blue,False);
TransOfTriangle(A,B,C,Z,Ac,Bc,Cc);
cvTriangle(Ac,Bc,Cc,Red,Green,Blue);
Delay;
Until A[1]<=-1;
end;
Procedura je navržena tak, že kreslí
do objektu Canvas obrázku Image1 (nikoli tedy na obrazové řádky). Konstrukce je
poněkud pomalejší, to však zde není na závadu, neboť stejně program „uměle“
zdržujeme procedurou Delay. Zato máme k dispozici vlastnosti kreslícího pera,
které bychom jinak museli programovat ve vlastní režii. Zde bodeme potřebovat
mód kreslícího pera pmNotXor. Ten znamená, že barva konstrukce vznikne
složením deklarované barvy konstrukce a negativu podkladu. Je-li tedy např
modrý trojúhelník překreslen tímtéž modrým trojúhelníkem, dojde k jeho
vymazání.
Zde najdete kompletní zdrojový kód a zde spustitelný kód
Základní
transformace v prostoru
Transformace v rozšířeném trojrozměrníém prostoru je určena soustavou
Opět se zaměříme na transformace afinní, tj. na případ z41=z42=z43=0; x4=1, soustavu opět transponujeme a vrátíme se k obvyklému značení, tedy
Uveďme nyní ve stručnosti několik speciálních matic, které určují nejznámější transformace (pořadě jsou uvedeny matice osových souměrností podle souřadných os a rovin, rotace kolem souřadných os, středová souměrnost podle počátku, translace)
Příklad 1: Zobrazte krychli ve volném rovnoběžném promítání, její posunutí a rotaci kolem souřadných os.
Control panel umožňuje nastavit souřadnice všech vrcholů krychle, která je zde nazvána obecně šestistěn (zadáváme libovolně osm bodů). Přiložený obrázek ukazuje, proč kosoúhlou axonometrii nepovažuji za promítací metodu vhodnou pro počítačovou grafiku. Vidíte průmět krychle (!). Jeden její vrchol je v počátku, podstava byla původně v rovině xy. Zatímco průmět této krychle jsme ochotni tolerovat (viz př.1), v průmětu jejího otočení o 0.12p by krychli hledal asi málokdo.
Program plynule otáčí krychli
kolem souřadných os.
Zde
najdete kompletní zdrojový kód a
zde
spustitelný kód
Příklad 2: v této modifikaci může uživatel transformaci volit za běhu programu a každý krok čeká na stisk tlačítka start nebo mezerníku, popř. klávesy Enter. Jednotlivé průměty je tak možno lépe prohlížet.
Zde
najdete kompletní zdrojový kód a
zde
spustitelný kód
Další dva příklady řeší totéž v kolmém promítání
Příklad 3: je obdobou příkladu 1.
Zde
najdete kompletní zdrojový kód a
zde
spustitelný kód
Příklad 4: je obdobou příkladu 2.
Zde
najdete kompletní zdrojový kód a
zde
spustitelný kód
Příklad 5. Demonstrace jednodílného rotačního hyperboloidu jako přímkové plochy: Jednodílný rotační hyperboloid vzniká jak známo rotací přímky kolem osy mimoběžné s touto přímkou, přičemž tyto dvě přímky na sebe nejsou kolmé. Program má obvyklý ovládací panel, na kterém lze nastavit měřítko rovinné uživatelské soustavy a směr pohledu. Dále lze definovat rotující úsečku dvěma krajními body a pomocí objektů RadioRotX, RadioRotY, RadioRotZ typu RadioButton určit souřadnou osu, kolem níž má úsečka rotovat. S tlačítkem Setting je spojena OnClick procedura SettingCP, která nastaví naeditované parametry a vykreslí výchozí situaci, tj. souřadnou soustavu a definovanou úsečku AB. Tlačítko start je spojeno opět OnClick s procedurou Hyperboloid, která provede vlastní konstrukci:
procedure TControl_Panel.Hyperboloid (Sender: TObject);
Const Pocet = 40; {počet úseček}
Step = 2*pi/Pocet; {krok rotace}
var
i :Integer; Matrix :TMatrix;
begin
With Draw2D
do
Begin {dle
označené osy rotace nastaví matici}
if RadioRotX.Checked then RotX(Step,Matrix);
if RadioRotY.Checked then
RotY(Step,Matrix);
if RadioRotZ.Checked then
RotZ(Step,Matrix);
for i:=1
to Pocet do
begin
TransOfPoint(A,Matrix,Ac); {rotace bodu A}
TransOfPoint(B,Matrix,Bc); {rotace bodu B}
Line(Ac,Bc,255,0,0); {úsečka}
A:=Ac;B:=Bc; {přechod na další úsečku}
end;
end;
end;
end;
Zde najdete kompletní zdrojový kód a zde spustitelný kód
Skládání
lineárních transformací
Velmi často potřebujeme použít několik transformací za sebou. Chceme-li například sestrojit otáčení bodu A o úhel a kolem bodu S různého od počátku O, je třeba nejdříve bod A posunout o vektor do bodu A1 , bod A1 otočit o úhel kolem počátku do bodu A2 a konečně bod A2 posunout o vektor do bodu A'. Označíme-li postupně , , , pak rotaci , dostaneme postupným provedením transformací Z1, Z2, Z3. Matematikové mají pro skládání transformací k dispozici operaci o („po“). Zápis čtou "j po y". Pořadí transformací v zápisu je tedy opačné, než jejich provádění. Zdůvodňují to „funkčním“ zápisem této kompozice. Je-li totiž bod A transformován do A' postupně transformacemi y, j, lze psát . Matematikové by tedy napsali a obrácené pořadí transformací v tomto zápisu by zdůvodňovali zápisem . Je zřejmé, že tento způsob zápisu je pro tři transformace již méně únosný. My však budeme málokdy skládat jen dvě transformace. Pro vytvoření obecné osové souměrnosti v prostoru bude potřeba složit dokonce sedm transformací. Při použití výše uvedené operace bychom zápis pak museli číst pozpátku dost dlouho. A zdůvodňovat toto přeškolení na Araby zápisem mi připadá již úplně absurdní. Budu tedy nadále používat operaci „potom“ (ozn. ). Zápis tedy znamená, že bod A podrobíme nejdříve transformaci Z1, potom nasadíme Z2 a nakonec Z3.
Složenou transformaci můžeme získat nejen postupným provedením jejich složek, ale také tak, že spočítáme její matici. Díky homogenním souřadnicím je každá transformace vyjádřena jedinou maticí typu 3´3 a matici složené transformace získáme pouhým součinem matic jednotlivých složek. Transpozice soustavy (2) na soustavu (3) v kapitole 7.2. je pak odůvodněna operací , která „zachovává pořadí“ skládání transformací. Matici transformace získáme jako součin matic jednotlivých složek (kdybychom rovnici (2) kpt. 6.2. netransformovali na (3), museli bychom netransformované matice z rovnice (2) násobit opět v obráceném pořadí).
Příklad 1.: Sestrojte a jeho obraz v rotaci .
Především budeme potřebovat proceduru pro součin matic:
Procedure Multip(M,N:TMatrix;var Result:TMatrix);
Var i,j,k:Integer;
begin
for i:=1to 3 do for j:=1 to 3 do Result[i,j]:=0;
for i:=1to 3 do for k:=1to 3 do for j:=1 to 3 do
Result[i,j]:=Result[i,j]+M[i,k]*N[k,j];
end;
a dále proceduru pro rotaci kolem
obecného bodu. Hledanou rotaci dostaneme zřejmě takto:
(1)
Procedure
GenRot(S:TPoint;Alfa:Double;
var Mat:TMatrix);
var Mat1,Mat2
:TMatrix; {mezivýsledné
matice}
v :TVector; {vektor posunutí}
begin
V[1]:=-S[1];V[2]:=-S[2];
Trans (V,Mat1); {nastavení matice posunutí}
Rot (Alfa,Mat2); {nastavení matice rotace
kolem počátku}
Multip
(Mat1,Mat2,Mat); {součin
mezivýsledků}
V[1]:=-V[1];V[2]:=-V[2]; {vektor zpětného
posunutí}
Mat1:=Mat;
Trans (V,Mat2);
{matice zpětného posunutí}
Multip (Mat1,Mat2,Mat);
end;
Poznámka: abychom i „vizuálně“ zachovali shodnost trojúhelníků, je nezbytně nutné používat na obou osách stejných jednotek. V tomto příkladu je v Control panelu znepřístupněno okénko pro editaci proměnné y2 (y-ová souřadnice horního okraje kreslící plochy. Okénka pro editaci souřadnic zbývajících okrajů jsou přes On Exit spojeny s procedurou SetY2, která pro nastavované hodnoty x1, x2, y1 hodnotu y2 automaticky dopočítává. Středová souměrnost podle obecného bodu je speciální případ rotace, stejnolehlost podle obecného bodu dostaneme analogicky - rovnici (1) nahradíme rotaci R(O,a) stejnolehlostí H(O, l).
Zde najdete kompletní zdrojový kód a zde spustitelný kód
Příklad 2.: Sestrojte a jeho obraz v osové symetrii
První problém, který musíme
vyřešit, je způsob zadání osy souměrnosti. Jednou z možností jsou
koeficienty při zadání osy rovnicí . Domnívám se však, že v tomto případě je vhodnější
zadání osy dvěma body . Podobně jako v předchozím příkladě je třeba obecnou
osu transformovat na speciální případ - zde je to zřejmě její ztotožnění
s osou . To provedeme tak, že přímku posuneme pomocí
translace o vektor do
počátku. Tato translace zobrazí bod na bod : , přímku na : Tuto posunutou
osu otočíme
rotací o úhel , kde . Je zřejmě
Nyní provedeme osovou souměrnost podle osy : a inverzní rotací a translací situaci „vrátíme do původní polohy“:
Osovou souměrnost podle obecné přímky tedy dostaneme takto:
a její matici vynásobením příslušných transformačních matic. Použijeme-li podobným způsobem osovou afinitu s osou v ose , dostaneme osovou afinitu podle obecné osy určené dvěma body.
Zde najdete kompletní zdrojový kód a zde spustitelný kód
Příklad 3.: Simulujme valení
kružnice po vodorovné přímce. Jeden krok valivého pohybu složíme zřejmě
z rotace kružnice kolem jejího středu o úhel
a a translace ve směru osy , tj o vektor . Nemá-li docházet ke smyku, musí platit, že délka
oblouku příslušného středovému úhlu a je rovna . Rotujeme tedy o úhel (pohybuje-li se
kružnice zleva doprava, je a kružnice rotuje
po směru hodinových ručiček, tedy v záporném smyslu). Střed rotace je po každém
kroku jiný. Výpočet matice provádí tato procedura:
Procedure RollCircLine(S:TPoint;r,h:Double;var Mat:TMatrix);
var V:TVector; T1,T2:TMatrix;
begin
v[1]:=h;v[2]:=0;v[3]:=1; {nastavení vektoru translace}
GenRot(S,-h/r,T1); {nastavení rotace}
Trans(V,T2); {nastavení translace}
Multip(T1,T2,Mat); {složení}
end;
Aby bylo
zřejmé, že se kružnice valí a nikoliv jen posouvá, spojíme s ní pevně
dva navzájem kolmé průměry - loukotě KM, LN.
Tuto konstrukci provádí procedura Disc na základě znalosti středu
a jednoho bodu na obvodu disku Ostatní body dopočítáme pomocí rotace okolo
středu o úhel p/2:
Procedure
Disc(S,
K:TPoint;Red,Green,Blue:Byte);
var L, M, N:TPoint;T :TMatrix;Radius :Double;
begin
Radius:=sqrt(sqr(s[1]-k[1])+sqr(s[2]-k[2])); {poloměr}
With
Draw2Ddo
begin
GenRot(S,pi/2,T); {rotace se středem S o p/2}
TransOfPoint(K,T,L);
TransOfPoint(L,T,M);
TransOfPoint(M,T,N); {výpočet loukotí}
{sestrojení disku}
cvCircle(S,r,Red,Green,Blue); {kružnice}
cvLine(K,M,Red,Green,Blue);
cvLine(L,N,Red,Green,Blue); {loukotě}
end;
end;
Tuto proceduru pak použijeme několikrát za sebou k vyvolání iluze pohybu - viz odstavec jednoduché animace v předchozí kapitole (kreslit musíme opět buď přímo na pozadí a pozdržet program procedurou Sleep, nebo můžeme kreslit do objektu Image a program pozdržet překreslením).
Zde najdete kompletní zdrojový kód a zde spustitelný kód
Příklad 4: v tomto příkladu je doplněno sestrojení dráhy pera - zkrácené či prodloužené loukotě (její délku lze volit na Control panelu parametrem délka kreslícího pera .
Zde najdete kompletní zdrojový kód a zde spustitelný kód
Příklad 5.: Simulujme valení kružnice po kružnici.
Tento pohyb musíme složit z rotace valící se kružnice okolo jejího středu o úhel a rotace téže kružnice okolo středu dráhy o úhel tak, aby délky oblouků , byly navzájem rovny, tj. .
Zde najdete kompletní zdrojový kód a zde spustitelný kód
Příklad 6 opět kreslí dráhu pera spojeného s valící se kružnicí.
Zde najdete kompletní zdrojový kód a zde spustitelný kód
Příklad 7 přidal do tohoto pohybu ještě jednu kružnici. Umožňuje volit poloměry, rychlosti a smysl otáčení valících se kružnic, jakož i vnější či vnitřní dotyk. Při pohledu na roztodivné křivky, které dostáváme při nejrůznějších zadáních, se nelze ubránit obdivu ke středověkým astronomům. Hájíce geocentrickou soustavu proti podobně vypadajícím naměřeným zdánlivým drahám planet na obloze totiž řešili inverzní úlohu. Předpokládali, že dráhy planet tvoří valící se kružnice a ke zdánlivým dráhám tyto kružnice a jejich rychlosti dopočítávali. Jakkoli se nám dnes toto jejich počínání může zdát absurdní, pravdou zůstává, že mnohé jejich výsledky jsou neobyčejně přesné, i když jedinou jejich výpočetní technikou byl husí brk a papír.
Zde najdete kompletní zdrojový kód a zde spustitelný kód
Příklad 8 - valení přímky po kružnici. Postup je podobný postupu v příkladu 4. Valivý pohyb se opět skládá z rotace kolem středu kružnice a translace přímky o vektor, jehož velikost musí souhlasit s délkou oblouku příslušnému úhlu rotace. Tentokrát se ovšem nemění úhel rotace, ale směr posunutí, dráhou je křivka zvaná evolnenta. Podobně jako předchozí příklady i tento řešený příklad umožňuje volit evolnentu prodlouženou či zkrácenou volbou délky kreslícího pera (na připojeném obrázku je evolventa prodloužená.
Zde najdete kompletní zdrojový kód a zde spustitelný kód
Příklad 9: Zde jsou evolventy dle předchozího příkladu sestrojovány v cyklu a v obou směrech tak, že výsledkem procesu je evolventní ozubení dle připojeného obrázku.
Zde najdete kompletní zdrojový kód a zde spustitelný kód
Podobně jako v rovině i v prostoru je často třeba vztahovat transformace objektů nejen k počátku souřadné soustavy či souřadným osám, ale k obecným bodům a přímkám. I zde je tedy třeba transformace skládat. Situace je analogická jako v rovině, uvedu proto jen jeden příklad:
Příklad 10: Sestrojme proceduru řešící rotaci v prostoru kolem obecné přímky. Osu rotace určíme opět pomocí dvou bodů , a budeme ji chtít ztotožnit s některou souřadnou osou, dejme tomu s osou . Je zřejmé, že osu nejdříve posuneme o vektor u, čímž bod ztotožníme s počátkem. Dále je třeba osu rotace otočit o úhel kolem osy . Po této rotaci bude osa ležet zřejmě v rovině . Ke ztotožnění s osou zbývá rotace kolem osy y o úhel . Osa rota-ce je nyní ztotožněna s osou . Nyní provedeme rotaci kolem osy o daný úhel a, a inverzními transformacemi se z osy vrátíme k původní ose . Rotaci kolem osy o úhel a dostaneme složením:
Procedura, která nastavuje matici této rotace, vypadá takto:
Procedure GenRot(A,B:T3DPoint;Alfa:Double;var M:TMatrix);
var Mat1,Mat2,Mat :TMatrix;
V :TVector;
i :Integer;
Beta,Gama :Double;
begin
U[1]:=-A[1];U[2]:=-A[2];U[3]:=-A[3];
Translat(U,Mat1); {nastavení translace T(u=AO)}
Beta:=-Arctan((B[2]-A[2])/(B[1]-A[1]));
RotZ(Beta,Mat2); {nastavení rotace R(z,b)}
Multip(Mat1,Mat2,Mat); Mat1:=Mat; {složení T R}
Gama:=Arctan(sqrt(sqr(B[1]-A[1])+sqr(B[2]-A[2]))/(B[3]-A[3])); {nastavení rot. R(y, g)}
RotY(Gama,Mat2);Multip(Mat1,Mat2,Mat); {"přiložení" rotace R(y, g)}
Mat1:=Mat;RotZ(Alfa,Mat2); {nastavení rotace R(z,a)}
Multip(Mat1,Mat2,Mat);Mat1:=Mat; {"přiložení" rotace R(z,a)}
Gama:=-Gama; RotY(Gama,Mat2); {nastavení rotace R(y, -g)}
Multip(Mat1,Mat2,Mat); Mat1:=Mat; {"přiložení" rotace R(y, -g)}
Beta:=-Beta; RotZ(Beta,Mat2); {nastavení rotace R(z,-b)}
Multip(Mat1,Mat2,Mat); Mat1:=Mat; {"přiložení" rotace R(z,-b)}
U[1]:=-U[1];U[2]:=-U[2];U[3]:=-U[3];
Translat(V,Mat2); {nastavení translace T(-u)}
Multip(Mat1,Mat2,Mat); M:=Mat; {"přiložení"
translace T(-u)}
end;
Zde najdete kompletní zdrojový kód a zde spustitelný kód