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