ormai lóránt: kvaterniók és alkalmazásuk

133
Kvaterniók és alkalmazásuk Ormai Lóránt KOSSUTH KIADÓ

Upload: bogarca4455

Post on 11-Aug-2015

202 views

Category:

Documents


4 download

DESCRIPTION

A komplex számok körében a szorzás a számsíkon elforgatást eredményez. De vajon létezik-e a számfogalomnak olyan kiterjesztése, amely a térbeli forgatást számokkal való szorzássá egyszerűsíti? Sir Hamilton 170 éve talált a kvaterniókra! A kvaterniók a számfogalom további (a komplex számokon túli) kibővítését jelentik: olyan számok, amelyeknek a képzetes része nem egyetlen valós szám, hanem három, nem összevonható, valós mértékű összetevő: a háromdimenziós tér egy pontját kijelölő vektor. Azon túl, hogy a kvaterniók képzetes részével a térbeli vektorműveletek minden előnyét megnyerjük, a térbeli vektorok kvaterniókká bővítése még azt is eredményezi, hogy az elforgatások kvaternióval való szorzássá egyszerűsödnek. Ez a könyv a fentiek részletes megmagyarázásán túl a térbeli forgatások programozásának látványos eredményt nyújtó kalandjára csábít. Az újabb kiadásba bekerült a térbeli pontok helyzetének homogén koordinátákkal való leírása. Ez lehetővé teszi a három pontra illesztett sík, illetve a három sík közös pontjának az (azonos algoritmussal való) könnyű meghatározását. A homogén koordináták bevezetése egyben utat nyit egy új téma, a transzformációk egységes kezelése felé.

TRANSCRIPT

Page 1: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

Ormai Lóránt

KOSSUTH KIADÓ

Page 2: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

TARTALOM

Alapok 1

A kvaternió meghatározása 1

Kvaternió alapműveletek 2

További kvaternió-műveletek 9

Kvaterniók szorzatának abszolút értéke 12

Térbeli forgatás kvaternióval 13

Kvaterniók hármas szorzata 19

A forgató kvaternió két vektorkvaternió szorzata 21

Homogén koordináták 23

F# modul: Kvaternió_műveletek 26

Térbeli szerkesztések ( F# ) 51

Térbeli alakzatok leírása homogén koordinátákkal 57

Kvaterniók programozott megjelenítése ( F# ) 63

Hasáb forgatása (Visual Basic program) 67

Síkidom forgatása (Visual Basic program) 74

Segédmennyiségek a szabályos testek rajzolásához 78

Programozási szerkezetek a szabályos testekhez ( F# ) 82

A szabályos testek szerkesztésének programozása ( F# ) 85

Konkáv testek rajzolása 111

Elforgatott pontokból a forgatási tengely és szög meghatározása 116

Konvergencia-fraktálok 120

Összefoglalás 129

Page 3: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

1

Alapok

Elektronikus könyvként már olvasható „A komplex számok elemi alkalmazásai” című munkám. Annak

ismeretét ennek az anyagnak a megértéséhez feltételeztem.

A Wikipédia kvaterniók szócikkén kívül Tóth Szilvia Ágnes Kvaterniók című, szintén az interneten

fellelhető dolgozatát használtam fel az alábbiakhoz. Persze – a komplex számokról szóló anyaghoz

hasonlóan – ebben a leírásban is igyekeztem az ismeretterjesztés szintjén maradni.

A kvaternió meghatározása

A két összetevőből álló komplex számok (amelyeknél a szorzás a komplex számsíkon való elforgatást

eredményez) mintájára térbeli forgatásra kereste a számok kiterjesztését Hamilton, ír matematikus.

1843-ban rájött arra, hogy a négy komponensű kvaternió lehet a megoldás a problémájára. Látni

fogjuk, hogy kvaterniókkal végzett műveletek tényleg megvalósíthatnak térbeli elforgatást.

Felismerése szerint a valós 1 egységű összetevőt kiegészítő másik három (vele és egymással össze

nem vonható) összetevőnek egységére a következő összefüggéseknek kell teljesülniük:

Ebből persze további balról, illetve jobbról szorzásokkal következik, hogy:

A szorzás asszociativitását (csoportosíthatóságát) feltételeztük. Viszont látszik, hogy a szorzás nem

kommutatív (felcserélhető), hiszen a bal oldalon a tényezőket felcserélve a jobb oldal előjelet vált. A

fenti két képletsorra a továbbiakban a kvaternió egységek szorzótáblájaként hivatkozunk. (Nem

szerepel itt a valós 1 egység: hiszen bármelyik egységet akár balról, akár jobbról a valós 1 egységgel

szorozzuk, az értéke nem változik.) A valós, illetve a komplex számokhoz hasonlóan a kvaternióknál is

vizsgálhatnánk a test-axiómák teljesülését: mivel a szorzás nem kommutatív, a kvaterniók csupán

ferdetestet alkotnak.

A további képleteinkben a nem kiemelt álló betűkkel valósakat, a félkövér dőlt betűkkel

kvaterniókat (ritkán – de akkor felhívjuk rá a figyelmet – komplex számokat) jelölünk. A vektor

mennyiségeket félkövér, álló, felülhúzott betűvel különböztetjük meg. A szorzást többnyire a * jellel

nyomatékosítjuk. De felső index * jel a konjugálás jele is. (Megjegyezzük, hogy sokféle szorzást

jelölünk ugyanazzal a * jellel: skalárok – valós számok – egymással szorzását; kvaterniók egymással

szorzását; kvaterniók valóssal szorzását; vektorok valóssal szorzását. Mindig az operandusok,

amelyekkel a szorzást el kell végezni, mutatják, hogy milyen szorzás műveletről van szó. De valós

számok egymással szorzását jelöljük egymás mellé írással is.)

Page 4: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

2

A következő bekezdésben felírjuk a kvaterniók legáltalánosabb, négy összetevőből álló alakját. Most

csak megemlítjük, hogy maguk a valós számok is kvaterniók, amelyeknek a képzetes részük (a három

további összetevőjük) 0. Kvaterniók a fenti egységek is, ezeknek (mint kvaternióknak) is a

másik három összetevője 0. Természetesen, a csak valós komponenst tartalmazó kvaterniókon a

kvaternió alapműveleteket végrehajtva az eredmény összetevői közül ismét csak a valós összetevő

mértéke lesz 0 -tól eltérő. A valós egység és (például) az egység által meghatározott számsík a

szokásos komplex számsík.

A komplex számsíkhoz tartozó kvaterniókon (amelyeknek tehát a és egységhez tartozó

összetevői 0-k) végzett kvaternió alapműveletek nem vezetnek ki a komplex számsíkból (hiszen még

az kvaterniószorzat is – a maga -es eredményével – ezen a számsíkon belül marad).

De ugyanígy használhatjuk a valós egység és a egység által kifeszített számsíkot vagy a valós

egység és a egység által kifeszített számsíkot komplex számsíkként (hiszen -re és -ra

egyaránt az önmagával való szorzás eredménye).

A továbbiakban az anyagban általánosan használt és betűjelű kvaternió szerkezetét (a

komplex analógiájára az algebrai alakot) mutatjuk az összetevőik szokásos megjelölésének

bevezetésével:

A jobb oldali tömör írásmóddal azt mutatjuk, hogy a kvaternió négy dimenziós terében egy dimenziót

a valós rész -szel, további három dimenziót pedig egy, a másik három bázis egység ( egység

kvaternió, amelyeket páronként egymásra merőleges egységvektoroknak tekintünk) által kifeszített

eredővel, a képzetes rész -szel adhatunk meg. ( A képzetes rész tehát háromdimenziós vektor. )

és kvaterniók egyenlőségéhez a négy összetevő mértékének egyenlősége szükséges, azaz

Kvaternió alapműveletek

A komplex számokhoz hasonlóan a kvaterniókon is értelmezhetünk egy- és kétoperandusú

műveleteket. Az egyoperandusú műveletek közül a negálás (ellentett képzés) itt is valamennyi

összetevő előjelét ellenkezőjére váltja.

Konjugálás során pedig csak a valós rész nem vált előjelet (a képzetes rész – a vektor – előjelet vált):

A szorzási inverz (reciprok) képzésének leírására itt is (kvaterniók esetén is) később kerül sor.

Page 5: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

3

Page 6: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

4

A kétoperandusú műveletek közül az összeadást és a kivonást (a komplex számokhoz hasonlóan)

összetevőnként végezzük el. Az összeadás és a kivonás (ennek a műveletvégzési szabálynak is

köszönhetően) természetesen felcserélhető (kommutatív) és csoportosítható (asszociatív).

( ) ( )

( ) ( ) ( ) ( )

( ) ( ) ( ) ( )

A kvaterniók szorzásához kikötjük a disztributivitást is (tehát összeget tagonként szorozhatunk).

( ) ( )

A szorzásokat tagonként elvégezve (alkalmazva az első oldalon bemutatott szorzótáblát):

A szorzás eredményét kvaternió-összetevők szerint rendezve:

( )

( )

( )

Összevonhatunk tagokat úgy, hogy megfelelően csoportosítva felismerhetünk pár szerkezetet:

( ) ( )

( ) ( )

( ) ( ) ( )

tehát ( zárójelpárba zárva külön a valós, külön a képzetes részt ):

( ⟨ ⟩ ) ( )

Ebben a képletben a két kvaterniónak a három, képzetes összetevője által meghatározott vektor

skaláris szorzata ( jelölése: ⟨ ⟩ ) és vektoriális szorzata ( jelölése: ) szerepel:

Page 7: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

5

⟨ ⟩

|

|

( ) ( ) ( )

A vektorok párhuzamossága esetén: a vektorok merőlegessége esetén pedig:

⟨ ⟩ . A vektorok párhuzamossága – természetesen – akkor is teljesül, ha a kvaterniót

önmagával (vagy a konjugáltjával) szorozzuk ( és ( ) ):

Ha a kvaterniót a saját konjugáltjával szorozzuk:

( ) ( )

akkor az eredmény valós, hiszen az alábbi szorzási eredmény zárójelezett, második része :

( )

Így megkaptuk a kvaternió abszolút értékének (normájának) a négyzetét:

| |

Tehát a norma, a kvaternió abszolút értéke:

| | √ √

.

Csak megjegyezzük, hogy egy kvaterniónak és a konjugáltjának az összege is valós:

( ) ( )

A valós számok ( ) tekinthetők olyan kvaternióknak, amelyeknek a képzetes része :

Olyan kvaterniók szorzata, amelyeknek csak valós részük van (a képzetes részük ): a valós részek

szorzata. Ha , illetve , akkor

Ha a kvaterniók valós része 0 ( , illetve ), akkor ⟨ ⟩

Vagyis tisztán képzetes kvaterniók esetén a kvaternió szorzat a vektorok skaláris és vektoriális

szorzatából áll elő, méghozzá úgy, hogy a szorzat kvaternió valós része a vektorok skaláris

szorzatának ellentettje, míg a szorzat kvaternió képzetes része a vektorok vektoriális szorzata.

(A fenti műveleteknél láthattuk, hogy a szorzásoknál csak a képzetes kvaternió egységek: és

egymás közötti sorrendjére kellett ügyelnünk, hiszen ezek egymással nem felcserélhetőek. Viszont a

kvaterniók összetevőinek mértékeit, az egységek mellé írt valós számokat a szorzatok elejére

kigyűjthetjük, egymással összeszorozhatjuk.)

Page 8: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

6

A továbbiakban kvaterniók összeszorzásánál, például a ( ) ( )

szorzat meghatározásánál kísértést érzünk arra, hogy azt mondjuk, kéttagút kéttagúval kell szorozni,

ebben semmi különleges nincs, kapunk négy szorzatot, azokat összeadjuk. Azaz:

( ) ( )

Eddig minden rendben van, igaz. De itt emlékeztetnénk arra, hogy a jel kvaternió-szorzás

műveletet jelent! A kvaternió-szorzás nem különbözik a szokott szorzásoktól mindaddig, amíg két

valós számot kell összeszorozni ( ) illetve, amikor ( akár kvaternió egységek

lineáris kombinációjaként felépített ) vektort kell skalárral, egy valós számmal szorozni: ilyenkor

ugyanis mindegyik vektor-összetevő mértékét megszorozzuk a skalárral, a valós számmal. Ez a

helyzet a illetve a esetben.

Más a helyzet a tagnál. Figyelem! Ez a művelet nem két vektor skaláris szorzata, de nem

is két vektor vektoriális szorzata! Itt a két vektor kvaternió-szorzatáról van szó! A kvaternió-szorzat

kiszámításánál pedig a kvaternió-szorzótábla ( #Szorzótábla ) szerint kell eljárnunk. Vagyis:

( ) ( )

⟨ ⟩

A bázis kvaternió-egységekből alkotott vektorok kvaternió-szorzata egy kvaternió, amelynek valós

része a vektorok skaláris szorzatának ellentettje, képzetes része pedig a vektorok vektoriális szorzata.

Oka ennek a kvaterniók szorzótáblája: a három tagból álló vektorok tagonkénti összeszorzásánál két

különböző bázisegység összeszorzásakor az eredmény (#Jobbsodrású_rendszer) a kettőre merőleges

harmadik bázis egység (vagy ellentettje); két megegyező bázis egység szorzata pedig a valós .

(Korábban sem tettük, a továbbiakban sem fogjuk, csupán ezen az oldalon különböztettük meg a

kvaternió-szorzást a többi szorzástól azzal, hogy a szorzási műveleti jelet piros csillagként írtuk. A

kvaternió egységekkel felépített vektorok elrejtik a vektor kvaternió voltát, erre mutattunk itt rá.)

Page 9: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

7

A kvaterniók egység eleme (a szorzási egység) az , azaz a valós 1 szám.

Kvaterniók hányadosának előállításához nézzük a reciprok (szorzási inverz) meghatározását! Ehhez

először belátjuk, hogy ( ) hiszen ( ) ( ) -nek

teljesülnie kell. De a szorzás asszociativitása (csoportosíthatósága) miatt ez a

( ) ( ) ( )

végrehajtás esetén teljesíthető.

Tegyük fel, hogy kvaternió abszolút értéke nem 0, azaz nem a nulla kvaternió ( ), amely minden

összetevőjének mértéke 0. (Abban az esetben ugyanis nem létezik a reciprok.)

| |

Ennek a kvaterniónak a reciprokát keressük! Ehhez balról egy egységnyi szorzattal bővítjük, majd az

asszociativitás (csoportosíthatóság) miatt a tényezőket másképp zárójelezzük:

( ) (

) ( )

| |

(Kihasználtuk, hogy a kiindulási feltétel szerint.) A reciprok tehát a konjugált osztva az

abszolút érték négyzetével. Látható, hogy egység kvaternió (1 abszolút értékű kvaternió) reciproka a

konjugáltja.

Az osztás műveletét ezek után úgy definiálhatjuk, hogy az osztandót az osztó reciprokával jobbról

szorozzuk (ha az osztó abszolút értéke nem nulla):

| |

| | | |

Tehát az osztó konjugáltjával (jobbról) való szorzás eredményét az osztó abszolút értéke négyzetével

kell elosztani (pontosan úgy, mint a komplex osztás esetén). Nullával nem lehet osztani.

Ha | | , akkor a -vel megegyező irányú egység kvaternió:

| |

| |

| |

| |

| |

Ha a kvaterniót a valós rész és a háromdimenziós képzetes vektor összegeként nézzük, azaz:

| | (

| |

| | )

akkor – mivel a zárójelen belüli rész egység kvaternió –

(

| | )

(| |

| | )

( )

ezért található olyan szög, amelyre

Page 10: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

8

| |

| |

| | √

ezzel a kvaternió trigonometrikus alakját felírhatjuk:

| | (

| | ) | | (

)

Itt tehát szerepel egy egységvektor, és – ha az abszolút értékkel nem szorozzuk – egy egység

kvaternió. Az egység kvaternió trigonometrikus alakja:

A kvaterniók trigonometrikus alakjához másképp is eljuthatunk. (A továbbiakban a tisztán képzetes

kvaterniókat vektorkvaternióknak is nevezzük, hiszen ezek vektorok, amelyekre kvaternióműveletek

értelmezettek.) Képezzük két vektorkvaternió szorzatát ( #Tisztán_képzetesek_szorzata ):

( ) ( ) ⟨ ⟩

Ha a és a vektor által közrezárt szög akkor a skaláris és a vektoriális szorzatukat átírhatjuk:

⟨ ⟩ | | | | | | | |

Ugyanis két vektor skaláris szorzata egy valós szám: a két vektor abszolút értékének és a közrezárt

szögük koszinuszának a szorzata. Két vektor vektoriális szorzata vektor, amelynek iránya mindkét

szorzótényezőre merőleges, nagysága (abszolút értéke) pedig a két vektor abszolút értékének és a

közrezárt szög (ez a szög az óramutató járásával ellentétesen pozitív) szinuszának a szorzata.

Végezzük el a lehetséges átalakításokat! Kihasználva, hogy kvaterniószorzat abszolút értéke a

tényezők abszolút értékének szorzata ( #Szorzat_abszolút_értéke ); a tisztán képzetes kvaternió

abszolút értéke megegyezik vektorának abszolút értékével; végül vektorok vektoriális szorzata

mindkét szorzótényezőre merőleges és – ebben a sorrendben: – a jobb kéz

hüvelyk-, mutató- és középső ujja által formálthoz hasonló, jobbsodrású rendszert alkot:

| | | | | |

| | ( ) ( )

Ha a és szöge, akkor és szöge Teljesül rá, hogy:

Ez a vektorkvaterniók szorzatából előállított egységkvaternió trigonometrikus alakja. Összefoglalva:

| |

| |

| | ( ) ( )

Létezik olyan szög, amelyre ( ) ( )

Page 11: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

9

További kvaternió-műveletek

Most kvaterniókkal olyan műveleteket mutatunk, amelyekre nem lesz feltétlenül szükségünk a

továbbiakban. (Ezeknek az összefüggéseknek a levezetését nem a használhatóságuk indokolja

minden esetben. Többször csupán azt szeretnénk megmutatni, hogy újabb és régebbi matematikai

mennyiségeink között különböző módon teremthetünk kapcsolatot.)

Például a szorzatban ( #Kvaterniók_szorzási_szabálya ) szerepelő vektoriális szorzat rész

kiszámítására vezetünk le egy kvaternió-összefüggést. Felírjuk a és szorzatot:

( ⟨ ⟩ ) ( )

( ⟨ ⟩ ) ( )

Itt is rámutatunk arra, hogy ha fennállna a szorzás kommutativitása (a tényezők felcserélhetősége), a

fenti két szorzat jobb oldala egymással megegyezne, ahogyan legtöbb tagjában meg is egyezik.

Így viszont a fentiek különbsége (kihasználva, hogy a valósok szorzata és a skaláris szorzat a tényezők

felcserélésével nem változik, a vektoriális szorzat viszont előjelet vált):

( )

A kvaternió-szorzat a tényezők felcserélésekor a vektoriális szorzatrész duplájával módosul. Tehát a

kvaternióknak a (valós részen kívüli) vektor összetevőik vektoriális szorzata így is számolható:

Két kvaternió négydimenziós skaláris szorzata: ( ) illetve ( ) ugyanis:

( ) ( )

a szorzatból csak a valós részt meghatározva ( függvény a kvaternió valós részét adja vissza ):

( )

Persze, ugyanezt kapjuk ( ) kiszámításakor is.

Kvaterniók összetevői az alábbi képletek szerint választhatók le ( kvaternión bemutatva ):

( ) ( ) ( ) ( )

De a konjugált hozzáadásával is leválaszthatjuk az egyes összetevők mértékét:

Page 12: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

10

A konjugálást a kvaternión végzett egyszerű algebrai műveletekkel is leírhatjuk. Ehhez a

Összeadva a jobb oldali egyenleteket látszik, hogy:

A komplex számok olyan kvaternióknak is tekinthetők, amelynél a és bázishoz tartozó

összetevő 0.

a kvaternió-szorzatot képezve láthatjuk, hogy az összetevőkkel kifejezve éppen a komplex szorzás

eredményét adja:

( ) ( )

( )

Hasonlóképpen a továbbiakban is látni fogjuk, hogy a kvaterniókkal és a komplex számokkal

kapcsolatos összefüggéseink (formálisan) megegyeznek.

Kvaterniók négyzetgyökét is kiszámolhatjuk. Az összefüggések levezetéséhez feltételezzük, hogy

kvaternió a kvaternió négyzetgyöke. Tehát:

A korábbiakban ( #Kvaternió_négyzete ) meghatároztuk -t, eszerint:

Page 13: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

11

Ebből a összetevőire vonatkozólag a következő egyenleteket nyerjük:

Az első egyenletet -nel szorozva és 0-ra rendezve:

majd behelyettesítve a fenti, további összetevőkre vonatkozó összefüggéseket:

√ (

)

| |

√ | |

A két kvaternió-négyzetgyök egymás ellentettje, hiszen mindegyik összetevő mértéke előjelet vált. A

másodfokú megoldó képlet gyökjele előtt (a norma előjeleként) csak a pozitív előjelet alkalmazzuk,

mivel p0 -ra csak valós számot keresünk.

Erről persze eszünkbe juthat, hogy ugyanígy kellene a komplex gyökvonást is megvalósítani! Tehát:

Ha c és d ismert, határozzuk meg a-t és b-t!

ez utóbbiból:

| |

| |

Azaz a kvaterniókra kapott képletek pontos megfelelőihez jutottunk. Megjegyezzük, hogy ’a’ valós,

ezért a gyökjel alatt csak a + előjel elfogadható. Így a végső képletek:

√ √ | |

A két komplex négyzetgyök éppen egymás ellentettje (ahogyan azt más módon is megkaptuk).

Mindenesetre sokkal egyszerűbb így, mint a komplex szám trigonometrikus alakjából kiindulva!

Page 14: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

12

Kvaterniók szorzatának abszolút értéke

Szorzat abszolút értéke az abszolút értékek szorzata. Ugyanis, ha a két kvaternió:

akkor abszolút értékük négyzete:

| |

| |

Ezek szorzata:

(

) (

)

Ugyanakkor a szorzat abszolút értékének négyzete a szorzat összetevőinek négyzetösszege:

( )

( )

( )

( )

Itt a kétszeres szorzatok színezésével kívántuk beláttatni, hogy ezek páronként kiesnek. Csak a

négyzetes tagok maradnak meg, amelyek viszont – a fentivel összehasonlítva jól látható –

megegyeznek a tényezők abszolút értékének szorzatánál kiszámított eredménnyel.

Page 15: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

13

Az abszolút értékekre vonatkozó szorzási szabályból levonható az a következtetés, hogy ha az alábbi

képletekben mindegyik betű nemnegatív, egész számot jelöl, akkor:

( ) ( )

( ) ( ) ( ) ( )

Tehát: ha két pozitív egész szám egyike és másika is négy nemnegatív, egész szám négyzetének

összege, akkor a két természetes szám szorzata is négy négyzetszám összege. (Mivel bebizonyítható,

hogy minden prímszám felírható – nem feltétlenül egyértelműen – négy nemnegatív, egész szám

négyzetének összegeként, ezért a fenti tétel következménye az, hogy minden természetes szám négy

nemnegatív, egész szám négyzetének összegeként írható fel: ez Lagrange négy négyzetszám tétele.

Akit a tétel teljes bizonyítása érdekel, az a http://tassyg.web.elte.hu/2005-2006-2/szamelmelet3.pdf

anyagban keresse a következőt: 4 négyzetszám.)

Térbeli forgatás kvaternióval

Egy adott egység komplex számmal való szorzást a komplex számsík egy alakzatának valamennyi

pontjára alkalmazva az alakzat (torzításmentes) elforgatását eredményezi.

A kvaternióval szorzás a disztributivitásból következően lineáris transzformáció, ezért remélhetjük

hogy eredményre jutunk amikor azt keressük, hogy milyen kvaternió-szorzással (szorzásokkal) tudjuk

a térbeli alakzatokat torzításmentesen elforgatni. Viszont problémánk, hogy a kvaterniók szorzása

nem kommutatív: más eredményt kapunk, ha egy kvaternióval balról, mást, ha jobbról szorzunk meg

egy másikat.

Gondolatmenetünk a következő: feltételezzük, hogy az elforgatást végre tudjuk hajtani, és a

tisztán képzetes kvaterniót elforgatva kvaternió volna az eredmény. Az ilyen tisztán képzetes

kvaterniók képzetes része által meghatározott háromdimenziós térben kifeszített kocka forgatás

révén ugyanannak a térnek egy másik, hozzá hasonló kockájába menne át.

A tényleges forgatás végrehajtásába úgy fogunk, hogy a kocka mindegyik sarokponti kvaternióját

egy kvaternióval balról megszorozzuk. Viszont az így nyert új kvaterniók által kifeszített alakzatot

nem kockának, hanem ferde hasábnak tapasztaljuk. (Ráadásul ennek a ferde hasábnak a csúcsai nem

tisztán képzetesek, a hasáb „kilóg” a képzetes, három dimenziós térből.)

Hasonlóképpen: ha a -vel jelölt sarokpontú kocka mindegyik sarokponti kvaternióját ugyanazon

kvaternióval jobbról szorozzuk, akkor az így nyert új kvaterniók nem lesznek tisztán képzetesek, és

az ezen kvaterniók által kifeszített alakzat ugyancsak nem kocka, hanem ferde hasáb lesz.

A ferde hasábot az elforgatási kísérletünkben olyan közbenső állapotnak tekintjük, amely a balról és

jobbról szorzás során a kockából kockába való átmenet során előáll.

Page 16: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

14

Ekkor a ferde hasáb –nek, illetve –nek megfelelő pontjára igaz az, hogy ez a sarokpont

kvaternióval balról szorzottja, illetve ez a sarokpont kvaternióval jobbról szorzottja:

Mindkét oldalt jobbról –nel szorozva kifejezhetjük az „elforgatott” kvaterniót:

A fentiekben vázolt gondolatmenetnek megfelelően egy tisztán képzetes kvaternió elforgatásánál

balról egy kvaternióval, jobbról ugyanennek a kvaterniónak a reciprokával kell szorozni.

Egység kvaternió esetén viszont a reciprok a konjugálttal azonos. Ezért az egyszerűbb számolás

érdekében a továbbiakban a fenti transzformációnál kvaternió gyanánt mindig egység

kvaterniót alkalmazunk. (Az egység kvaternió – láttuk – így írható: )

Megjegyezzük, hogy –t a kvaternió szögének, –t pedig a kvaternió tengelyének nevezzük. A

( )

alakban felírható tisztán képzetes kvaterniók vektorán az alábbi kétoldali szorzási művelet

tengely körüli szöggel való elforgatást jelent (forgatási transzformáció).

Page 17: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

15

Szigorúan véve ezt az állítást nem bizonyítjuk, de több lépésben megmutatjuk, hogy tapasztalataink

alátámasztják a helyességét.

Nézzük meg először, mit eredményez, ha a fenti műveletet az tengely irányába mutató

( ), tisztán képzetes kvaternióra alkalmazzuk ( #Kvaterniók_szorzási_szabálya ):

( ) ( ) ( )

( ) ( )

Vagyis – ahogyan azt forgatás esetén várjuk is – a tengelyre eső pontok nem változnak.

Nézzünk két, azonos ( ) tengelyű, egység kvaternió egymás utáni

alkalmazásával megvalósított elforgatást:

( ) ( ) ( ) ( )

A #Kvaterniók_szorzási_szabálya ( ( ⟨ ⟩ ) ( ) )

a fenti, bal oldali két tényező szorzatára alkalmazva:

( ) ( )

( ) ( )

Hasonlóképpen a jobb oldali két tényező szorzatára alkalmazva:

( ) ( )

( ) ( )

Vagyis az egymás után alkalmazott két elforgatás egyetlen, szögű elforgatást jelent:

( ) ( ) ( ) ( )

Vagy legyen a forgató kvaternió tengelye éppen a kvaternió szöge ⁄ , akkor a

forgató kvaternió:

( )

(Joggal jegyezhetik meg, hogy az egység kvaternió képletében – – a

képzetes rész mértéke mellett egy egységvektor szerepel, a fenti képletben pedig a egység

kvaternió. Ezt úgy kell érteni, hogy vektor.)

A forgatást az egység által meghatározott kvaternión végezzük el (a szorzótábla szerint):

( )

( )

( ) ( )

Valóban: a tengely körül ⁄ ⁄ szögű forgatás -t -be viszi át.

Page 18: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

16

Általánosan alkalmazzuk a tisztán képzetes kvaternióra (azaz kvaternióra ) az

egység kvaternióval való forgatást – amely révén a kvaternióba megy át! Tehát a forgatás

tengelye: a forgatás szöge azaz a forgató kvaternió:

( ) ( ) ( )

A bal oldali kvaternió-szorzást végrehajtva ( #Kvaterniók_szorzási_szabálya ):

⟨ ⟩ ( ) ( )

Az újabb kvaternió-szorzás eredménye:

⟨ ⟩ ⟨ ⟩ ⟨ ( ) ⟩

⟨ ⟩ ( ) ( )

( ( ) )

Az ⟨( ) ⟩ vegyesszorzat ( a három vektor által kifeszített parallelepipedon térfogata ) 0,

mert két vektor azonos benne. Az ( ) szorzatra pedig, amely éppen a vektor

egységvektorra merőleges összetevője, a kifejtési tételt ( http://ttmk.nyme.hu/mtmi/Documents/

Vektorok és gömbi geometria végső.pdf ) alkalmazzuk:

( ) ⟨ ⟩ ⟨ ⟩

(A levonandó vektor -vel párhuzamos összetevője.) Ezeket behelyettesítve:

⟨ ⟩ ( )

⟨ ⟩ ⟨ ⟩

( ) ⟨ ⟩ ( )

Ez az összefüggés a tisztán képzetes kvaterniónak az egység kvaternióval végzett elforgatására

egyszerű számítási lehetőséget nyújt.

Látható a fenti összefüggésből: egyrészt, hogy az elforgatott kvaternió is tisztán képzetes; másrészt,

hogy a második tag kiesik, ha vektor -re merőleges, a harmadik tag pedig akkor , ha

vektor az elforgatás tengelyével párhuzamos.

Az elforgatás szöge ezért is célszerű a fenti egyenletbe a kétszeres szögekre vonatkozó

trigonometriai összefüggéseket beírni:

( ) ⟨ ⟩ ( )

A kvaterniónak megfelelő vektor egy irányú, egy irányú ( ⟨ ⟩ éppen

vektornak az irányú összetevője) és egy mindkettőre merőleges ( ) összetevőből áll.

Page 19: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

17

Jobban megérthető ennek a képletnek a jelentése is, ha bevezetjük a felbontást,

ahol a forgatási tengellyel ( ) párhuzamos, pedig az arra merőleges összetevőt jelenti.

( ) ( ) ( )

amelyben figyelembevételvel és némi átrendezéssel világos képet kapunk:

( )

A párhuzamos összetevő változatlan marad, a merőleges összetevő szöggel elfordul.

Például a korábbi forgatási példánkban ⁄ Akkor

(

) ⟨ ⟩

( )

Ha vektor -re merőleges, akkor (ahogyan fél oldallal korábban is kiadódott):

( )

ahol a jobb oldali második tagban szerepelő vektor ( ) abszolút értéke megegyezik | | –kel,

iránya pedig az –re merőleges síkban van és -re merőleges. két komponense éppen a

szöggel elforgatott vektort határozza meg. (Az ábra ezt az esetet mutatja.)

Page 20: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

18

Ha vektor -vel párhuzamos, akkor (a képlet utolsó előtti változatából elhagyva a vektoriális

szorzatos tagot, amely ekkor természetesen ):

( ) ⟨ ⟩

Ha vektor – vel párhuzamos, akkor ⟨ ⟩ | | | | Így behelyettesítve:

( )

Tehát az – vel párhuzamos vektorok nem változnak.

Nem hagyott nyugodni a kérdés: mi van akkor, ha a forgatási transzformáció alá vetett kvaternió

nem tisztán képzetes, azaz alakú! (Magyarán: több pontból álló alakzat

esetén a sarokpontokhoz tartozó kvaterniók valós részét nullázni kell-e azelőtt, mielőtt a szorzási

transzformációt alkalmaznánk, mert különben az alakzat az elforgatás következtében eltorzul?)

Alkalmazzuk kvaternióra az egység kvaternióval való ( az tengely körüli

szögű ) forgatást – amely révén a kvaternióba megy át! Képletünk kissé módosul:

( ) ( ) ( )

A bal oldali kvaternió-szorzást végrehajtva (a jobb oldali szorzást továbbra is csak jelöljük):

⟨ ⟩ ( )

( )

Most a jobb oldali kvaternió-szorzást ( #Kvaterniók_szorzási_szabálya ) is elvégezzük:

⟨ ⟩

⟨ ⟩ ⟨ ⟩ ⟨( ) ⟩

⟨ ⟩

( )

( ) ( ) (( ) )

Az ⟨( ) ⟩ vegyesszorzat ismét 0 ( mert két vektor azonos ). Az ( ) szorzat a

kifejtési tétel szerint ismét: ( ) ⟨ ⟩ ⟨ ⟩ ⟨ ⟩

A skaláris szorzatoknál nyilván: ⟨ ⟩ ⟨ ⟩ ezért két egymás melletti tag kiesik. A vektoriális

szorzatoknál pedig a tényezők felcserélése előjelváltást eredményez, ezért két tagot összevonunk:

Page 21: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

19

⟨ ⟩

( ) ⟨ ⟩ ⟨ ⟩

Végül láthatjuk, hogy csak annyi a változás, hogy hiszen

( ) ⟨ ⟩ ( )

Tehát a kvaternió valós részére az elforgatás nem hat, és a valós rész értékétől függően nem

változik az elforgatott kvaternió képzetes része ( #Tisztán_képzetes_forgatása ). A valós rész egy-

egy értékéhez tartozó háromdimenziós térből a forgatási transzformáció sem vezet ki. ( Tudjuk,

hogy a vektorműveletek nem vezetnek ki, hiszen azoknak nincs közük a kvaternió valós részéhez. )

Kvaterniók hármas szorzata

Ez a fejezet tulajdonképpen nem nyújt új ismereteket, ezért annak, aki gyorsan kíván informálódni a

kvaterniókról, aki ennek az anyagnak a lényegét kívánja csak áttekinteni, nem kell vele foglalkoznia.

Annak, hogy mégis bekerült ez a fejezet is az anyagba, az az oka, hogy bizonyítsuk: igenis van értelme

a levezetéseknek akkor is, ha a számítógépes kiértékeléseket nem az ilyen módon tömörített, és

áttekinthetővé tett képletekkel végezzük! Egyrészt így ismételten meggyőződhetünk levezetett

eredményeink helyességéről; másrészt aki ilyen levezetéseket nyomon követ, jártasságra tesz szert,

és könnyebben tud újabb, hasonló összefüggésekre bukkanni.

Legyen három kvaternió, a szokott és

Az első kettő összeszorzását nem kell újra elvégeznünk, elég a #Kvaterniók_szorzási_szabálya

alapján szorzatukat idemásolnunk (a harmadik szorzás még csak kijelölve):

( ⟨ ⟩ ) ( )

A kijelölt kvaterniószorzást is ugyanezen szabály alapján hajtjuk végre:

⟨ ⟩ ⟨ ⟩ ⟨ ⟩

⟨ ⟩ ( )

( ) ( ) ( )

vegyesszorzat a három benne szereplő vektor által kifeszített parallelepipedon térfogata. Ez

0, ha a három vektor közül legalább kettő párhuzamos.

Page 22: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

20

( ) szorzatra ismét a kifejtési tételt alkalmazzuk:

( ) ⟨ ⟩ ⟨ ⟩

A kifejtési tétel szerint feloldjuk a a kétszeres vektorszorzatot:

⟨ ⟩ ⟨ ⟩ ⟨ ⟩

⟨ ⟩

( ) ( ) ( ) ⟨ ⟩ ⟨ ⟩

Kicsit átcsoportosítva ezt tekinthetjük a háromtényezős kvaterniószorzat képletének:

⟨ ⟩ ⟨ ⟩ ⟨ ⟩

( ⟨ ⟩) ( ⟨ ⟩) ( ⟨ ⟩)

( ) ( ) ( )

Alkalmazzuk ezt a képletet arra az esetre, amikor Ekkor ; .

( | | )

( | | ) ⟨ ⟩

( ) ( ) ( )

A vegyesszorzat és az utolsó vektorszorzat nyilvánvalóan , tehát a (forgatási transzformációra

emlékeztető) hármas kvaterniószorzat:

( | | )

⟨ ⟩ ( | | ) ( )

A szorzat kvaternió valós része , megszorozva | | –tel, a kvaternió abszolút értéke

négyzetével; képzetes része , irányú, és e kettőre merőleges ( ) összetevőből áll.

Az általunk alkalmazott forgató kvaterniókat egységnyi abszolút értékűnek választottuk, amelyek

trigonometrikus alakja: ahol egységvektor a forgatás tengelyének

irányát jelöli ki (a tengely mindig átmegy az origón), pedig a forgatás szögének fele.

Ha ezt a kvaternió helyett bevezetjük, akkor láthatjuk, hogy | | hiszen ez

éppen a kvaternió abszolút értékének négyzete. Másrészt .

Page 23: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

21

Ezeket behelyettesítve:

⟨ ⟩ ( ) ( )

Ismét a korábbi eredményünket kaptuk (természetesen).

Ismét leírjuk a képletet a kétszeres szögekkel is:

( ) ⟨ ⟩ ( )

A forgató kvaternió két vektorkvaternió szorzata

A kvaterniók trigonometrikus alakjának második megvilágításánál ( #Vektorkvaterniók_szorzata )

forgató kvaterniót ( #Forgató_kvaternió ) állítottunk elő, amely a két vektorra merőleges tengely

( ) körül ( ) szöggel ( ) forgat:

( ) ( ) ( ) ( )

Ugyanerre az eredményre jutunk, ha először a vektorból előállított vektorkvaternióval,

majd a vektorból képezett tisztán képzetes egységnyi vektorkvaternióval forgatunk, hiszen:

( )

( ) ( )

| | (

| |)

A fenti képletben egy tetszőleges kvaternió, erre alkalmazzuk az elforgatást. A forgató kvaternió

pedig két (egységnyi abszolút értékű) vektorkvaternió szorzata. A két vektor egymáshoz viszonyított

hajlásszöge a forgatási szög fele. A két vektorkvaternió összeszorzásakor előáll a forgató kvaternió

szokott alakja: a forgató kvaternió valós része a forgatási szög felének koszinusza, a forgató kvaternió

képzetes része a forgatási szög felének szinuszával szorozva egy – mindkét vektorra merőleges –

egységvektor ( ), amely (tehát a két vektor vektoriális szorzatának egységvektora).

A forgatás a két vektorkvaternióval egymás után végrehajtott forgatási transzformációként is

megvalósítható. Tehát a forgató kvaterniót nem feltétlenül szükséges a fenti (explicit) alakjában

meghatározni, a forgatás a két vektor ismeretében már végrehajtható. Csak megjegyezzük, hogy

minden vektorkvaternió –vel forgató, azaz tengelyesen tükröző forgató kvaternió. Meg is felel a

forgató kvaterniók szokásos felírásának:

(

) (

) hiszen (

) (

)

Tehát két vektorkvaternióval egymás után végrehajthatunk két forgatási transzformációt. Az első

transzformáció tükrözést jelent az első vektor által (origón átmenően felvett) tengelyre. Ugyanígy, a

második forgatási transzformáció is tükrözést eredményez a második vektor által (ugyancsak origón

átmenően felvett) tengelyre. Az eredmény az eredeti alakzat olyan elforgatása, amely a két vektor

Page 24: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

22

eredője által meghatározott tengely körül történik, és szöge a két vektor szögének duplája.

A vektoriális szorzásnál a tényezőket felcserélve a szorzat előjelet vált. Ez az alapja annak, hogy a

forgató kvaterniót létrehozó vektorvaterniók alkalmazásának sorrendjét megcserélve a forgatás

iránya ellenkezőjére változik.

Az ábrán piros drótvázú a kiindulási kocka. Ha ezt először az által meghatározott tengelyre majd

az által kijelölt tengelyre tükrözzük, a zöldes színezésű kockákhoz jutunk. Ugyanaz a végállapot

(narancs színezés), ha a két vektorkvaterniót összeszorozzuk ( ) és az vektorral

párhuzamos tengely körül forgatunk. Ha viszont először az tengelyirányra tükrözünk, akkor

kiindulásul a püspöklila kockához jutunk. Ezt az által meghatározott tengelyre tovább tükrözve

az előbbitől eltérő a végállapot, viszont ugyanoda (türkiz szín) jutunk, ha a két vektorkvaterniót

Page 25: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

23

fordított sorrendben összeszorozzuk ( ) és az vektorral párhuzamos tengely körül

forgatunk. (A példában a két vektor hajlásszöge 60°, az eredő vektor körüli elforgatás szöge 120°.)

A két végállapot (a türkiz, illetve narancs színnel színezett) a kiindulási állapotból egyazon, origón

átmenő forgatási tengely körül végzett ellenkező irányú forgatással áll elő (hiszen csupán a vektorok

sorrendjét cseréltük meg, ezért a forgatási tengelyt a két esetben egymással párhuzamos, de

ellentétes irányú vektor jelöli ki).

Homogén koordináták

A kvaterniót a háromdimenziós tér homogén koordinátás leképezésénél is használhatjuk. A három-

dimenziós tér [ x, y, z ] koordinátájú pontjához ugyanis az [ 1, x, y, z ] homogén koordinátákat

rendelhetjük. ( Ugyanehhez a ponthoz tartozik a [ λ, λ * x, λ * y, λ * z ] koordinátanégyes is,

ahol λ tetszőleges valós szám. A homogén koordináták az arányosság erejéig meghatározottak.) A

kvaternió azért célszerű eszköz a háromdimenziós tér homogén koordinátás leírására, mert éppen

négy valós koordinátaértéket tárol.

Megjegyzés: A kiegészítő homogén koordinátát a térkoordináták mögé szokták negyedikként beírni.

Mivel mi a kvaterniók valós összetevőjét, amelyet a kvaternióban első összetevőként szerepeltetünk,

kívánjuk erre a célra felhasználni, ebben az anyagban a kiegészítő koordinátát írjuk az első helyre.

Az interneten bőségesen találhatnak anyagokat a „homogén koordináták” kifejezésre kerestetve. De

tanulmányozhatják a http://www.agt.bme.hu/szakm/szg/homogen.htm anyagot is, amelyet a téma

kifejtésénél leginkább követtem.

Kiindulásképpen a kétdimenziós esetet tekintjük át. A síkbeli kép pontjai két koordinátájának a

pontokon és a vetítési középponton áthaladó sugárra illeszkedő térbeli pontok homogén koordinátái

valamennyien megfelelnek. (A mélységi koordináta elveszítésével áll elő a kép az ernyőn.)

Elméleti oldalról nézzük a síkbeli koordinátapárról a homogén koordinátákra való áttérés menetét!

A koordináták számát eggyel megnöveljük. Közönséges (véges) pont esetén a kiegészítő koordináta

értékéül egy nullától eltérő számot adunk meg. Az 1 számértékű koordinátával való kiegészítés azzal

az előnnyel jár, hogy ekkor a másik két koordináta számértékét nem kell megváltoztatni. A kiegészítő

koordináta értéke 0 is lehet, de akkor a homogén koordinátahármas egy ideális pontot jelöl ki a

síkon, amely tulajdonképpen a maradék két koordináta által meghatározott irány. (Az ideális pontok

a végtelen távoli pontok. Párhuzamos egyenesek sokaságához ugyanaz az ideális pont tartozik. Ezért

is mondják, hogy az ideális pontok mindegyike valójában egy irány.) Mind a három koordináta

egyszerre 0 nem lehet: ilyen érvényes homogén koordináta nincs.

A síkbeli egyenes megszokott egyenletét – formára

átírva láthatjuk, hogy az egyenes egyenlete ⟨ ⟩ (skaláris szorzat) alakban is írható, ahol

az együtthatók vektora, pedig az egyenes pontjait kijelölő

Page 26: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

24

(homogén) koordinátavektor. Az egyenes együtthatóvektorának és a pont koordináta-vektorának a

skaláris szorzata (félsík-függően előjelezve) arányos a pontnak az egyenestől mért távolságával, és 0

abban az esetben, ha a pont az egyenesre illeszkedik. (A fenti átalakítással az egyenes egyenletéhez

tartozó együtthatóvektor az egyenes normálisa: az egyenes – homogén koordinátavektorukkal adott

– pontjai erre merőlegesek.)

Az egyenesnek a fenti, 0-ra rendezett egyenletéből nyilvánvalóan látszik, hogy ha vektor megadott

értéke kielégíti az egyenletet, akkor bármely valós számmal szorzott értéke is megfelel. Innen a név:

a homogén koordinátáknak ez az arányosság erejéig való meghatározottsága a homogenitás.

Ha az egyenesnek két, egymástól különböző pontját, az és

pontot nézzük, akkor a ⟨ ⟩ és ⟨ ⟩ egyenletnek is teljesülnie kell, vagyis

. (Ha egy vektor két – egymással nem párhuzamos – vektorra merőleges, akkor a két

vektor vektoriális szorzatával párhuzamos.) Ekkor pedig az együtthatókat a vektoriális szorzat első

sorában felsorolt egységvektorok együtthatóiként (az ezekhez a pontokhoz tartozó előjeles

aldeterminánsokként) számíthatjuk, ugyanis párhuzamos vektorok együtthatói csak egy közös

szorzóban különbözhetnek:

|

|

( )

Most nézzük a hasonló műveleteket a háromdimenziós környezetben! A háromdimenziós tér

közönséges pontjaihoz hozzárendelt homogén koordináták négy összetevőből állnak, amelyek közül

három a korábbi, háromdimenziós térbeli koordináta, a negyedik számértéke pedig 1 (bár mind a

négyet tetszőleges számmal szorozhatjuk, vagy tetszőleges számmal el is oszthatjuk). Természetesen

a térbeli esetben is létezik olyan homogén koordináta, amelynél a negyedik koordinátaérték 0, ez

ebben az esetben is egy végtelen távoli, ideális pontot jelent, amely valójában a másik három

koordináta mint vektor által meghatározott irány. (Tulajdonképpen a kvaterniókkal ezt pontosan így

érthetnénk: ha a valós rész 0, a képzetes rész vektora által meghatározott irányt jelenti a kvaternió.)

Nem létezik négy 0 értékkel leírt homogén koordináta.

A háromdimenziós tér síkjának egyenletét a következő képlettel adhatjuk meg:

A sík egyenlete ⟨ ⟩ alakban is írható, ahol az együtthatók vektora,

pedig a sík pontjait kijelölő (homogén) koordinátavektor. A sík fenti

egyenletéből következően a sík együtthatóvektorának és egy pont koordinátavektorának a skaláris

szorzata 0 abban az esetben, ha a pont a síkra illeszkedik. (A sík egyenletéhez tartozó együttható-

vektor a sík négydimenziós normálisa: a sík pontjai – homogén koordinátájukkal – erre merőlegesek.)

Ha a síknak három pontját, az a és a

pontot nézzük, akkor a ⟨ ⟩ a ⟨ ⟩ és a

Page 27: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

25

⟨ ⟩ egyenletnek is teljesülnie kell, vagyis ( ). (Itt az ( )

jelölés a három vektor négydimenziós vektoriális szorzatára vonatkozik, amely a három felhasznált

vektor irányára merőleges.) A kétdimenziós vektoriális szorzat mintájára ennek determinánsa:

( ) |

|

Az együtthatókat a determináns első sorában felsorolt egységvektorok együtthatóiként számíthatjuk,

az ezekhez tartozó előjeles aldeterminánsok az (arányosság erejéig meghatározott) együtthatók:

( ) ( ) ( )

( ) ( ) ( )

( ) ( ) ( )

( ) ( ) ( )

Korábbi jelölésünkhöz visszatérve a sík együtthatóiból alkotott

kvaternió és a sík egy pontja homogén koordinátáit tartalmazó

kvaternió felhasználásával a sík egyenlete ( #Skaláris_szorzat_4 ):

( )

Ha van három, kvaterniójával adott pontunk:

akkor (mivel a fenti egyenlőségnek mindhárom ponttal teljesülnie kell) az alábbi négydimenziós

vektoriális szorzat eredményével kell a sík négydimenziós vektorának párhuzamosnak lennie:

|

|

a fenti determináns első sorához tartozó előjeles aldeterminánsoknak kell d, a, b, c-nek megfelelnie.

( ) ( ) ( )

( ) ( ) ( )

( ) ( )

( ) ( ) ( )

Kézi számításnál ezt az egyszerűbb képletet használhatnánk. Számítógépen viszont az általánosan

érvényes, a lap tetején látható képletet programoztam ( Kvaternió_műveletek VectProd4 ). Így a

négydimenziós vektoriális szorzat képlete a síkok metszésponjának meghatározására is működni fog.

Page 28: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

26

F# modul: Kvaternió_műveletek

Az előző fejezetekben látható átalakítások, egyszerűsítő összevonások egyrészt segítenek megérteni

a kvaternió-kifejezések működését, másrészt igen megkönnyítik a kézi számolást. Valójában ma már

senki nem fog kvaterniókkal végzett számításokba számítógép nélkül. Akkor viszont – hiszen a mai

számítógépeink már olyan nagy teljesítményűek – nem az összevonások lehetősége, hanem a

kvaternió osztály és az osztály példányain végezhető műveletek teljeskörű programozása lényeges.

Nagy jelentősége van a programozási nyelv korszerűségének, amely okból például a Microsoft F#

(efsárp) nyelve választandó. Hangsúlyozzuk, hogy ma már a programjainkat akár modulonként is

eltérő nyelven írhatjuk, így az alábbi programmodult is más nyelven írt programokba is importálni

lehet. (Például Visual Basic nyelvű programokba is, ahogyan két, későbbi fejezetben látható.)

// Kvaternió osztály deklarálása és alkalmazása module Kvaternió_műveletek open System

let public NULLA = double 0.0 // Gyakran használt állandók let public FÉL = double 0.5 let public EGY = double 1.0 let public MINEGY = - EGY let public MÁSFÉL = double 1.5 let public KETTŐ = double 2.0 let public KÉTÉSFÉL = double 2.5 let public HÁROM = double 3.0 let public HARMAD = EGY / HÁROM let public PI = double Math.PI let public NÉGY = double 4.0 let public NEGYED = EGY / NÉGY let public ÖT = double 5.0 let public ÖTÖD = EGY / ÖT let public HAT = double 6.0 let public HATOD = EGY / HAT let public HÉT = double 7.0 let public NYOLC = double 8.0 let public KILENC = double 9.0 let public TÍZ = double 10.0 let public TIZED = EGY / TÍZ let public TIZENKETTŐ = double 12.0 let public TIZENÖT = double 15.0 let public HÚSZ = double 20.0 let public HUSZONÖT = double 25.0 let public HARMINC = double 30.0 let public HARMINCHAT = double 36.0 let public HATVAN = double 60.0

Page 29: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

27

let public HATVANAD = EGY / HATVAN let public HETVENKETTŐ = double 72.0 let public NYOLCVAN = double 80.0 let public SZÁZ = double 100.0 let public SZÁZNYOLCVAN = double 180.0 let public FOKBÓLÍV = Math.PI / SZÁZNYOLCVAN let public ÍVBŐLFOK = SZÁZNYOLCVAN / Math.PI let public GYÖKKETTŐ = Math.Sqrt(KETTŐ) let public GYÖKHÁROM = Math.Sqrt(HÁROM) let public GYÖKÖT = Math.Sqrt(ÖT) let public GYÖKKETTŐPERKETTŐ = GYÖKKETTŐ / KETTŐ let public GYÖKHÁROMPERKETTŐ = GYÖKHÁROM / KETTŐ let public PICINY = double 0.0000000001 let public SEMMI = double 1e-14 let public VÉGTELEN = Double.PositiveInfinity // A kvaternió képzetes része 3 dimenziós vektor - ezt írjuk le itt: type public Vector(iRész: double, jRész: double, kRész: double) = class // A vektor összetevői: member vektor.iComp = iRész member vektor.jComp = jRész member vektor.kComp = kRész // A vektor hossza: member vektor.abs = Math.Sqrt( iRész * iRész + jRész * jRész + kRész * kRész) // Vektor nyújtása (illetve zsugorítása, ha a nyújtás < 1.0): member vektor.Scal( nyújtás : double ) = Vector( iRész * nyújtás, jRész * nyújtás, kRész * nyújtás) // Vektor egységvektora: member vektor.Unit = let osztó = vektor.abs if osztó < PICINY then Vector() else Vector(iRész / osztó, jRész / osztó, kRész / osztó)

Page 30: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

28

// Két vektort egyenlőnek tekint ez a művelet, ha egy irányba // mutatnak, azaz ha egységvektoraik egyenlők: static member (=) ( U: Vector, V: Vector ) : bool = let Ue = U.Unit let Ve = V.Unit Math.Abs(Ue.iComp - Ve.iComp) < PICINY && Math.Abs(Ue.jComp - Ve.jComp) < PICINY && Math.Abs(Ue.kComp - Ve.kComp) < PICINY // Vektorok összege (U + V): static member (+) ( U: Vector, V: Vector ) = Vector( U.iComp + V.iComp, U.jComp + V.jComp, U.kComp + V.kComp ) // Vektorok különbsége (U - V): static member (-) ( U: Vector, V: Vector ) = Vector( U.iComp - V.iComp, U.jComp - V.jComp, U.kComp - V.kComp ) // Vektorok vektoriális szorzata (a vektorok "természetes" // összeszorzása - az eredmény is vektor): static member (*) ( U: Vector, V: Vector ) = Vector( U.jComp * V.kComp - U.kComp * V.jComp, U.kComp * V.iComp - U.iComp * V.kComp, U.iComp * V.jComp - U.jComp * V.iComp) // Vektorok skaláris szorzata: static member dotProd( U: Vector, V: Vector ) = U.iComp * V.iComp + U.jComp * V.jComp + U.kComp * V.kComp // Határozatlan (nulla) vektor: new() = Vector(NULLA, NULLA, NULLA) end // A kvaternió osztály deklarálása: type public Kvaternió( valós: double, iMérték: double, jMérték: double, kMérték: double) = class // A kvaternió összetevőinek mértéke: member példány.re = valós

Page 31: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

29

member példány.imI = iMérték member példány.imJ = jMérték member példány.imK = kMérték // Kvaternió képzetes része vektor alakban: member példány.Vect = Vector( iMérték, jMérték, kMérték ) // Kvaternió abszolút értékének négyzete: member példány.abs2 = valós * valós + iMérték * iMérték + jMérték * jMérték + kMérték * kMérték // Kvaternió abszolút értéke (hossza): member példány.abs = Math.Sqrt(példány.abs2) // Kvaternió trigonometrikus alakjára utalva az egység // kvaternió képzetes részének mértéke, a kvaternióhoz // tartozó egységvektort ezzel megszorozva áll elő a // (vektor) képzetes rész: member példány.sin = if példány.abs < PICINY then NULLA else példány.Vect.abs / példány.abs // Kvaternió trigonometrikus alakjára utalva az egység // kvaternió valós részének mértéke: member példány.cos = if példány.abs < PICINY then NULLA else valós / példány.abs // Kvaternió konjugálása: member példány.Konj = Kvaternió( valós, -iMérték, -jMérték, -kMérték ) // Kvaternió nyújtása vagy zsugorítása: member példány.Scal( nyújtás : double ) = Kvaternió( valós * nyújtás, iMérték * nyújtás, jMérték * nyújtás, kMérték * nyújtás) // Egység kvaternió képzése: member példány.Unit = let osztó = példány.abs if osztó < PICINY then Kvaternió() else Kvaternió( valós / osztó, iMérték / osztó, jMérték / osztó, kMérték / osztó )

Page 32: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

30

// Egy kvaternió példány trigonometrikus alakja szerint // adja vissza az ívhosszban mért szöget: member példány.arc = if példány.abs < PICINY then NULLA elif példány.Vect.abs < PICINY then Math.PI else Math.Acos(példány.cos) // Két kvaternió egyenlő, ha komponensenként megegyeznek: static member (=) ( P: Kvaternió, Q: Kvaternió ) : bool = Math.Abs(P.re - Q.re) < PICINY && Math.Abs(P.imI - Q.imI) < PICINY && Math.Abs(P.imJ - Q.imJ) < PICINY && Math.Abs(P.imK - Q.imK) < PICINY // Kvaternió összeadás: a szokott módon leírva az összeadás // jele előtt álló kvaternióhoz hozzáadja a + jel utáni // kvaterniót (P + Q ): static member (+) ( P: Kvaternió, Q: Kvaternió ) = Kvaternió( P.re + Q.re, P.imI + Q.imI, P.imJ + Q.imJ, P.imK + Q.imK ) // Kvaternió kivonás: a kisebbítendőből kivonja a // kivonandót ( P - Q ): static member (-) ( P: Kvaternió, Q: Kvaternió ) = Kvaternió( P.re - Q.re, P.imI - Q.imI, P.imJ - Q.imJ, P.imK - Q.imK ) // Kvaternió szorzás: a szorzás jele előtti kvaterniót // szorozza a * jel után álló kvaternióval ( P * Q ): static member (*) ( P: Kvaternió, Q: Kvaternió ) = Kvaternió( P.re * Q.re - P.imI * Q.imI - P.imJ * Q.imJ - P.imK * Q.imK, P.re * Q.imI + P.imI * Q.re + P.imJ * Q.imK - P.imK * Q.imJ, P.re * Q.imJ + P.imJ * Q.re - P.imI * Q.imK + P.imK * Q.imI, P.re * Q.imK + P.imK * Q.re + P.imI * Q.imJ - P.imJ * Q.imI ) // Kvaternió osztás: az osztás jele előtti kvaterniót // elosztja a / jel után álló kvaternióval. Végrehajtása: // az osztandót megszorozza az osztó konjugáltjával, majd // az így nyert szorzatot elosztja az osztó abszolút értéke // négyzetével. Ezért az osztás akkor végrehajtható, ha az

Page 33: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

31

// osztó kvaternió nem az O kvaternió (tehát, ha az osztó // abszolút értéke nem 0) ( P / Q ): static member (/) (P: Kvaternió, Q: Kvaternió) = let osztó = Q.abs2 if osztó < PICINY then Kvaternió() else Kvaternió(( P.re * Q.re + P.imI * Q.imI + P.imJ * Q.imJ + P.imK * Q.imK) / osztó, (-P.re * Q.imI + P.imI * Q.re - P.imJ * Q.imK + P.imK * Q.imJ) / osztó, (-P.re * Q.imJ + P.imJ * Q.re + P.imI * Q.imK - P.imK * Q.imI) / osztó, (-P.re * Q.imK + P.imK * Q.re - P.imI * Q.imJ + P.imJ * Q.imI) / osztó ) // Négydimenziós (kvaternió) skalárszorzat. Homogén // koordinátájú pont és kvaterniójával megadott együtthatós // (egységvektor képzetes részű) sík esetén jól használható // eredményt szolgáltat. (Síkra illeszkedő pontnál 0, amúgy a // háromdimenziós térben a síktól mért távolságot adja meg.) static member dotProd4(P: Kvaternió, Q: Kvaternió): double = (P * Q.Konj).re // Négydimenziós vektoriális szorzat. Három kvaterniót // használ 4 x 4 -es determinánsában. Az első sorhoz tartozó // előjeles aldeterminánsok értékét adja vissza kvaternióba // rendezve. Ha három pont kvaterniójával hívjuk, akkor // sík együtthatóvektorát adja vissza úgy, hogy a vektor // helyén egységvektor van. (Ha határozatlan volna a sík, // a homogén koordinátaként nem megengedett O-t adja ki.) // Ha síkok metszéspontját számítjuk, a pont valós része // helyén 1 vagy 0 lesz attól függően, hogy létezik-e. static member VectProd4( P: Kvaternió, Q: Kvaternió, R: Kvaternió ) : Kvaternió = let d = P.imI * ( Q.imJ * R.imK - Q.imK * R.imJ ) - P.imJ * ( Q.imI * R.imK - Q.imK * R.imI ) + P.imK * ( Q.imI * R.imJ - Q.imJ * R.imI ) let a = -P.re * ( Q.imJ * R.imK - Q.imK * R.imJ ) + P.imJ * ( Q.re * R.imK - Q.imK * R.re ) - P.imK * ( Q.re * R.imJ - Q.imJ * R.re ) let b = P.re * ( Q.imI * R.imK - Q.imK * R.imI ) - P.imI * ( Q.re * R.imK - Q.imK * R.re ) + P.imK * ( Q.re * R.imI - Q.imI * R.re ) let c = -P.re * ( Q.imI * R.imJ - Q.imJ * R.imI ) + P.imI * ( Q.re * R.imJ - Q.imJ * R.re ) - P.imJ * ( Q.re * R.imI - Q.imI * R.re ) let Képzetes = Vector( a, b, c )

Page 34: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

32

let osztó = if d < NULLA then Képzetes.abs else -Képzetes.abs if Math.Abs( P.re - EGY ) < PICINY && // 3 pont alapján Math.Abs( Q.re - EGY ) < PICINY && // sík létrehozása Math.Abs( R.re - EGY ) < PICINY then if Math.Abs(osztó) < PICINY then Kvaternió() else Kvaternió(d, Képzetes).Scal( EGY / osztó ) else // Három sík metszéspontjának meghatározása: if Math.Abs(d) < PICINY then Kvaternió(NULLA,Képzetes) else Kvaternió( EGY, Képzetes.Scal( EGY / d ) ) // Két homogén kvaternió (arányosság erejéig) egyenlő, ha // egységkvaternióik komponensenként megegyeznek: static member HomogeneousEqual( P: Kvaternió, Q: Kvaternió ): bool = let Pe = P.Unit let Qe = Q.Unit Math.Abs(Pe.re - Qe.re) < PICINY && Math.Abs(Pe.imI - Qe.imI) < PICINY && Math.Abs(Pe.imJ - Qe.imJ) < PICINY && Math.Abs(Pe.imK - Qe.imK) < PICINY // Kvaternió létrehozása a képzetes rész vektor alakjával: new( val: double, vektor: Vector) = Kvaternió( val, vektor.iComp, vektor.jComp, vektor.kComp ) // Kvaternió létrehozása, amely a V (origón átmenően // felvett) vektor (mint tengely) körül fok szöggel forgat // (az elforgatandó kvaterniót balról ezzel a kvaternióval, // jobbról pedig ennek a konjugáltjával kell szorozni). new( V : Vector, fok: double ) = let ív = fok * FOKBÓLÍV / KETTŐ Kvaternió( Math.Cos(ív), V.Unit.Scal(Math.Sin(ív)) ) // Határozatlan (nulla) kvaternió: new() = Kvaternió( NULLA, NULLA, NULLA, NULLA ) end // Kvaternió állandók: let public O = Kvaternió() // Valós egység. Homogén koordinátáknál ez a nulla pont: let public Base0 = Kvaternió( EGY, NULLA, NULLA, NULLA )

Page 35: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

33

// Homogén koordinátáknál I tengely végtelen távoli pontja: let public BaseI = Kvaternió( NULLA, EGY, NULLA, NULLA ) // Homogén koordinátáknál J tengely végtelen távoli pontja: let public BaseJ = Kvaternió( NULLA, NULLA, EGY, NULLA ) // Homogén koordinátáknál K tengely végtelen távoli pontja: let public BaseK = Kvaternió( NULLA, NULLA, NULLA, EGY ) // Kvaternió négyzetgyökvonás: let public Négyzetgyök ( Szám : Kvaternió) : ( Kvaternió * Kvaternió ) = let valós = Math.Sqrt( ( Szám.re + Szám.abs ) / KETTŐ ) let nevező = KETTŐ * valós if Math.Abs(nevező) < PICINY then ( O, O ) else ( Kvaternió( valós, Szám.imI / nevező, Szám.imJ / nevező, Szám.imK / nevező ), Kvaternió( -valós, -Szám.imI / nevező, -Szám.imJ / nevező, -Szám.imK / nevező ) ) // Az alábbi konstruktoroknál pontokat vagy a három koordináta // felsorolásával, vagy kvaternióként lehet megadni. A vektorokat // soha nem kvaternióként, hanem csakis Vector-ként adjuk meg. // Egyenesszakasz leírása: type public Section( Kezdet: Kvaternió, Vég : Kvaternió ) = class // Ha a Kezdet és a Vég valós része nem azonos, nincs // értelme a szakasznak, pontjai nincsenek ugyanabban a // térben: let egyVilágban = Math.Abs( Kezdet.re - Vég.re ) < PICINY member szakasz.Pont1 = if egyVilágban then Kezdet else O member szakasz.Pont2 = if egyVilágban then Vég else O member szakasz.hossz = ( szakasz.Pont2 - szakasz.Pont1 ).abs // Paraméter nélkül is határozatlan a szakasz: new() = Section( O, O ) end

Page 36: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

34

// Háromszög osztály leírása: type public Triangle( A : Kvaternió, B : Kvaternió, C : Kvaternió ) = class // Ha a A, B és C valós része nem azonos, nincs értelme // a háromszögnek, pontjai nincsenek ugyanabban a térben: let egyVilágban = Math.Abs( B.re - A.re ) < PICINY && Math.Abs( C.re - A.re ) < PICINY member alakzat.Acsúcs = if egyVilágban then A else O member alakzat.Bcsúcs = if egyVilágban then B else O member alakzat.Ccsúcs = if egyVilágban then C else O member alakzat.Csúcsok : Kvaternió array = [| alakzat.Acsúcs; alakzat.Bcsúcs; alakzat.Ccsúcs |] member alakzat.aHossz = ( alakzat.Ccsúcs - alakzat.Bcsúcs ).abs member alakzat.bHossz = ( alakzat.Ccsúcs - alakzat.Acsúcs ).abs member alakzat.cHossz = ( alakzat.Bcsúcs - alakzat.Acsúcs ).abs member alakzat.kerület = alakzat.aHossz + alakzat.bHossz + alakzat.cHossz member alakzat.terület = let félK = alakzat.kerület / KETTŐ Math.Sqrt( félK * ( félK - alakzat.aHossz ) * ( félK - alakzat.bHossz ) * ( félK - alakzat.cHossz ) ) member alakzat.szabályos : bool = Math.Abs( alakzat.aHossz - alakzat.bHossz ) < PICINY && Math.Abs( alakzat.bHossz - alakzat.cHossz ) < PICINY && Math.Abs( alakzat.cHossz - alakzat.aHossz ) < PICINY // A csúcsok megadása nélkül is határozatlan a háromszög: new() = Triangle( O, O, O )

Page 37: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

35

// A csúcsok leírhatók kvaterniók három elemű tömbjével is: new( Csúcsok : Kvaternió array ) = if Csúcsok.Length = 3 then Triangle( Csúcsok.[0], Csúcsok.[1], Csúcsok.[2] ) else Triangle() end // Négyszög osztály leírása: type public Quadrangle( A : Kvaternió, B : Kvaternió, C : Kvaternió, D : Kvaternió) = class // Ha a A, B, C és D valós része nem azonos, nincs értelme // a négyszögnek, pontjai nincsenek ugyanabban a térben: let egyVilágban = Math.Abs( B.re - A.re ) < PICINY && Math.Abs( C.re - A.re ) < PICINY && Math.Abs( D.re - A.re ) < PICINY member alakzat.Acsúcs = if egyVilágban then A else O member alakzat.Bcsúcs = if egyVilágban then B else O member alakzat.Ccsúcs = if egyVilágban then C else O member alakzat.Dcsúcs = if egyVilágban then D else O member alakzat.Csúcsok : Kvaternió array = [| alakzat.Acsúcs; alakzat.Bcsúcs; alakzat.Ccsúcs; alakzat.Dcsúcs |] member alakzat.hosszAB = ( alakzat.Bcsúcs - alakzat.Acsúcs ).abs member alakzat.hosszBC = ( alakzat.Ccsúcs - alakzat.Bcsúcs ).abs member alakzat.hosszCD = ( alakzat.Dcsúcs - alakzat.Ccsúcs ).abs member alakzat.hosszDA = ( alakzat.Acsúcs - alakzat.Dcsúcs ).abs member alakzat.kerület = alakzat.hosszAB + alakzat.hosszBC + alakzat.hosszCD + alakzat.hosszDA

Page 38: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

36

member alakzat.terület = let hosszAC = ( alakzat.Ccsúcs - alakzat.Acsúcs ).abs let terület( a: double, b:double, c: double) = let s = ( a + b + c ) / KETTŐ Math.Sqrt( s * (s - a) * (s - b) * (s - c) ) terület( alakzat.hosszAB, alakzat.hosszBC, hosszAC ) + terület( alakzat.hosszCD, alakzat.hosszDA, hosszAC ) member alakzat.szabályos : bool = Math.Abs( alakzat.hosszAB - alakzat.hosszBC ) < PICINY && Math.Abs( alakzat.hosszBC - alakzat.hosszCD ) < PICINY && Math.Abs( alakzat.hosszCD - alakzat.hosszDA ) < PICINY && Math.Abs( alakzat.hosszDA - alakzat.hosszAB ) < PICINY // A csúcsok megadása nélkül is határozatlan a négyszög: new() = Quadrangle( O, O, O, O ) // A csúcsok leírhatók kvaterniók négy elemű tömbjével is: new( Csúcsok : Kvaternió array ) = if Csúcsok.Length = 4 then Quadrangle( Csúcsok.[0], Csúcsok.[1], Csúcsok.[2], Csúcsok.[3] ) else Quadrangle() end // Ötszög osztály leírása: type public Pentagon( A : Kvaternió, B : Kvaternió, C : Kvaternió, D : Kvaternió, E : Kvaternió) = class // Ha a A, B, C, D és E valós része nem azonos, nincs // értelme az ötszögnek, pontjai nincsenek ugyanabban a // térben: let egyVilágban = Math.Abs( B.re - A.re ) < PICINY && Math.Abs( C.re - A.re ) < PICINY && Math.Abs( D.re - A.re ) < PICINY && Math.Abs( E.re - A.re ) < PICINY member alakzat.Acsúcs = if egyVilágban then A else O member alakzat.Bcsúcs = if egyVilágban then B else O member alakzat.Ccsúcs = if egyVilágban then C else O

Page 39: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

37

member alakzat.Dcsúcs = if egyVilágban then D else O member alakzat.Ecsúcs = if egyVilágban then E else O member alakzat.Csúcsok : Kvaternió array = [| alakzat.Acsúcs; alakzat.Bcsúcs; alakzat.Ccsúcs; alakzat.Dcsúcs; alakzat.Ecsúcs |] member alakzat.hosszAB = ( alakzat.Bcsúcs - alakzat.Acsúcs ).abs member alakzat.hosszBC = ( alakzat.Ccsúcs - alakzat.Bcsúcs ).abs member alakzat.hosszCD = ( alakzat.Dcsúcs - alakzat.Ccsúcs ).abs member alakzat.hosszDE = ( alakzat.Ecsúcs - alakzat.Dcsúcs ).abs member alakzat.hosszEA = ( alakzat.Acsúcs - alakzat.Ecsúcs ).abs member alakzat.kerület = alakzat.hosszAB + alakzat.hosszBC + alakzat.hosszCD + alakzat.hosszDE + alakzat.hosszEA member alakzat.terület = let hosszAC = ( alakzat.Ccsúcs - alakzat.Acsúcs ).abs let hosszAD = ( alakzat.Dcsúcs - alakzat.Acsúcs ).abs let terület( a: double, b:double, c: double) = let s = ( a + b + c ) / KETTŐ Math.Sqrt( s * (s - a) * (s - b) * (s - c) ) terület( alakzat.hosszAB, alakzat.hosszBC, hosszAC) + terület( alakzat.hosszCD, hosszAD, hosszAC) + terület( alakzat.hosszDE, alakzat.hosszEA, hosszAD) member alakzat.szabályos : bool = Math.Abs( alakzat.hosszAB - alakzat.hosszBC ) < PICINY && Math.Abs( alakzat.hosszBC - alakzat.hosszCD ) < PICINY && Math.Abs( alakzat.hosszCD - alakzat.hosszDE ) < PICINY && Math.Abs( alakzat.hosszDE - alakzat.hosszEA ) < PICINY && Math.Abs( alakzat.hosszEA - alakzat.hosszAB ) < PICINY // A csúcsok megadása nélkül is határozatlan az ötszög: new() = Pentagon( O, O, O, O, O )

Page 40: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

38

// A csúcsok leírhatók kvaterniók öt elemű tömbjével is: new( Csúcsok : Kvaternió array ) = if Csúcsok.Length = 5 then Pentagon( Csúcsok.[0], Csúcsok.[1], Csúcsok.[2], Csúcsok.[3], Csúcsok.[4]) else Pentagon() end // Hatszög osztály leírása: type public Hexagon( A : Kvaternió, B : Kvaternió, C : Kvaternió, D : Kvaternió, E : Kvaternió, F : Kvaternió) = class // Ha a A, B, C, D, E és F valós része nem azonos, nincs // értelme a hatszögnek, pontjai nincsenek ugyanabban a // térben: let egyVilágban = Math.Abs( B.re - A.re ) < PICINY && Math.Abs( C.re - A.re ) < PICINY && Math.Abs( D.re - A.re ) < PICINY && Math.Abs( E.re - A.re ) < PICINY && Math.Abs( F.re - A.re ) < PICINY member alakzat.Acsúcs = if egyVilágban then A else O member alakzat.Bcsúcs = if egyVilágban then B else O member alakzat.Ccsúcs = if egyVilágban then C else O member alakzat.Dcsúcs = if egyVilágban then D else O member alakzat.Ecsúcs = if egyVilágban then E else O member alakzat.Fcsúcs = if egyVilágban then F else O member alakzat.Csúcsok : Kvaternió array = [| alakzat.Acsúcs; alakzat.Bcsúcs; alakzat.Ccsúcs; alakzat.Dcsúcs; alakzat.Ecsúcs; alakzat.Fcsúcs |] member alakzat.hosszAB = ( alakzat.Bcsúcs - alakzat.Acsúcs ).abs member alakzat.hosszBC = ( alakzat.Ccsúcs - alakzat.Bcsúcs ).abs member alakzat.hosszCD = ( alakzat.Dcsúcs - alakzat.Ccsúcs ).abs

Page 41: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

39

member alakzat.hosszDE = ( alakzat.Ecsúcs - alakzat.Dcsúcs ).abs member alakzat.hosszEF = ( alakzat.Fcsúcs - alakzat.Ecsúcs ).abs member alakzat.hosszFA = ( alakzat.Acsúcs - alakzat.Fcsúcs ).abs member alakzat.kerület = alakzat.hosszAB + alakzat.hosszBC + alakzat.hosszCD + alakzat.hosszDE + alakzat.hosszEF + alakzat.hosszFA member alakzat.terület = let hosszAC = ( alakzat.Ccsúcs - alakzat.Acsúcs ).abs let hosszAD = ( alakzat.Dcsúcs - alakzat.Acsúcs ).abs let hosszAE = ( alakzat.Ecsúcs - alakzat.Acsúcs ).abs let terület( a: double, b:double, c: double) = let s = ( a + b + c ) / KETTŐ Math.Sqrt( s * (s - a) * (s - b) * (s - c) ) terület( alakzat.hosszAB, alakzat.hosszBC, hosszAC ) + terület( alakzat.hosszCD, hosszAC, hosszAD ) + terület( alakzat.hosszDE, hosszAD, hosszAE ) + terület( alakzat.hosszEF, alakzat.hosszFA, hosszAE ) member alakzat.szabályos : bool = Math.Abs( alakzat.hosszAB - alakzat.hosszBC ) < PICINY && Math.Abs( alakzat.hosszBC - alakzat.hosszCD ) < PICINY && Math.Abs( alakzat.hosszCD - alakzat.hosszDE ) < PICINY && Math.Abs( alakzat.hosszDE - alakzat.hosszEF ) < PICINY && Math.Abs( alakzat.hosszEF - alakzat.hosszFA ) < PICINY && Math.Abs( alakzat.hosszFA - alakzat.hosszAB ) < PICINY // A csúcsok megadása nélkül is határozatlan a hatszög: new() = Hexagon( O, O, O, O, O, O ) // A csúcsok leírhatók kvaterniók hat elemű tömbjével is: new( Csúcsok : Kvaternió array ) = if Csúcsok.Length = 6 then Hexagon( Csúcsok.[0], Csúcsok.[1], Csúcsok.[2], Csúcsok.[3], Csúcsok.[4], Csúcsok.[5] ) else Hexagon() end

Page 42: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

40

// Egyenesnél és síknál az 1 valós részhez tartozó térben hozzuk // létre a példányokat, minden kvaterniónál EGY valós részt adunk // meg. A pontok koordinátái így a pontok homogén koordinátájának // felelnek meg. // Kvaterniók (illetve azok vektor képzetes összetevője) segítségével // végzett térbeli szerkesztésekhez használt egyenes osztály // deklarálása (alaphelyzetben két pontja hat koordinátájával): type public Line( i1: double, j1: double, k1: double, i2: double, j2: double, k2: double) = class // Kvaternióként tárolva az egyenes egyik pontja: member egyenes.Point1 = Kvaternió( EGY, i1, j1, k1 ) // Minden esetben tárolva ez a második pont is: member egyenes.Point2 = Kvaternió( EGY, i2, j2, k2 ) // Az egyenes tárolt tulajdonsága az irány egységvektor // (amelynek hossza hossza 0, ha az egyenes nem létezik): member egyenes.Vect : Vector = ( egyenes.Point2.Vect - egyenes.Point1.Vect ).Unit // Az egyenes két pontja a konstruktorban két kvaternióval // is megadható: new( egyikPont: Kvaternió, másikPont: Kvaternió ) = if Math.Abs(egyikPont.re - másikPont.re) < PICINY then Line( egyikPont.imI, egyikPont.imJ, egyikPont.imK, másikPont.imI, másikPont.imJ, másikPont.imK ) else Line() // Az egyenes két pontja a pontok szakaszával is megadható: new( szakasz: Section) = Line( szakasz.Pont1.imI, szakasz.Pont1.imJ, szakasz.Pont1.imK, szakasz.Pont2.imI, szakasz.Pont2.imJ, szakasz.Pont2.imK ) // Megadható az egyenes egy pontja három koordinátájával és // az irányvektor: new( pI: double, pJ: double, pK: double, egyenesIrány: Vector)=

Page 43: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

41

Line( pI, pJ, pK, pI + PI * egyenesIrány.iComp, pJ + PI * egyenesIrány.jComp, pK + PI * egyenesIrány.kComp) // Az egyenes egy pontja kvaternióként és az irányvektor // is ugyanúgy leírja az egyenest: new( egyenesPont: Kvaternió, egyenesIrány: Vector) = Line( egyenesPont.imI, egyenesPont.imJ, egyenesPont.imK, egyenesPont.imI + PI * egyenesIrány.iComp, egyenesPont.imJ + PI * egyenesIrány.jComp, egyenesPont.imK + PI * egyenesIrány.kComp) // Határozatlan egyenes: new() = Line(NULLA, NULLA, NULLA, NULLA, NULLA, NULLA) end // Kvaterniók (illetve azok vektor képzetes összetevője) segítségével // végzett térbeli szerkesztésekhez használt sík osztály deklarálása // Egy pontja három koordinátájával és a normálvektorával: type public Plane( pontI: double, pontJ: double, pontK: double, nVektor: Vector ) = class // A sík tárolt tulajdonsága kvaternióként egyik pontja: member sík.Point = Kvaternió( EGY, pontI, pontJ, pontK ) // A sík normális egységvektora (hossza 0, ha a sík nincs): member sík.NormVect = if Vector.dotProd(nVektor, sík.Point.Vect) < NULLA then nVektor.Unit.Scal(MINEGY) else nVektor.Unit // A sík normálvektora körül 60°-os forgatást végző kvaternió: member sík.Rot60Kvat = Kvaternió( GYÖKHÁROMPERKETTŐ, sík.NormVect.Scal( FÉL ) ) // A sík normálvektora körül 72°-os forgatást végző kvaternió: member sík.Rot72Kvat = Kvaternió( sík.NormVect, HETVENKETTŐ )

Page 44: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

42

// A sík normálvektora körül 90°-os forgatást végző kvaternió: member sík.Rot90Kvat = Kvaternió( EGY, sík.NormVect).Scal(GYÖKKETTŐPERKETTŐ) // A sík normálvektora körül 120°-os forgatás: member sík.Rot120Kvat = Kvaternió( FÉL, sík.NormVect.Scal(GYÖKHÁROMPERKETTŐ ) ) // A sík normálvektora körül 180°-os forgatás: member sík.Rot180Kvat = Kvaternió( NULLA, sík.NormVect )

// A sík pontja (metszéspontja vagy végtelen távoli pont) // az i tengelyen:

member sík.iPoint =

if Math.Abs(sík.NormVect.iComp) < PICINY then Kvaternió(EGY, VÉGTELEN, NULLA, NULLA) else Kvaternió(EGY, Vector.dotProd(sík.Point.Vect, sík.NormVect) / sík.NormVect.iComp, NULLA, NULLA)

Page 45: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

43

// A sík pontja (metszéspontja vagy végtelen távoli pont) // a j tengelyen: member sík.jPoint = if Math.Abs(sík.NormVect.jComp) < PICINY then Kvaternió(EGY, NULLA, VÉGTELEN, NULLA) else Kvaternió(EGY, NULLA, Vector.dotProd(sík.Point.Vect, sík.NormVect) / sík.NormVect.jComp, NULLA) // A sík pontja (metszéspontja vagy végtelen távoli pont) // a k tengelyen: member sík.kPoint = if Math.Abs(sík.NormVect.kComp) < PICINY then Kvaternió(EGY, NULLA, NULLA, VÉGTELEN) else Kvaternió(EGY, NULLA, NULLA, Vector.dotProd(sík.Point.Vect, sík.NormVect) / sík.NormVect.kComp) // Homogén koordinátákat használva ezek a tulajdonságok: // A sík (közönséges vagy ideális) pontja az i tengelyen: member sík.iHomogeneous = if Math.Abs(sík.NormVect.iComp) < PICINY then BaseI else Kvaternió(EGY, Vector.dotProd(sík.Point.Vect, sík.NormVect) / sík.NormVect.iComp, NULLA, NULLA) // A sík (közönséges vagy ideális) pontja a j tengelyen: member sík.jHomogeneous = if Math.Abs(sík.NormVect.jComp) < PICINY then BaseJ else Kvaternió(EGY, NULLA, Vector.dotProd(sík.Point.Vect, sík.NormVect) / sík.NormVect.jComp, NULLA) // A sík (közönséges vagy ideális) pontja a k tengelyen: member sík.kHomogeneos = if Math.Abs(sík.NormVect.kComp) < PICINY then BaseK

Page 46: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

44

else Kvaternió(EGY, NULLA, NULLA, Vector.dotProd(sík.Point.Vect, sík.NormVect) / sík.NormVect.kComp)

// A sík origóhoz legközelebbi pontja: az origóból a síkra // bocsájtott merőleges talppontja, a forgatási tengely pontja member sík.Near = let iTáv = sík.iPoint.imI let jTáv = sík.jPoint.imJ let kTáv = sík.kPoint.imK let ijNevező = iTáv * iTáv + jTáv * jTáv let jkNevező = jTáv * jTáv + kTáv * kTáv let kiNevező = kTáv * kTáv + iTáv * iTáv let ijArány = if ijNevező > PICINY then iTáv * jTáv / ijNevező else NULLA let jkArány = if jkNevező > PICINY then jTáv * kTáv / jkNevező else NULLA

Page 47: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

45

let kiArány = if kiNevező > PICINY then kTáv * iTáv / kiNevező else NULLA let iN = iTáv * iTáv let jN = jTáv * jTáv let kN = kTáv * kTáv let négyzetek = iN * jN + jN * kN + kN * iN let főArány = if négyzetek > PICINY then Math.Abs(iTáv * jTáv * kTáv)/Math.Sqrt(négyzetek) else NULLA if iTáv = VÉGTELEN then if jTáv = VÉGTELEN then sík.kPoint elif kTáv = VÉGTELEN then sík.jPoint else Kvaternió(EGY, NULLA, kTáv * jkArány, jTáv * jkArány) elif jTáv = VÉGTELEN then if kTáv = VÉGTELEN then sík.iPoint else Kvaternió(EGY, kTáv * kiArány, NULLA, iTáv * kiArány) elif kTáv = VÉGTELEN then Kvaternió(EGY, jTáv * ijArány, iTáv * ijArány, NULLA) else Kvaternió(EGY, sík.NormVect.Scal(főArány)) // A sík origóhoz legközelebbi pontja: az origóból a síkra // bocsájtott merőleges talppontja, a forgatási tengely pontja member sík.HomogeneousNear = let iTáv = sík.iPoint.imI let jTáv = sík.jPoint.imJ let kTáv = sík.kPoint.imK let ijNevező = iTáv * iTáv + jTáv * jTáv let jkNevező = jTáv * jTáv + kTáv * kTáv let kiNevező = kTáv * kTáv + iTáv * iTáv let ijArány = if ijNevező > PICINY then iTáv * jTáv / ijNevező else NULLA let jkArány = if jkNevező > PICINY then jTáv * kTáv / jkNevező else NULLA let kiArány = if kiNevező > PICINY then kTáv * iTáv / kiNevező else NULLA let iN = iTáv * iTáv let jN = jTáv * jTáv let kN = kTáv * kTáv let négyzetek = iN * jN + jN * kN + kN * iN let főArány =

Page 48: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

46

if négyzetek > PICINY then Math.Abs(iTáv * jTáv * kTáv)/Math.Sqrt(négyzetek) else NULLA if sík.iHomogeneous = BaseI then if sík.jHomogeneous = BaseJ then sík.kPoint elif sík.kHomogeneos = BaseK then sík.jPoint else Kvaternió(EGY, NULLA, kTáv * jkArány, jTáv * jkArány) elif sík.jHomogeneous = BaseJ then if sík.kHomogeneos = BaseK then sík.iPoint else Kvaternió(EGY, kTáv * kiArány, NULLA, iTáv * kiArány) elif sík.kHomogeneos = BaseK then Kvaternió(EGY, jTáv * ijArány, iTáv * ijArány, NULLA) else Kvaternió(EGY, sík.NormVect.Scal(főArány))

// Homogén koordinátákkal a síkot egy kvaternió írja le, // amelynek képzetes része a sík normálisa, valós része pedig // abból a feltételből számítható, hogy a sík tetszőleges

Page 49: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

47

// pontja homogén koordinátanégyesével a síkot leíró kvaternió // négydimenziós skaláris szorzata 0. Egyébként ez a valós // rész az origó (Base0) és a sík távolsága negatív előjellel. member sík.HomogeneousPlane = Kvaternió( -sík.Near.Vect.abs, sík.Near.Vect.Unit ) // A sík megadható három pontjának kilenc koordinátájával is: new( i1: double, j1: double, k1: double, i2: double, j2: double, k2: double, i3: double, j3: double, k3: double) = Plane( i1, j1, k1, Vector(i3 - i2, j3 - j2, k3 - k2) * Vector(i3 - i1, j3 - j1, k3 - k1) ) // A sík három pontja megadható háromszögként is: new( háromSzög: Triangle) = Plane( háromSzög.Acsúcs.imI, háromSzög.Acsúcs.imJ, háromSzög.Acsúcs.imK, ( háromSzög.Ccsúcs.Vect - háromSzög.Acsúcs.Vect ) * ( háromSzög.Bcsúcs.Vect - háromSzög.Acsúcs.Vect ) ) // A sík egy pontja kvaternióként és a normálvektora is jó: new( síkPontja: Kvaternió, normálVektor: Vector ) = Plane(síkPontja.imI, síkPontja.imJ, síkPontja.imK, normálVektor) // Egyetlen kvaternióval is megadhatjuk a síkot, ha ez // a kvaternió a sík homogén koordinátás leírója: new( homogénSík: Kvaternió ) = let osztó = homogénSík.Vect.abs let normáltSík = if osztó < PICINY then O else homogénSík.Scal( EGY / osztó ) Plane( Kvaternió( EGY, normáltSík.Vect.Scal( -normáltSík.re ) ), normáltSík.Vect ) // A sík rögzíthető a három tengelyen elhelyezkedő // pontjának három koordinátájával (ezek helyébe VÉGTELEN // is írható, ha a sík az adott tengellyel párhuzamos): new( iPont: double, jPont: double, kPont:double ) =

Page 50: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

48

let iKvaternió = Kvaternió(EGY, iPont, NULLA, NULLA) let jKvaternió = Kvaternió(EGY, NULLA, jPont, NULLA) let kKvaternió = Kvaternió(EGY, NULLA, NULLA, kPont) if iPont = VÉGTELEN then if jPont = VÉGTELEN then // A normálvektor k irányába mutat: Plane(kKvaternió, BaseK.Vect) elif kPont = VÉGTELEN then // A normálvektor j irányába mutat: Plane(jKvaternió, BaseJ.Vect) else Plane( kKvaternió, BaseI.Vect * ( jKvaternió.Vect - kKvaternió.Vect ) ) elif jPont = VÉGTELEN then if kPont = VÉGTELEN then // A normálvektor i irányába mutat: Plane(iKvaternió, BaseI.Vect) else Plane( iKvaternió, BaseJ.Vect * ( kKvaternió.Vect - iKvaternió.Vect ) ) elif kPont = VÉGTELEN then Plane( jKvaternió, BaseK.Vect * ( iKvaternió.Vect - jKvaternió.Vect ) ) else Plane( kKvaternió, ( iKvaternió.Vect - kKvaternió.Vect ) * ( jKvaternió.Vect - kKvaternió.Vect ) ) // Homogén koordinátás pontmegadás esetén (közönséges és // ideális pontokkal) további lehetőségek kínálkoznak: // A sík rögzíthető a három tengelyen elhelyezkedő pontjának // három kvaterniójával (ezek helyébe ideális pont kvaterniója // is írható, ha az adott pont végtelen távol van): // iKvaternió = Kvaternió(EGY, iPont, NULLA, NULLA) // vagy: Kvaternió(NULLA, EGY, NULLA, NULLA) - BaseI // jKvaternió = Kvaternió(EGY, NULLA, jPont, NULLA) // vagy: Kvaternió(NULLA, NULLA, EGY, NULLA) - BaseJ // kKvaternió = Kvaternió(EGY, NULLA, NULLA, kPont) // vagy: Kvaternió(NULLA, NULLA, NULLA, EGY) - BaseK // Ha a három pont nem tengelypont, akkor is működik a // függvény három tetszőleges pontra: new( iKvaternió: Kvaternió, jKvaternió: Kvaternió, kKvaternió: Kvaternió ) = if Math.Abs(iKvaternió.imJ) < PICINY && Math.Abs(iKvaternió.imK) < PICINY && Math.Abs(jKvaternió.imI) < PICINY && Math.Abs(jKvaternió.imK) < PICINY &&

Page 51: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

49

Math.Abs(kKvaternió.imI) < PICINY && Math.Abs(kKvaternió.imJ) < PICINY then if iKvaternió = BaseI then if jKvaternió = BaseJ then // A normálvektor k irányába mutat: Plane(kKvaternió, BaseK.Vect) elif kKvaternió = BaseK then // A normálvektor j irányába mutat: Plane(jKvaternió, BaseJ.Vect) else Plane( kKvaternió, BaseI.Vect * ( jKvaternió.Vect - kKvaternió.Vect ) ) elif jKvaternió = BaseJ then if kKvaternió = BaseK then // A normálvektor i irányába mutat: Plane(iKvaternió, BaseI.Vect) else Plane( iKvaternió, BaseJ.Vect * ( kKvaternió.Vect - iKvaternió.Vect ) ) elif kKvaternió = BaseK then Plane( jKvaternió, BaseK.Vect * ( iKvaternió.Vect - jKvaternió.Vect ) ) else Plane( kKvaternió, ( iKvaternió.Vect - kKvaternió.Vect ) * ( jKvaternió.Vect - kKvaternió.Vect ) ) elif Math.Abs(iKvaternió.re - jKvaternió.re) < PICINY && Math.Abs(jKvaternió.re - kKvaternió.re) < PICINY then Plane( iKvaternió.imI, iKvaternió.imJ, iKvaternió.imK, ( kKvaternió.Vect - iKvaternió.Vect ) * ( jKvaternió.Vect - iKvaternió.Vect ) ) else Plane() // Határozatlan sík: new() = Plane(NULLA, NULLA, NULLA, Vector() ) end

Ebben a modulban nem csupán a Kvaternió osztályt és az osztály példányain végrehajtható

műveleteket határoztunk meg, hanem további (segéd) osztályokat is deklaráltunk. A homogén

koordináták természetes használatát kívánjuk elősegíteni azzal, hogy a közönséges pontokat 1 valós

összetevővel tároljuk, továbbá a síkok és tulajdonságaik homogén koordinátás leírását is támogatjuk.

A Vector osztály deklarálása a kvaterniók képzetes részének tömör leírását szolgálja. A Vector osztály

példányain az összeadáson és kivonáson kívül kétféle szorzást: a vektor eredményt adó vektoriális

szorzást (ezt a műveletet az operandusok szokásos szorzásaként írhatjuk le), továbbá a valós

eredményt szolgáltató skaláris szorzást (angol dot product nevéből: dotProd) is megvalósítottunk.

Page 52: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

50

A Kvaternió osztály példányain az összeadást, a kivonást, a szorzást és az osztást is lehetővé teszi ez

az osztály-deklaráció. Tudjuk, hogy a kvaternió-szorzás nem kommutatív, azaz a szorzótényezőket

nem lehet felcserélni, de a műveleteket (egyik és másik sorrendben is) el lehet végezni. Az osztást az

osztó konjugáltjával elvégzett jobbról szorzásként értelmezzük. (Nulla abszolút értékű osztóval nem

lehet osztani.) A homogén koordináták egyszerű kezelése érdekében a kvaternió egyenlőséget

(valamennyi összetevő egyenlőséget vizsgáljuk) és a homogén egyenlőséget (ekkor csak a kvaterniók

arányosságát nézzük) is vizsgálhatjuk ennek az osztálynak a függvényeivel. Hívható a négydimenziós

skaláris (dotProd4) és vektoriális szorzás (VectProd4) függvény is. (Utóbbi függvény egyszerűvé

teszi a három pontra illeszkedő sík egyenletének, illetve a három metsző sík metszéspontjának a

meghatározását.)

Mivel a kvaterniók segítségével könnyű a háromdimenziós tér alakzatait leírni és forgatni, a modul a

háromdimenziós térben elvégezhető rajz műveletekhez szükséges két osztály (az egyenes és a sík)

deklarációját is tartalmazza. Itt is megemlítjük, hogy a szerkesztésekhez szükséges térbeli pontok

megadását a homogén koordinátáikat tartalmazó kvaterniókkal végezzük el, míg a térbeli vektorok

leírására a Vector osztály példányait használjuk. Láttuk, hogy a forgatási transzformáció a kvaternió

valós részét változatlanul hagyja, valamint az elforgatott kvaternió valós részének sincs hatása a

forgatás utáni helyzethez tartozó képzetes részhez. (Ezért a forgatásnak alávetett kvaternió valós

részével nem kell törődni – például nem kell nullázni, mielőtt a kvaterniójával adott pontra forgatási

transzformációt alkalmaznánk – a homogén koordinátát sem teszi értelmezhetetlenné a forgatás.)

A síkot nagyon egyszerű a három koordinátatengelyen elhelyezkedő pontjának megadásával

létrehozni. A sík lehet koordinátasíkkal vagy tengellyel párhuzamos is, ennek egyszerű kezelésére, a

tengellyel való metszéspont megadására a VÉGTELEN érték is használható. Valamelyik tengellyel,

illetve az egyik koordinátasíkkal párhuzamos sík létrehozásánál használhatjuk a homogén ideális

pontok értékét is. (Az tengely végtelen távoli pontja BaseI, a j tengelyé BaseJ, a k tengelyé

pedig BaseK.) Másrészt a síkok ábrázolásánál a normális egységvektoruk talppontjaként az origóhoz

(illetve a homogén nullához: Base0-hoz) legközelebbi pontjukat (Near, illetve HomogeneousNear)

használjuk. Ez nagy jelentőségű pont, hiszen a forgatási tengely (az origó mellett, illetve homogén

koordinátás ábrázolásunknál Base0 mellett) ezen a ponton is keresztülmegy. Ötféle szöghöz tartozó

forgató kvaterniót is kiszámítunk a síkok normális egység-vektora (mint tengely) körüli forgatásra. A

homogén koordináták teljeskörű bevezetéseképpen sík objektumpéldányt tudunk a sík egyenletét

leíró együtthatónégyest tartalmazó kvaternióval (egy síkkvaternióval) is létrehozni, valamint a sík

objektum tulajdonságaként lekérdezhető az együtthatókat tartalmazó síkkvaternió is.

Pontpárként a szakasz, síkidomok közül pedig a háromszög, a négyszög, az ötszög és a hatszög

objektumot is leírtuk. Ezek deklarációjánál a valós részükkel is foglalkoztunk annyiban, hogy a

bennük szereplő kvaterniók mindegyikének ugyanahhoz a háromdimenziós térhez kell tartoznia, azaz

a valós részeknek azonosaknak kell lenniük. Egyébként ugyanis a kvaterniók különbségének abszolút

értékeként megadott oldalhosszak nem a háromdimenziós, hanem a négydimenziós térben értendő

hosszak volnának. ( Ha egy objektum létrehozásánál kvaternióként megadott pontok nem tartoznak

egyazon háromdimenziós világhoz, helyettük -t tárolunk: az objektum határozatlanná válik. )

Page 53: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

51

Térbeli szerkesztések ( F# )

module public Kvaterniós_szerkesztések open System open Kvaternió_műveletek // Ez a modul a kvaterniók képzetes részének háromdimenziós terében // létrehozott geometriai alakzatok (pontok, egyenesek és síkok) // segítségével elvégezhető szerkesztések eredményének számítására // szolgál. A Kvaternió_műveletek modulban leírt módon lehet a // geometriai alakzatokat létrehozni: az objektumok tulajdonságainak // leírását is ott találják. // Síkon keressük az egyenes döféspontját. A döféspontot kvaternió // képzetes részeként adja vissza. (Ha nincs döféspont, mert az // egyenes a síkkal párhuzamos, vagy az egyenes a síkra illeszkedik, // ezért minden pont döféspont lehetne, a visszaadott kvaternió a // valós egység: Base0 - Homogén koordinátákkal a közönséges nulla). let public SíkotDöfEgyenes( Sík : Plane, Egyenes : Line ) : Kvaternió = let vetület = Vector.dotProd(Sík.NormVect, Egyenes.Vect) if Math.Abs(vetület) < PICINY then Base0 else let lambda = Vector.dotProd( Sík.Point.Vect - Egyenes.Point1.Vect, Sík.NormVect ) / vetület Kvaternió( EGY, Egyenes.Point1.Vect + Egyenes.Vect.Scal(lambda) ) // Szinte ugyanaz a homogén koordinátás változat. Ekkor a síkot egy // kvaternió írja le, ennek négydimenziós skaláris szorzatát lehet // képezni az egyenes egyik pontjával (amelytől az egyenes irány- // vektorának lambdaszorosára van a döféspont). Ha az egyenes s // síkkal párhuzamos, a döféspont az egyenes ideális pontja. let public HomogénSíkotDöfEgyenes( HomogénSík : Kvaternió, Egyenes : Line ) : Kvaternió = let osztó = Vector.dotProd(HomogénSík.Vect, Egyenes.Vect) if Math.Abs(osztó) < PICINY then Kvaternió(NULLA, Egyenes.Vect) else let lambda = -Kvaternió.dotProd4(HomogénSík, Egyenes.Point1) / osztó Kvaternió( EGY, Egyenes.Point1.Vect + Egyenes.Vect.Scal(lambda) ) // Két sík metszésvonalát határozza meg ez a függvény. Ha a két sík // párhuzamos (vagy egybeesik), akkor olyan egyenest ad vissza, // amelyik határozatlan: mindkét meghatározó pontja a nulla kvaternió.

Page 54: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

52

let public SíkokMetszésvonala( Sík1: Plane, Sík2: Plane ) : Line = let metszésvonalVektor = Sík1.NormVect * Sík2.NormVect if metszésvonalVektor.abs < PICINY then Line(O,O) else let síkPontja = if (Sík1.Point - Sík1.Near).abs < PICINY then new Kvaternió(EGY,Sík1.Near.Vect + metszésvonalVektor) else Sík1.Point let mutable síkbeliEgyenes = new Line(síkPontja, Sík1.Near) let mutable döfésPont = SíkotDöfEgyenes(Sík2, síkbeliEgyenes) if döfésPont.Vect.abs < PICINY then let síkbeliPont = Sík1.Rot90Kvat * síkPontja * Sík1.Rot90Kvat.Konj síkbeliEgyenes <- new Line(síkbeliPont, Sík1.Near) döfésPont <- SíkotDöfEgyenes(Sík2, síkbeliEgyenes) Line( döfésPont, metszésvonalVektor ) // A síkokat ilyenkor egy-egy kvaternió írja le. A Síkok kvaternió- // jában a komplex vektor rész a sík háromdimenziós térbeli // normálisa. Ezek vektoriális szorzata most is a metszésvonal

Page 55: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

53

// irányát adja meg. Felveszünk egy harmadik síkot a két síkra // merőlegesen. Mivel ez a sík tetszőleges ponton átmenő lehet, // a síkot leíró kvaternió valós részét 1-re állítjuk (ez volna a // sík Base0-tól mért távolsága). A három sík metszéspontja a két // sík metszésvonalának pontja. Ezt és a metszésvonal irányát fel- // használva a metszésvonal előáll. Ha a két sík egymással // párhuzamos, akkor a síkokhoz tartozó végtelen távoli egyenes, az // ideális egyenes volna a megoldás. Ennek pontja a normális // irányhoz rendelt ideális pont, iránya pedig meghatározatlan. let public HomogénSíkokMetszésvonala ( Sík1: Kvaternió, Sík2: Kvaternió ) : Line = let metszésvonalVektor = Sík1.Vect * Sík2.Vect if metszésvonalVektor.abs < PICINY then Line(Kvaternió(NULLA, Sík1.Vect),Vector()) // párhuzamosak else let Sík3 = Kvaternió(EGY,metszésvonalVektor) let egyenesPontja = Kvaternió.VectProd4(Sík1, Sík2, Sík3) let normáltPont = if (egyenesPontja.re - EGY) < PICINY then egyenesPontja else egyenesPontja.Scal( EGY / egyenesPontja.re ) Line( normáltPont, metszésvonalVektor )

Page 56: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

54

// Egyenesek egymáshoz legközelebbi pontjának meghatározása. let public EgyenesekTávolsága( Egyik: Line, Másik: Line ) : double = let normálVektor = Egyik.Vect * Másik.Vect if normálVektor.abs < PICINY then // Egy síkban a két egyenes: let ferdeVektor = ( Egyik.Point1 - Másik.Point1 ).Vect let vetületVektor = Egyik.Vect.Scal(Vector.dotProd(Egyik.Vect, ferdeVektor)) let merőlegesVektor = ferdeVektor - vetületVektor merőlegesVektor.abs else // Kitérő egyenesek: let Sík = new Plane(Egyik.Point1, normálVektor) let döfésPont = SíkotDöfEgyenes(Sík, Line(Másik.Point1, normálVektor)) ( döfésPont - Másik.Point1 ).abs // Egyenesek metszéspontjának meghatározása. let public Metszéspont( Egyik: Line, Másik: Line ) : Kvaternió = let normálVektor = Egyik.Vect * Másik.Vect if normálVektor.abs < PICINY then Base0 else let Sík = new Plane( Egyik.Point1, Egyik.Vect * normálVektor ) let döfésPont = SíkotDöfEgyenes(Sík, Másik) if ( döfésPont - Egyik.Point1 ).abs < PICINY then döfésPont elif ( ( döfésPont - Egyik.Point1 ).Vect * Egyik.Vect ).abs < PICINY then döfésPont else Base0 // Egyenesek metszéspontját meghatározó függvényünk homogén // koordinátás változata (A nem metszők "metszéspontja" más.) let public HomogénMetszéspont ( Egyik: Line, Másik: Line ) : Kvaternió = let normálVektor = Egyik.Vect * Másik.Vect if normálVektor.abs < PICINY then Kvaternió(NULLA, Egyik.Vect) // párhuzamos egyenesek! else let Sík = new Plane( Egyik.Point1, Egyik.Vect * normálVektor ) let döfésPont = SíkotDöfEgyenes(Sík, Másik) if ( döfésPont - Egyik.Point1 ).abs < PICINY then döfésPont elif ( ( döfésPont - Egyik.Point1 ).Vect * Egyik.Vect ).abs < PICINY then döfésPont else Kvaternió(NULLA, normálVektor) // kitérő egyenesek! // Egy síkban megadott (fok) nagyságú szög szerkesztése: let public SzögSzerkesztés ( Sík : Plane, fok: Double) : (Line * Line) =

Page 57: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

55

if (Sík.Near - Sík.Point).abs < PICINY then ( Line(O, O), Line(O,O) ) // Határozatlan feladat! else let forgatóKvaternió = new Kvaternió( Sík.NormVect, fok ) let konjKvaternió = forgatóKvaternió.Konj let újPont = forgatóKvaternió * Sík.Point * konjKvaternió ( new Line(Sík.Point, Sík.Near ), new Line( Sík.Near,újPont))

// Vektor felbontása két összetevőjére. (Paraméterként a vektoron // kívül megadva a felbontási irányok egyike vektorként.) let public ÖsszetevőkreBontás ( eredőVektor: Vector, egyikVektor: Vector ) : ( Vector * Vector ) = let egyikEgység = egyikVektor.Unit let párhuzamosÖsszetevő = egyikEgység.Scal(Vector.dotProd(eredőVektor,egyikEgység)) let merőlegesÖsszetevő = eredőVektor - párhuzamosÖsszetevő ( párhuzamosÖsszetevő, merőlegesÖsszetevő )

Page 58: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

56

// A sík két pontjától megadott távolságra levő pontjai: let public KétPonttólMegadottTávolságra(Pont1: Kvaternió, Sík: Plane, táv1: double, táv2: double ): (Kvaternió * Kvaternió)= if Math.Abs( Kvaternió.dotProd4( Pont1, Sík.HomogeneousPlane )) > PICINY then ( Kvaternió( NULLA, Sík.NormVect), Kvaternió( NULLA, Sík.NormVect.Scal( MINEGY )))

else let táv = (Sík.Point.Vect - Pont1.Vect).abs let x = táv / KETTŐ - ( táv2 - táv1 ) * ( táv2 + táv1 ) / ( KETTŐ * táv ) let y = Math.Sqrt( táv1 * táv1 - x * x ) let párhuzamosIrány = (Sík.Point.Vect - Pont1.Vect).Unit let merőlegesIrány = párhuzamosIrány * Sík.NormVect

Page 59: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

57

( Kvaternió( EGY, Pont1.Vect + párhuzamosIrány.Scal(x) + merőlegesIrány.Scal(y) ), Kvaternió( EGY, Pont1.Vect + párhuzamosIrány.Scal(x) - merőlegesIrány.Scal(y) ) ) // Kvaternióval megadott pontok középpontos tükrözése: let public KözéppontosTükrözés( középPont: Kvaternió, Pont: Kvaternió ) : Kvaternió = Kvaternió( EGY, középPont.Vect.Scal( KETTŐ ) - Pont.Vect )

Térbeli alakzatok leírása homogén koordinátákkal

Előrebocsátjuk, hogy az egyenesek kezelésében okozza a legkisebb változást a homogén koordináták

alkalmazása. Az egyenesnek van egy (homogén koordinátáit tartalmazó kvaternióval leírt) rögzített

pontja, továbbá van irányvektora. Az egyenes változó pontja homogén koordinátás kvaterniójának a

valós része a rögzített pont valós része (az itt használt homogén koordináták szerint 1), képzetes

része pedig a rögzített pont vektor összetevőjének és az irányvektor valahányszorosának összege. (Az

így leírt egyenes a Line objektum egy példánya lehet.)

A pontok közönséges, illetve ideális pontok lehetnek. A pontok homogén koordinátáit tartalmazó

kvaternió valós része eszerint 1 vagy 0 (pontkvaternió).

A síkok homogén koordinátás környezetbeli leírására olyan kvaterniót (síkkvaterniót) használunk,

amelynek a vektor összetevője egységvektor, valós része pedig negatív szám. Egyedül síknál merül

fel az a kérdés, hogy mi a kapcsolat a Plane objektumpéldány és ezt a síkot leíró síkkvaternió között?

Ugyanis a síkot így is – úgy is – ábrázolhatjuk. A két ábrázolási forma között biztosítottuk az átjárást:

mindig azt a formát alkalmazhatjuk, amelyet éppen egyszerűbbnek tartunk. A Plane objektumnak

van HomogenouosPlane tulajdonsága, amely a síkkvaternióját adja vissza, másrészt van olyan Plane

konstruktor, amelynek egyetlen síkkvaternió a paramétere. Ezért az előző fejezetben bemutatott

függvénykészletben is több függvénynek elkészítettük az egyik sík-ábrázolási formára és a másik sík-

ábrázolási formára alkalmazott változatát is. Például:

SíkokMetszésvonala( Sík1: Plane, Sík2: Plane ) : Line

HomogénSíkokMetszésvonala( Sík1: Kvaternió, Sík2: Kvaternió ) : Line

Másfelől, működik az alábbi programrészlet is (Plane objektumból síkkvaternióvá alakítás):

let Pont = Kvaternió( EGY, ÖT, ÖT, ÖT ) // Pont koordinátás megadása

let Sík = Plane( HÁROM, NÉGY, EGY ) // Sík a tengelypontjaiból

let homogénSík = Sík.HomogenouosPlane // Ugyanaz kvaternióval…

let távolság = Kvaternió.dotProd4( homogénSík, Pont ) // Erre jó…

Page 60: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

58

De visszafelé is működik az átalakítás! Tehát síkkvaterniójával leírt síkból Plane síkot hozunk létre:

let másikSík = Plane( homogénSík ) // Kvaternióból Plane példány

let síkPontja = másikSík.Point // másikSík Plane példány, van Point-ja

A síkkvaternió képzetes része éppen a sík normálvektora, tehát ugyanazt a vektort kapjuk

másikSík.NormVect és homogénSík.Vect hivatkozással.

Hasonlóképpen a Kvaternió_műveletek modulban elhelyezett ábrák mindegyikén láthatják, hogy a

felvett síkok metszéspontját is meghatároztuk és rárajzoltuk az ábrára. Ott így néz ki a kódrészlet:

let közösPont = Kvaternió.VectProd4( SíkG.HomogeneousPlane, SíkH.HomogeneousPlane, SíkM.HomogeneousPlane )

A kvaternió alakban megadott síkegyenletek négydimenzós vektorszorzata közvetlenül kiadja a

metszéspont kvaterniót (a valós rész mindjárt 1 értékű, mert a vektoriális szorzást végző függvény így

normálja az eredményt, ha a függvény bemenő paraméterértékei nem pontkvaterniók).

Teljes programot mutatunk, amely három pont homogén koordinátás kvaterniójából kiindulva

határozza meg a pontokra illeszkedő síkot. Egy negyedik, nem a síkon levő pontnak pedig kiszámítja a

síktól mért távolságát. (A távolság előjeles, a síknak a Base0-t is tartalmazó térfelében negatív.)

Felirat( "Homogén koordináták", 0, 500, 20, Color.DarkCyan ) HomogénKoordinátaHáttér(Color.Silver) // A sík három pontja homogén (kvaternió-) koordinátákkal: let P = Kvaternió( EGY, ÖT, HÁROM, HÁROM ) PontRajz(Color.Lime, 2, P) KvaternióFelirat( "p", P, 0, -20, 20, Color.Lime ) KódFelirat( "p: Kvaternió( EGY, ÖT, EGY, HÁROM )", 0, 650, 16, Color.Lime ) let Q = Kvaternió( EGY, MINEGY, ÖT, EGY ) PontRajz( Color.Cyan, 2, Q ) KvaternióFelirat( "q", Q, -30, -15, 20, Color.Cyan ) KódFelirat( "q: Kvaternió (EGY, MINEGY, ÖT, EGY )", 0, 680, 16, Color.Cyan ) let R = Kvaternió( EGY, NÉGY, -MÁSFÉL, FÉL ) PontRajz( Color.Magenta, 2, R ) KvaternióFelirat( "r", R, 0, -10, 20, Color.Magenta ) KódFelirat("r: Kvaternió(EGY, NÉGY, -MÁSFÉL, FÉL )", 0, 710, 16, Color.Magenta ) // Egy, a síkra nem illeszkedő pont homogén koordinátákkal: let T = Kvaternió( EGY, -HÁROM, -KETTŐ, HÁROM ) PontRajz( Color.Tomato, 2, T ) KvaternióFelirat( "t", T, 0, -40, 20, Color.Tomato ) KódFelirat( "t: Kvaternió( EGY, -HÁROM, -KETTŐ, HÁROM )", 0, 740, 16, Color.Tomato )

Page 61: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

59

// A sík egyenletének meghatározása (Kvaternió.VectProd4 hívásával): let S = Kvaternió.VectProd4( P, Q, R ) // Ellenőrzés: illeszkedő pontnál Kvaternió.dotProd4(S,.) = 0 let próbaP = Kvaternió.dotProd4( S, P ) let próbaQ = Kvaternió.dotProd4( S, Q ) let próbaR = Kvaternió.dotProd4( S, R ) // A képzetes tengelyeken található sík-metszéspontok meghatározása: let iTengelyen = -S.re / S.imI // Ti = Kvaternió(1, iTengelyen, 0, 0) let jTengelyen = -S.re / S.imJ // Tj = Kvaternió(1, 0, jTengelyen, 0) let kTengelyen = -S.re / S.imK // Tk = Kvaternió(1, 0, 0, kTengelyen) // A Plane objektum Sík példányának konstruálása:

let Sík = Plane( iTengelyen, jTengelyen, kTengelyen )

Page 62: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

60

SíkRajz( Color.SteelBlue, Sík, 5.0 ) // A sík rajzoltatása: // A síkra nem illeszkedő pont síktól való távolságának meghatározása // a négydimenziós skaláris szorzat felhasználásával: let távolság = Kvaternió.dotProd4( S, T ) // A sík távolságának ellenőrző meghatározása "józan paraszti ésszel": let döfésPont = SíkotDöfEgyenes( Sík, Line( T, Sík.NormVect ) ) let táv = ( T - döfésPont ).abs // A sík távolsága a Base0 ponttól: let döfő = SíkotDöfEgyenes(Sík, Line(Base0, Sík.NormVect)) let síktáv = (Base0 - döfő).abs // Kiírások a rajzfelületre...

Az ábrán kiírt eredményekből is látható, hogy az síkkvaterniónak a komplex vektora a sík normál-

vektora. A síknak a képzetes tengelyekkel való metszéspontjait a szemléltetés érdekében egy, a síkra

simuló háromszögsorozat megrajzolásához számítottuk ki. Ellenőriztük is, hogy a sík együttható-

kvaterniójának és egy, a síkra nem illeszkedő pont kvaterniójának a négydimenziós skaláris

szorzata abszolút értéke szerint a pont és a sík (előjeles) távolságát szolgáltatja.

A három (pontkvaterniójával adott) pontra illeszkedő sík síkkvaterniójának meghatározása, valamint

a három (síkkvaterniójával adott) sík közös pontja pontkvaterniójának meghatározása ugyanaz a

matematikai probléma (dualitás tétele).

Az alábbi példában három sík közös pontjának homogén koordinátáit számoljuk VectProd4 -gyel.

HomogénKoordinátaHáttér( Color.Silver ) // Kiindulásul homogén koordinátáival megadunk öt pontot: let P = Kvaternió(EGY, ÖT, EGY, HÁROM) let Q = Kvaternió(EGY, MINEGY, ÖT, NÉGY) let R = Kvaternió(EGY, NÉGY, -MÁSFÉL, FÉL) let T = Kvaternió(EGY, -HÁROM, -KETTŐ, HÁROM) let U = Kvaternió(EGY, TIZENKETTŐ, -HÉT, KETTŐ) // A síkokat három-három fenti pontra illesztjük: let S1 = Kvaternió.VectProd4(P, Q, R) let S2 = Kvaternió.VectProd4(T, Q, R) let S3 = Kvaternió.VectProd4(P, U, R)

Page 63: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

61

let H1 = Triangle(P, Q, R) // Láthatóvá tesszük a síkokat: Háromszög(Pens.SteelBlue, H1, 2.0) let H2 = Triangle(T, Q, R) Háromszög(Pens.Lime, H2, 1.2) let H3 = Triangle(P, U, R) Háromszög(Pens.Orange, H3, 1.8)

// Meghatározzuk a három sík metszésponti homogén koordinátáit: let mutable Pont = Kvaternió.VectProd4(S1, S2, S3)

A három síkot úgy vettük fel, hogy közös pontjuk legyen. Ezért ellenőrizni tudjuk az eredményt: a

síkok metszéspontjának koordinátái koordinátáival megegyeznek.

Page 64: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

62

A sík homogén koordinátás egyenletének szerkezetét foglaljuk össze. A síkkvaternió képzetes része a

sík normálisa. (A négydimenziós skaláris szorzásnál a képzetes résznek és a pontok háromdimenziós

térbeli helyvektorának a skaláris szorzata ugyanazt az értéket adja a síkot kifeszítő pontok esetén: a

koordinátarendszer kezdőpontjának, amely hol az origo, hol a Base0 pont és a sík Near pontjának a

távolságát. Persze, mert ez a skalárszorzat mindig a síkon található pont helyvektorának vetülete a

normális irányára.) A Kvaternió_műveletek modulban ugyanis a sík egyenletének három pontja

kvaterniójából való meghatározásánál ( #Négydimenziós_vektoriális_szorzat ) a kapott kvaternió

képzetes részének abszolút értékével el is osztottuk a kvaterniót, így elértük, hogy a képzetes rész a

sík normális egységvektora. A sík együtthatóit tartalmazó kvaterniót még úgy alakítjuk ott a létre-

hozásnál, hogy valós része negatív legyen. (Mivel a sík együtthatói csak az arányosság erejéig

meghatározottak, tetszőleges állandóval szorozhatjuk, oszthatjuk valamennyi együtthatót, a sík nem

változik.) Ha a sík kvaterniójának valós része negatív, akkor a pontok síktól mért távolságának előjel-

szabálya a következő: a sík kvaterniójának és egy, a síkra nem illeszkedő homogén koordinátájú pont

kvaterniójának négydimenziós skaláris szorzata a pont síktól mért távolságát úgy előjelezi, hogy a

távolság a Base0 pontot tartalmazó térfélben negatív, a sík által kijelölt másik térfélben pozitív. A

Base0 pont távolsága a síktól a sík kvaterniójának valós része, hiszen Base0 = Kvaternió(1, 0, 0, 0).

Egy külső pont távolsága a síktól a pont Base0-ból induló háromdimenziós helyvektora és a

normális irányú egységvektor skaláris szorzatából ( ), valamint a Base0 pont síktól mért ( DV )

távolságából áll. Mivel a pont valós része 1, ez éppen a négydimenziós skaláris szorzat.

Page 65: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

63

Kvaterniók programozott megjelenítése ( F# )

Ebben a fejezetben a képernyőn való megjelenítéshez szükséges több függvényt ismertetünk. A

fejezet tanulmányozását nyugodtan kihagyhatja az, akit nem érdekelnek a programozási részletek.

Másrészt szeretném megmutatni, hogy milyen egyszerű a programozás, hogy mennyire könnyű

például a koordinátatengelyek helyzetén változtatni az alábbi szerkezetekkel.

let private tér4Ox : int = 450 // Az origó helye az 1000 x 1000 let private tér4Oy : int = 600 // képpontnyi rajzterületen belül let private tér4Oz : int = 0 // Mélységi koordináta (láthatóság)

let private tér4Rx : int = 820 // A valós tengelyvég koordinátái let private tér4Ry : int = 780 let private tér4Rz : int = +100

let private tér4Ix : int = 830 // Az i képzetes tengely végpontjának let private tér4Iy : int = 450 // elhelyezése a képen ... let private tér4Iz : int = +100

let private tér4Jx : int = 650 // A j képzetes tengely végpontjának let private tér4Jy : int = 320 // elhelyezése a képen ... let private tér4Jz : int = -200

let private tér4Kx : int = 250 // A k képzetes tengely végpontjának let private tér4Ky : int = 220 // elhelyezése a képen ... let private tér4Kz : int = +100

let private SKÁLA = double 6.0 // Végpont az egység hányszorosa // Ez a függvény a kvaternió koordinátából kép koordinátát számol. // A képi koordináta egy hármas (tuple), amelyben a vízszintes és a // függőleges képi koordináta mellett mindig egy mélységi koordinátát // is kapunk. A mélységi koordinátának csak a láthatóság eldöntésekor // van szerepe (máskor általában ezt a koordinátát eldobjuk). let KvaternióPont( fKvaternió : Kvaternió ) : ( int * int * int ) = (tér4Ox + int((fKvaternió.re * double(tér4Rx - tér4Ox) + fKvaternió.imI * double(tér4Ix - tér4Ox) + fKvaternió.imJ * double(tér4Jx - tér4Ox) + fKvaternió.imK * double(tér4Kx - tér4Ox)) / SKÁLA), tér4Oy + int((fKvaternió.re * double(tér4Ry - tér4Oy) + fKvaternió.imI * double(tér4Iy - tér4Oy) + fKvaternió.imJ * double(tér4Jy - tér4Oy) + fKvaternió.imK * double(tér4Ky - tér4Oy)) / SKÁLA), tér4Oz + int((fKvaternió.re * double(tér4Rz - tér4Oz) + fKvaternió.imI * double(tér4Iz - tér4Oz) + fKvaternió.imJ * double(tér4Jz - tér4Oz) + fKvaternió.imK * double(tér4Kz - tér4Oz)) / SKÁLA))

Page 66: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

64

// A kvaternió koordinátákból precízen számoltuk ki az előző függvényben // a képi koordinátákat. Meghagytunk még valamiféle mélységi koordinátát // is, amely viszont lehetőséget nyújt arra, hogy térbeli ábránk vetületét // kissé átalakítsuk, perspektivikussá tegyük. A függvényt módosítjuk: let KvaternióPont(fKvaternió : Kvaternió) : (int * int * int ) = let deltaZ = (fKvaternió.re * double(tér4Rz - tér4Oz) + fKvaternió.imI * double(tér4Iz - tér4Oz) + fKvaternió.imJ * double(tér4Jz - tér4Oz) + fKvaternió.imK * double(tér4Kz - tér4Oz)) / SKÁLA let deltaX = (fKvaternió.re * double(tér4Rx - tér4Ox) + fKvaternió.imI * double(tér4Ix - tér4Ox) + fKvaternió.imJ * double(tér4Jx - tér4Ox) + fKvaternió.imK * double(tér4Kx - tér4Ox)) / SKÁLA * 2800.0 / (3000.0 - deltaZ) let deltaY = (fKvaternió.re * double(tér4Ry - tér4Oy) + fKvaternió.imI * double(tér4Iy - tér4Oy) + fKvaternió.imJ * double(tér4Jy - tér4Oy) + fKvaternió.imK * double(tér4Ky - tér4Oy)) / SKÁLA * 2800.0 / (3000.0 - deltaZ) (tér4Ox + int(deltaX), tér4Oy + int(deltaY), tér4Oz + int(deltaZ)) // Az ábrán piros színnel a kocka perspektív vetítéses változatát látják:

A kockákat összehasonlítva láthatjuk, hogy a „szabályos”-nak szerkesztett fekete kockához képest

szabályosabbnak látjuk a perspektivikusan torzított piros változatot.

Page 67: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

65

A perspektív vetítésnél egyenlő hosszúságú szakaszok közül a távolabbiak megrövidülnek, ezért a

drótvázas kocka képét az alábbi ábra vetítősíkján láthatóhoz hasonlónak tapasztaljuk:

A perspektív vetítés számításának menete a következő ábra alapján látható be:

A programban táv = 2800, bázis = 3000. z mélységi koordináta a -200 - +200 határok közötti értékű.

Az O jelű pontba képzelve a szemünket, a tőle távolságra levő síkon a tárgy A, B, C, D pontjainak

képét A’, B’, C’, D’-nek látnánk. A síkon az x és y koordináta egyaránt a képlet szerint módosul.

Page 68: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

66

// Töröttvonalakat a képen Point-ok sorozataként adunk meg. Segítség // ebben ez a függvény, amely a kvaternió koordinátákból (a mélységi // koordináta eldobásával) rögtön Point-ot ad vissza: let KvaternióKépen( fKvaternió : Kvaternió ) : Point = let mutable lKépPont : Point = new Point(0,0) let ( átmenőX, átmenőY, átmenőZ ) = KvaternióPont( fKvaternió ) lKépPont.X <- átmenőX lKépPont.Y <- átmenőY lKépPont // A kvaterniók vektor komponenseként leírt térben két pont egyenes // vonallal való összekötését végzi: let Összeköt( fToll : Pen, fEzt : Kvaternió, fEzzel : Kvaternió ) = let ( lEztX, lEztY, lEztZ ) = KvaternióPont( fEzt ) let ( lEzzelX, lEzzelY, lEzzelZ ) = KvaternióPont( fEzzel ) ábra.DrawLine( fToll, lEztX, lEztY, lEzzelX, lEzzelY ) // Képi koordinátáival megadott pontban a pontot kijelölő kis // keresztet rajzol: let Kereszt( fSzín : Color, fVastag : int, fX : int, fY : int ) = ábra.DrawLine( new Pen(fSzín, float32 fVastag), fX - VON, fY - VON, fX + VON, fY + VON ) ábra.DrawLine( new Pen(fSzín, float32 fVastag), fX - VON, fY + VON, fX + VON, fY – VON ) // Kvaternióval megadott pontot a rajzon kereszttel megjelöl: let PontRajz( fSzín : Color, fVastag : int, Pont : Kvaternió ) = let ( pontX, pontY, pontZ ) = KvaternióPont( Pont ) Kereszt( fSzín, fVastag, pontX, pontY ) // Kvaternióval kijelölt pontok között nyíl végződésű vektort rajzol: let VektorRajz ( fSzín : Color, fVastag : int, fVég : Kvaternió, fHegy :Kvaternió ) = let lToll : Pen = new Pen( fSzín, float32 fVastag ) let ( lVégX, lVégY, lVégZ ) = KvaternióPont( fVég ) let ( lHegyX, lHegyY, lHegyZ ) = KvaternióPont( fHegy ) let lSzét : double = 10.0 let lCsúcs : double = 20.0 ábra.DrawLine( lToll, lHegyX, lHegyY, lVégX, lVégY) let lTg = double(lVégY - lHegyY) / double(lVégX - lHegyX) let lSzög : double = Math.Atan(lTg) * ÍVBŐLFOK let lXCsúcs = if lVégX < lHegyX then -lCsúcs else lCsúcs

Page 69: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

67

let mutable lYCsúcs = if lVégY < lHegyY then -lCsúcs else lCsúcs if lSzög < NULLA then lYCsúcs <- -lYCsúcs ábra.DrawLine( lToll, lHegyX, lHegyY, lHegyX + int(lXCsúcs * Math.Cos((lSzög + lSzét) * FOKBÓLÍV)), lHegyY + int(lYCsúcs * Math.Sin((lSzög + lSzét) * FOKBÓLÍV)) ) ábra.DrawLine( lToll, lHegyX, lHegyY, lHegyX + int(lXCsúcs * Math.Cos((lSzög - lSzét) * FOKBÓLÍV)), lHegyY + int(lYCsúcs * Math.Sin((lSzög - lSzét) * FOKBÓLÍV)) ) // Line osztály példányaként megadott egyenest a rajzon vonallal és // vonal irányú egységvektorral kijelöl: let EgyenesRajz ( fEgyenesSzín : Color, fEgyenes : Line, fSzakaszMéret : Double ) = let Egyik : Kvaternió = fEgyenes.Point1 let Másik : Kvaternió = fEgyenes.Point2 let irányVektorRajz : Vector = fEgyenes.Vect Összeköt( new Pen(fEgyenesSzín, float32 1.0), Egyik - (Másik - Egyik).Scal(fSzakaszMéret), Másik + (Másik - Egyik).Scal(fSzakaszMéret) ) PontRajz( fEgyenesSzín, 2, Egyik ) PontRajz( fEgyenesSzín, 2, Másik ) VektorRajz( fEgyenesSzín, 3, Egyik, Egyik + Kvaternió(NULLA, irányVektorRajz) )

Hasáb forgatása (Visual Basic program)

Ebben a fejezetben olyan programrészletet mutatunk be, amellyel az síkon elhelyezkedő alapú,

a irányban feltornyosuló hasábot forgatunk ( tengely körül 90°-kal). A forgatást elvégző

ÁltalánosForgatás nevű szubrutin bemenő paraméterei között nem csak a térbeliPontok

kvaterniótömb szerepel, hanem megadható a forgatás tengelye és szöge is.

' A kocka 8 sarokpontjához tartozó kvaternió: Dim négyzetalapúHasáb() As Kvaternió_műveletek.Kvaternió = { New Kvaternió_műveletek.Kvaternió(1.0, 0.0, 0.0, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 0.0, 3.5, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 3.5, 3.5, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 3.5, 0.0, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 0.0, 0.0, 3.5), New Kvaternió_műveletek.Kvaternió(1.0, 0.0, 3.5, 3.5), New Kvaternió_műveletek.Kvaternió(1.0, 3.5, 3.5, 3.5), New Kvaternió_műveletek.Kvaternió(1.0, 3.5, 0.0, 3.5) }

Page 70: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

68

Dim toll1 = New Pen(Color.Red, 3) Dim toll2 = New Pen(Color.Lime, 2) ÁltalánosForgatás( négyzetalapúHasáb, New Vector(NULLA, NULLA, EGY), Math.PI / KETTŐ, toll1, toll2 )

' A hatszög alapú hasáb 12 sarokpontjához tartozó kvaternió: Dim hatszögalapúHasáb() As Kvaternió_műveletek.Kvaternió = { New Kvaternió_műveletek.Kvaternió(1.0, 0.0, 3.045, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 1.75, 0.0, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 5.25, 0.0, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 7.0, 3.045, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 5.25, 6.09, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 1.75, 6.09, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 0.0, 3.045, 3.5), New Kvaternió_műveletek.Kvaternió(1.0, 1.75, 0.0, 3.5), New Kvaternió_műveletek.Kvaternió(1.0, 5.25, 0.0, 3.5), New Kvaternió_műveletek.Kvaternió(1.0, 7.0, 3.045, 3.5), New Kvaternió_műveletek.Kvaternió(1.0, 5.25, 6.09, 3.5), New Kvaternió_műveletek.Kvaternió(1.0, 1.75, 6.09, 3.5) }

Page 71: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

69

' Egy, a sarokponti kvaternióival adott hasábot a tollal kirajzoltat: Sub HasábRajz( ByVal fToll As Pen, ByVal sarokPontok() As Kvaternió_műveletek.Kvaternió) Dim lI, lJ, lK, lM As Integer Dim rajzPontok(UBound(sarokPontok)) _ As Tuple(Of Integer, Integer, Integer) For lI = LBound(sarokPontok) To UBound(sarokPontok) rajzPontok(lI) = KvaternióPont(sarokPontok(lI)) Next lI

Page 72: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

70

For lI = LBound(rajzPontok) To (UBound(rajzPontok) - 1) \ 2 lJ = lI + 1 If lJ > (UBound(rajzPontok) - 1) \ 2 Then lJ = 0 End If Összeköt(fToll, rajzPontok(lI), rajzPontok(lJ)) lK = (UBound(rajzPontok) - 1) \ 2 + lI + 1 lM = lK + 1 If lM > UBound(rajzPontok) Then lM = (UBound(rajzPontok) - 1) \ 2 + 1 End If Összeköt(fToll, rajzPontok(lK), rajzPontok(lM)) Összeköt(fToll, rajzPontok(lI), rajzPontok(lK)) Next lI End Sub ' A sarokponti kvaternióival adott hasábot eredeti helyzetében az ' „ős” tollal kirajzoltatja, majd az összetevőivel megadott vektor ' egységvektora körül az átadott szöggel elforgatja. Az elforgatott ' hasábot az átadott „új” tollal rajzoltatja a képernyőre. Sub ÁltalánosForgatás( _ ByVal fPontok() As Kvaternió_műveletek.Kvaternió, ByVal tengelyIrány As Kvaternió_műveletek.Vector, ByVal fSzög As Double, ByVal fŐsToll As Pen, ByVal fÚjToll As Pen) Dim lSzög As Double = fSzög / KETTŐ Dim lQ As Kvaternió_műveletek.Kvaternió = _ New Kvaternió_műveletek.Kvaternió( _ Math.Cos(lSzög), tengelyIrány.Unit.Scal(Math.Sin(lSzög))) Dim lQKonj As Kvaternió_műveletek.Kvaternió = lQ.Konj Dim újPontok(UBound(fPontok)) As Kvaternió_műveletek.Kvaternió HasábRajz(fŐsToll, fPontok) For lI = LBound(fPontok) To UBound(fPontok) újPontok(lI) = lQ * fPontok(lI) * lQKonj ' Forgatás Next lI HasábRajz(fÚjToll, újPontok) End Sub

Page 73: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

71

' A hasáb ábrája előtt az annak megrajzolásához szükséges eljárás- ' hívást láthatták. Itt a hatszög alapú hasáb j körüli 180 fokos ' elforgatását végző eljárás-hívást ismertetjük.

' Az eljárás paramétere a sarokpontokat leíró kvaternió-tömb, a ' forgatás tengelyének mint vektornak az összetevői, a forgatás szöge, ' valamint a rajzoltatáshoz használt tollak. ÁltalánosForgatás(hatszögalapúHasáb, New Vector(NULLA, EGY, NULLA), Math.PI, toll1, toll2)

Látványos ábrákhoz juthatunk a KörbeForgatás szubrutin használatával. Paraméterként lehet

megadni az induló rajz színét, valamint azt is, hogy hány lépésben történjék meg a körbeforgatás.

( Csak 100-nál kisebb lépésszám esetén váltogatja a színeket…)

Sub KörbeForgatás( _ ByVal térbeliPontok() As Kvaternió_műveletek.Kvaternió, ByVal tengelyIrány As Kvaternió_műveletek.Vector, ByVal fLépés As Integer, ByVal fŐsToll As Pen ) Dim lByte As New Random Dim lSzín As Color, lÚjToll As Pen = fŐsToll Dim lSzög As Double = Math.PI / fLépés Dim lQ As Kvaternió_műveletek.Kvaternió = _ New Kvaternió_műveletek.Kvaternió(Math.Cos(lSzög), _ tengelyIrány.Unit.Scal(Math.Sin(lSzög))) Dim lQKonj As Kvaternió_műveletek.Kvaternió = lQ.Konj HasábRajz(fŐsToll, térbeliPontok) For lN = 1 To fLépés If fLépés < 100 Then lSzín = Color.FromArgb( _ lByte.Next(256), lByte.Next(256), lByte.Next(256)) lÚjToll = New Pen(lSzín, 2) End If For lI = LBound(térbeliPontok) To UBound(térbeliPontok) térbeliPontok(lI) = lQ * térbeliPontok(lI) * lQKonj Next lI If lN = fLépés Then HasábRajz(New Pen(Color.Red, 2), térbeliPontok) Else HasábRajz(lÚjToll, térbeliPontok) End If Next lN End Sub

Page 74: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

72

A színek véletlen megválasztásával, a tengely körül 5 lépésben körbeforgatva:

KörbeForgatás( hatszögalapúHasáb, New Vector(EGY, NULLA, NULLA), 5, Pens.Red) De egészen más képet kapunk, ha 100 lépésben végezzük el a körbeforgatást a tengely körül: KörbeForgatás( hatszögalapúHasáb, New Vector(EGY, NULLA, NULLA), 100, Pens.Lime)

Page 75: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

73

Page 76: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

74

Síkidom forgatása (Visual Basic program)

Hasonlóan látványos eredményhez juthatunk, ha az – síkban szerkesztett síkidomot forgatjuk

meg az illetve a tengely körül úgy, hogy teljes körbeforgatást végezzünk el. A LapForgatás

nevű szubrutin a paraméterként átadott pontokkal leírt síkidommal éppen ezt végzi. Első

paraméterével azt tudjuk beállítani, hogy a forgatást az vagy a tengely körül hajtsa- e végre..

Dim keresztPontok() As Kvaternió_műveletek.Kvaternió = {New Kvaternió_műveletek.Kvaternió(1.0, 1.0, 3.0, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 3.0, 3.0, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 3.0, 1.0, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 4.0, 1.0, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 4.0, 3.0, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 6.0, 3.0, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 6.0, 4.0, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 4.0, 4.0, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 4.0, 6.0, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 3.0, 6.0, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 3.0, 4.0, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 1.0, 4.0, 0.0), New Kvaternió_műveletek.Kvaternió(1.0, 1.0, 3.0, 0.0)} Sub SíkbeliRajz( ByVal fToll As Pen, ByVal síkPontok() As Kvaternió_műveletek.Kvaternió) Dim lI, lJ As Integer Dim rajzPontok(UBound(síkPontok)) _ As Tuple(Of Integer, Integer, Integer) For lI = LBound(rajzPontok) To UBound(rajzPontok) rajzPontok(lI) = KvaternióPont(síkPontok(lI)) Next lI For lI = LBound(rajzPontok) To UBound(rajzPontok) lJ = lI + 1 If lJ > UBound(rajzPontok) Then lJ = 0 End If Összeköt(fToll, rajzPontok(lI), rajzPontok(lJ)) Next lI End Sub Sub LapForgatás( ByVal síkbeliPontok() As Kvaternió_műveletek.Kvaternió, ByVal fIKörül As Boolean, ByVal fLépés As Integer, ByVal fŐsToll As Pen, ByVal fÚjToll As Pen) Dim lI As Integer Dim lSzög As Double = Math.PI / fLépés Dim lCos As Double = Math.Cos(lSzög) Dim lSin As Double = Math.Sin(lSzög) Dim lQ As Kvaternió_műveletek.Kvaternió

Page 77: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

75

Dim lQKonj As Kvaternió_műveletek.Kvaternió If fIKörül Then lQ = New Kvaternió_műveletek.Kvaternió( _ lCos, lSin, NULLA, NULLA) Else lQ = New Kvaternió_műveletek.Kvaternió( _ lCos, NULLA, lSin, NULLA) End If lQKonj = lQ.Konj SíkbeliRajz(fŐsToll, síkbeliPontok) For lN = 1 To fLépés - 1 For lI = LBound(síkbeliPontok) To UBound(síkbeliPontok) síkbeliPontok(lI) = lQ * síkbeliPontok(lI) * lQKonj Next lI SíkbeliRajz(fÚjToll, síkbeliPontok) Next lN End Sub LapForgatás(keresztPontok, True, 150, Pens.Red, Pens.DarkBlue)

Page 78: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

76

Végül kör pontjait az egyenletéből előállítva két rajzot készítünk. Önmaga körül forgatjuk a kört, így gömböt, majd külső tengely körül forgatjuk a körlapot, így tóruszt rajzoltatunk: Dim ellipszisPontok(239) As Kvaternió_műveletek.Kvaternió For lI = 0 To 239 If lI < 60 Then ellipszisPontok(lI) = New Kvaternió_műveletek.Kvaternió( _ 1.0, HATVANAD * lI - EGY + ÖT, Math.Sqrt(EGY * EGY - _ (HATVANAD * lI - EGY) * (HATVANAD * lI - EGY)) + ÖT, 0.0) ElseIf lI < 120 Then ellipszisPontok(lI) = New Kvaternió_műveletek.Kvaternió( _ 1.0, HATVANAD * lI - EGY + ÖT, Math.Sqrt(EGY * EGY - _ (HATVANAD * lI - EGY) * (HATVANAD * lI - EGY)) + ÖT, 0.0)

Page 79: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

77

ElseIf lI < 180 Then ellipszisPontok(lI) = New Kvaternió_műveletek.Kvaternió( _ 1.0, HATVANAD * (239 - lI) - EGY + ÖT, _ - Math.Sqrt(EGY * EGY - (HATVANAD * (239 - lI) - EGY) _ * (HATVANAD * (239 - lI) - EGY)) + ÖT, 0.0) Else ellipszisPontok(lI) = New Kvaternió_műveletek.Kvaternió( _ 1.0, HATVANAD * (239 - lI) - EGY + ÖT, _ - Math.Sqrt(EGY * EGY - (HATVANAD * (239 - lI) - EGY) _ * (HATVANAD * (239 - lI) - EGY)) + ÖT, 0.0) End If Next lI LapForgatás(ellipszisPontok, False, 200, Pens.Red, Pens.Lime)

Page 80: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

78

Segédmennyiségek a szabályos testek rajzolásához

A szabályos testeket (tetraéder, oktaéder, hexaéder, dodekaéder, ikozaéder) a kvaterniók képzetes

része által leírt háromdimenziós térben szerkesztjük meg. Az a módszerünk, hogy a szabályos testek

oldalhosszúságából ( amelyet -val jelölünk ) kiindulva számítással határozzuk meg a szabályos test

többi, a test szerkesztéséhez szükséges szakaszának méretét. Például az alábbi ábra jelöléseit

használva a szabályos ötszögön végzünk számításokat, hogy a dodekaéder, illetve az ikozaéder

szerkesztésénél szükségessé váló szakaszhosszakat kiszámítsuk.

A szabályos ötszög valamennyi átlója ugyanakkora, ezt jelöltük c-vel. Az ábrán a zöld és piros színnel

festett két háromszög egybevágó, hiszen mindkettőnek mindegyik hegyesszöge 36°-os (a szabályos

ötszög körbe írható, és megegyező méretű húrokhoz megegyező méretű kerületi szögek tartoznak),

továbbá az alapjuk közös, ezért – . A kis piros háromszög és a nagy piros háromszög,

egymáshoz hasonló, hiszen a szögeik megegyeznek. Ebből a hasonlóságból következik, hogy

( – ) .

Az aránypárt (aranymetszés) átalakítva láthatjuk, hogy

Page 81: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

79

A másodfokú egyenlet megoldó képletét alkalmazva (és csak a pozitív gyököt megtartva):

( √ )

A szabályos ötszög b-vel jelölt (sárgára színezett) magasságát is számíthatjuk:

– (

)

Ebből

( √( √ ) ) ⁄

√ √

A szabályos ötszög körülírt körének középpontja a csúcsoktól egyenlő ( ) távolságra van. Ezt is

meghatározhatjuk ismeretében az előző oldali ábra alapján:

( ) (

)

( ) (

)

(

)

De a lap tetején levő képlet szerint a jobb oldal éppen :

(

)

√ √

√ √

(Csak megjegyzem, hogy az interneten a következő képletre bukkantam:

√ √

A két képlet azonossága bizonyítható, tehát ugyanazt az eredményt szolgáltatja mindkettő.)

A tetraéder rajzolását úgy kezdtük, hogy felvettünk egy síkot, amelyik mind a három tengelyt metszi,

és egy egyenest, amelyik nem párhuzamos a síkkal. A döféspontot a sík normálisa körül kétszer 120°-

kal elforgatva egyenlő oldalú háromszöget kaptunk, amely a tetraéder alapjául szolgált. (A forgatási

tengely a sík normálisa, amely a sík origóhoz legközelebbi pontján megy keresztül.) Az alap súlyponti

(a csúcstól a magasság kétharmadánál levő) vektorához az alapra merőleges (a sík normálisa

irányába mutató) p hosszúságú vektort hozzáadva jelöltük ki a tetraéder negyedik csúcspontját.

√ ( √

)

Dodekaéder készítésénél az alap ötszöghöz illeszkedő további szabályos ötszögek szerkesztését a

következő ábra alapján elvégzett számításokkal alapoztuk meg. A rajzon az előző oldalakon

bevezetett betűjelükkel hivatkoztunk a korábbiakban kiszámolt mennyiségekre.

Page 82: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

80

Az ábrán látható ’n’ a ’c’ oldalú szabályos háromszög magassága:

√ √

Az 5T1 háromszög síkja a 0. és 2. csúcspont közötti szakasz felezőpontján keresztül a szakaszra

merőleges sík. Ebben a síkban a T ponttól a fent meghatározott ’n’, az 1. csúcstól pedig ’a’ távolságra

levő 5. pontot kell meghatározni. A Kvaterniós_számítások modul KétPonttólMegadottTávolságra

nevű eljárásával állíthatjuk elő ezt a pontot. Ugyanígy kaptuk meg, persze, az U ponttól ’n’, a 0.

ponttól ’a’ távolságra levő 9. pont helyét is.

A 0., 1., 5., 9. pont síkjában szintén a fenti eljárással lehet az 5. és a 9. ponttól is ’a’ távolságra levő

14. pontot kijelölni.

Page 83: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

81

Ikozaédernél is egy szabályos ötszögből indulunk ki. A szabályos ötszög középpontját meghatározzuk,

majd ott a szabályos ötszög normálvektora irányába mutató, q hosszúságú vektort adunk hozzá a

pont helyvektorához, így állítjuk elő az ötszög fölötti csúcspontot. Az ötszög alapú gúla magasságát

egy olyan derékszögű háromszögből számítjuk, amelynek átfogója az ’a’ oldal, a szabályos test

oldalhossza, egyik befogója pedig az ’a’ -oldalú sokszög körülírt körének sugara ( r ).

√ √ (

√ √ )

√ √

A 4. csúcsnál türkiz (cyan) színnel berajzolt derékszögű háromszög alapja a szabályos ötszög rajza

alapján éppen ( ) Ugyanennek a háromszögnek az átfogója éppen az

oldalú egyenlő oldalú háromszög magassága, befogója pedig az ikozaéderben egymással

párhuzamosan (bár a saját lapján belül egymáshoz képest középpontosan tükrözötten elhelyezkedő)

két szabályos ötszög távolsága.

√( √

)

( )

( √ √ )

Page 84: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

82

Ezeknek a képleteknek az ismeretében az egyes szabályos testek szerkesztésének kódját, a

megszerkesztett drótváz-ábrát és a zárt felületű szabályos testek képét láthatják a következő fejezet

utáni oldalakon. (Másik lapon az eredeti test mellett annak egy elforgatottját is mutatjuk.)

Programozási szerkezetek a szabályos testekhez ( F# )

A szabályos testek konvexek, éleik egyforma hosszúságúak, lapjaik egybevágó, szabályos síkidomok:

háromszögek, négyzetek vagy ötszögek. Az alábbi táblázatban összefoglaljuk az adataikat:

Megnevezés Tetraéder Oktaéder Hexaéder Dodekaéder Ikozaéder

Csúcsok száma: 4 6 8 20 12

Élek száma: 6 12 12 30 30

Lapok száma: 4 8 6 12 20

Síkidom: háromszög háromszög négyzet ötszög háromszög

( Euler tétele: csúcsok száma + lapok száma = élek száma – 2. )

A programban a csúcsok helyét kvaterniókkal határozzuk meg. (Ezeknek a kvaternióknak a valós

része 0, a képzetes rész írja le a háromdimenziós teret). A csúcsok sorszámának megválasztása

természetesen önkényes, de az élek és lapok csúcs-sorszámokkal való megadásához célszerű a

sorszámozás menetét rögzíteni. Az alábbi adatstruktúrák találhatók a program kódjában:

// A tetraéder (számítandó) csúcspontjai: let tPontok : Kvaternió array = [| O; O; O; O|] // 0, 1, 2 az alap szabályos háromszög sorszámozása, 3 az alapon // kívül levő csúcs sorszáma. Ezzel a számozással az élek: let tetraéderÉlek : (int * int) array = [| (0, 1); (1, 2); (2, 0); (0, 3); (1, 3); (2, 3) |] // A lapok leírói ötösök, most csak három érték kihasznált. Mindegyik // nem kihasznált helyre -1 kerül: let tetraéderLapok : (int * int * int * int * int) array = [| (0, 1, 2, -1, -1); (0, 1, 3, -1, -1); (1, 2, 3, -1, -1); (2, 0, 3, -1, -1) |]

Page 85: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

83

// Az oktaéder csúcspontjait tároló tömb: let oPontok : Kvaternió array = [| O; O; O; O; O; O|] // Az oktaéder élei (0, 1, 2, 3 sorszám a kiinduló négyzethez...): let oktaéderÉlek : (int * int) array = [| (0, 1); (1, 2); (2, 3); (3, 0); (0, 4); (1, 4); (2, 4); (3, 4); (0, 5); (1, 5); (2, 5); (3, 5) |] // Az oktaéder lapjai (a -1 értékek helykitöltők...): let oktaéderLapok : (int * int * int * int * int) array = [| (0, 1, 4, -1, -1); (1, 2, 4, -1, -1); (2, 3, 4, -1, -1); (3, 0, 4, -1, -1); (0, 1, 5, -1, -1); (1, 2, 5, -1, -1); (2, 3, 5, -1, -1); (3, 0, 5, -1, -1) |] // A hexaéder (kocka) csúcsponjainak tárolására szolgáló tömb: let hPontok : Kvaternió array = [| O; O; O; O; O; O; O; O |] // A kocka élei: let hexaéderÉlek : (int * int) array = [| (0, 1); (1, 2); (2, 3); (3, 0); (4, 5); (5, 6); (6, 7); (7, 4); (0, 4); (1, 5); (2, 6); (3, 7) |] // A kocka lapjai (a -1-ek helykitöltők...): let hexaéderLapok : (int * int * int * int * int) array = [| (0, 1, 2, 3, -1); (4, 5, 6, 7, -1); (0, 1, 5, 4, -1); (1, 2, 6, 5, -1); (2, 3, 7, 6, -1); (3, 0, 4, 7, -1) |] // A dodekaéder csúcspontjait ez a tömb várja: let dPontok : Kvaternió array = [| O; O; O; O; O; O; O; O; O; O; O; O; O; O; O; O; O; O; O; O |] // A dodekaéder élei (0, 1, 2, 3, 4, illetve 5, 6, 7, 8, 9 a két // kiindulási szabályos ötszög csúcs-sorszáma): let dodekaéderÉlek : (int * int) array = [| (0, 1); (1, 2); (2, 3); (3, 4); (4, 0); (1, 5); (2, 6); (3, 7); (4, 8); (0, 9); (5, 10); (6, 10); (6, 11); (7,11); (7, 12); (8, 12); (8, 13); (9, 13); (9,14); (5,14); (15, 16); (16, 17); (17, 18); (18, 19); (19, 15); (11,15); (12, 16); (13, 17); (14, 18); (10, 19) |] // A dodekaéder lapjai:

Page 86: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

84

let dodekaéderLapok : (int * int * int * int * int) array = [| (0, 1, 2, 3, 4); (4, 8, 13, 9, 0); (3, 7, 12, 8, 4); (1, 2, 6, 10, 5); (0, 1, 5, 14, 9); (14, 18, 19, 10, 5); (3, 2, 6, 11, 7); (6, 10, 19, 15, 11); (11, 15, 16, 12, 7); (13, 17, 18, 14, 9); (17, 13, 8, 12, 16); (15, 16, 17, 18, 19) |] // Az ikozaédernek mindössze 12 csúcsa van, ide kerülnek: let iPontok : Kvaternió array = [| O; O; O; O; O; O; O; O; O; O; O; O |] // Az ikozaéder élei: (A 0, 1, 2, 3, 4 és az 5, 6, 7, 8, 9 sorszámok itt // is a két szerkesztési alap ötszöghöz kiosztva. 10 és 11 a sorszáma // annak a két csúcsnak, amely az ezekre emelt két gúla csúcsa.) let ikozaéderÉlek : (int * int) array = [| (0, 1); (1, 2); (2, 3); (3, 4); (4, 0); (0, 10); (1, 10); (2, 10); (3, 10); (4, 10); (0, 8); (1, 8); (1, 9); (2, 9); (2, 5); (3, 5); (3, 6); (4, 6); (4, 7); (0, 7); (5, 6); (6, 7); (7, 8); (8, 9); (9, 5); (5, 11); (6, 11); (7, 11); (8, 11); (9, 11) |] // Az ikozaéder 20 lapjának leírása (a -1-ek helykitöltők...):

let ikozaéderLapok : (int * int * int * int * int) array = [| (0, 1, 10, -1, -1); (1, 2, 10, -1, -1); (2, 3, 10, -1, -1); (3, 4, 10, -1, -1); (4, 0, 10, -1, -1); (0, 1, 8, -1, -1); (1, 2, 9, -1, -1); (2, 3, 5, -1, -1); (3, 4, 6, -1, -1); (4, 0, 7, -1, -1); (5, 6, 3, -1, -1); (6, 7, 4, -1, -1); (7, 8, 0, -1, -1); (8, 9, 1, -1, -1); (9, 5, 2, -1, -1); (5, 6, 11, -1, -1); (8, 9, 11, -1, -1); (9, 5, 11, -1, -1); (6, 7, 11, -1, -1); (7, 8, 11, -1, -1) |] Szebb lett volna, ha a lapleírók a Kvaternió_műveletek modul háromszög, négyszög, ötszög

objektumai. (Éppen erre gondolva került be a síkidomok deklarációja ebbe a modulba.)

Viszont a csúcsok kvaterniótömbjének átadása után a lapoknak és az éleknek a kezeléséhez csupán

annak megadására volt szükség, hogy az él milyen sorszámú csúcsokat köt össze, illetve, hogy a lapon

milyen sorszámú csúcsokat találunk. Ekkor pedig azt találtuk a legegyszerűbbnek, ha olyan

adatstruktúrákat használunk, amelyeknél az élek a csúcsok sorszámainak kettősei (tuples 2 elemmel),

a lapok pedig a csúcsok sorszámainak ötösei (tuples 5 elemmel). Persze, ekkor az ötösökben egy vagy

két hely néha kihasználatlanul marad. (A kihasználatlan helyekre olyan számot kell tenni, amely nem

lehet csúcs sorszáma. Mivel a programozási indexelés 0-tól indul, valamint a csúcsok sorszámozását

is 0-tól kezdtük, a kihasználatlan helyekre célszerűen -1-et írtunk.)

Page 87: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

85

A szabályos testek szerkesztésének programozása ( F# )

A szabályos testek mindegyikét egyformán vezérelhető függvénnyel rajzoltatjuk. A függvényeknek

három bemenő paramétük van: vastag, színez és a forgat paraméter. Az első paraméter a

drótváz előtérben levő éleinek vonalvastagságát adja meg (most 3-ra van állítva). A drótváz hátsó

vonalait 1 vonalvastagsággal rajzolja a program. színez paraméter true helyzetében a test

oldallapjai nem átlátszóak: az oldallapok festve vannak ( false helyzetében, persze, a drótvázat

látjuk). Végül forgat paraméter true értéke esetén nem csak a kiindulási alakzatot szerkeszti

és rajzoltatja a program, hanem annak egy elforgatottját is.

A programrészletek működésének megértését a program sorai közé írt magyarázatokkal igyekeztünk

biztosítani.

Mindegyik szabályos alakzathoz két rajzos oldal tartozik, amelyen a forgat false, illetve true

értékének megfeleltetett lapokon a színez változó false, illetve true értékének megfelelő

ábrákat látunk.

Tetraéder

let Tetraéder( vastag : int, színez : bool, forgat : bool ) = rajz.BackColor <- Color.White Felirat( "Tetraéder", 50, 50, 20, Color.DarkCyan ) HomogénKoordinátaHáttér( Color.Gray ) // A tetraéder alapja egy sík, amelyen egy egyenes döféspontjaként // jelölünk ki egy pontot: let SíkS = Plane( VÉGTELEN, VÉGTELEN, EGY ) let EgyenesT = Line( -HAT, -KETTŐ, NÉGY, -HAT, -KETTŐ, -FÉL ) let döfésPont = SíkotDöfEgyenes( SíkS, EgyenesT ) // színez külső változó beállításától függően drótvázat készít, // vagy színezett felületű testet állít elő a függvény. A színezéssel // együtt bizonyos részleteket (háttér sík színe, egyenes és pont // képe) jelentéktelenítünk, illetve elhagyunk: if színez then SíkRajz( Color.Silver, SíkS, HÉT ) else SíkRajz( Color.Lime, SíkS, HÉT ) EgyenesRajz( Color.Blue, EgyenesT, NULLA ) PontRajz( Color.Red, 2, döfésPont ) // A döféspontot kétszer 120 fokkal elforgatva előállítjuk az alap // szabályos háromszöget:

Page 88: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

86

tPontok.[0] <- döfésPont tPontok.[1] <- SíkS.Rot120Kvat * tPontok.[0] * SíkS.Rot120Kvat.Konj tPontok.[2] <- SíkS.Rot120Kvat * tPontok.[1] * SíkS.Rot120Kvat.Konj // A szabályos háromszög két szomszédos pontjának távolsága: let oldalHossz = ( tPontok.[1] - tPontok.[0] ).abs // Az egyenlőszárú háromszög középpontja az alaplap súlypontja: let háromszögSúlypontja = ( tPontok.[0] + tPontok.[1] + tPontok.[2] ).Scal(HARMAD) // A súlyponthoz mutató vektorhoz a számított p hosszúságú, az // alaplapra merőleges vektort hozzáadva ( #Tetraéder_magassága ) // a tedraédernek az alaplapon kívüli csúcspontját megkapjuk: tPontok.[3] <- Kvaternió( EGY, háromszögSúlypontja.Vect + SíkS.NormVect.Scal( oldalHossz * Math.Sqrt( KETTŐ / HÁROM ) ) ) let mutable rajzToll = new Pen( Color.Red, float32 vastag ) // Ezzel a függvénnyel összekötjük (a láthatóságot is figyelembe véve) // a tetraéder csúcspontjait (a rajzToll piros): if not színez then ÉlRajz( rajzToll, tetraéderÉlek, tPontok ) if forgat then // Ha két helyzetben ábrázoljuk a tetraédert, és drótvázat // rajzoltatunk, megsorszámozza a csúcsokat: PontsorszámotÍr( tPontok, Color.Cyan ) else // Nem átlátszóvá teszi (színezi) a lapokat: LapRajz( rajzToll, tPontok, tetraéderLapok, Színek ) if forgat then // A tetraédert az i tengely körül 160 fokkal elforgatja: let Forgató = Kvaternió( Vector(EGY, NULLA, NULLA), double(160) ) for i = 0 to tPontok.Length - 1 do tPontok.[i] <- Forgató * tPontok.[i] * Forgató.Konj // Kék színnel megrajzoltatja az új helyzetnek megfelelő vázat: rajzToll <- new Pen( Color.Navy, float32 vastag ) if not színez then ÉlRajz( rajzToll, tetraéderÉlek, tPontok ) // A forgatási tengelyt rajzoltatjuk kiírva a forgatás szögét: RáRajzol( Forgató, -150, -10, HÉT, HÉT ) // Ha két helyzetben ábrázoljuk a tetraédert, és drótvázat // rajzoltatunk, megsorszámozza a csúcsokat: PontsorszámotÍr( tPontok, Color.Magenta ) else // Kifesti új helyzetében a tetraédert: LapRajz( rajzToll, tPontok, tetraéderLapok, Színek )

Page 89: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

87

Page 90: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

88

Page 91: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

89

Oktaéder

let Oktaéder( vastag : int, színez : bool, forgat : bool ) = Felirat( "Oktaéder", 50, 50, 20, Color.DarkCyan ) HomogénKoordinátaHáttér( Color.Gray ) // k-ra merőleges síkon lesz a közép négyzet: let SíkS : Plane = new Plane( VÉGTELEN, VÉGTELEN, HÁROM ) // A fentivel párhuzamos „árnyéksíkot” is készítünk: let SíkH : Plane = new Plane( VÉGTELEN, VÉGTELEN, double -1.25 ) // A síkon felveszünk egy pontot, ez lesz a 0. idexű csúcs: let PontS : Kvaternió = new Kvaternió( EGY, HÁROM, HÁROM, HÁROM ) // Rajzolások az üzemmódtól függően: if színez then SíkRajz( Color.Silver, SíkH, NÉGY ) else if not forgat then SíkRajz( Color.Silver, SíkH, NÉGY ) SíkRajz( Color.RoyalBlue, SíkS, NÉGY ) PontRajz( Color.Red, 2, PontS ) // 90 fokos forgatásokkal a további csúcsok szerkesztése: oPontok.[0] <- PontS oPontok.[1] <- SíkS.Rot90Kvat * oPontok.[0] * SíkS.Rot90Kvat.Konj oPontok.[2] <- SíkS.Rot90Kvat * oPontok.[1] * SíkS.Rot90Kvat.Konj oPontok.[3] <- SíkS.Rot90Kvat * oPontok.[2] * SíkS.Rot90Kvat.Konj // Az oktaéder oldalhossza két szomszédos csúcs távolsága: let oldalHossz = ( oPontok.[1] - oPontok.[0] ).abs // Kiszámítjuk a négyzet középpontját: let négyzetSúlypontja = ( oPontok.[0] + oPontok.[1] + oPontok.[2] + oPontok.[3] ).Scal(NEGYED) // A négyzet középpontjától kiindulva a négyzetre merőleges // egyenes mentén mindkét irányban felmérjük a meglevő négyzeten // kívüli csúcs távolságát: a négyzetátló felét.

Page 92: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

90

oPontok.[4] <- Kvaternió( EGY, négyzetSúlypontja.Vect + SíkS.NormVect.Scal( oldalHossz / GYÖKKETTŐ ) ) oPontok.[5] <- Kvaternió( EGY, négyzetSúlypontja.Vect - SíkS.NormVect.Scal( oldalHossz / GYÖKKETTŐ ) ) let mutable rajzToll = new Pen( Color.Red, float32 vastag ) // Drótvázat rajzoltatunk, illetve felületet színeztetünk: if not színez then ÉlRajz( rajzToll, oktaéderÉlek, oPontok ) if forgat then PontsorszámotÍr( oPontok, Color.Cyan ) else LapRajz( rajzToll, oPontok, oktaéderLapok, Színek ) // Ha az oktaédert két helyzetben mutatjuk, megszerkesztjük az // elforgatottját, majd rajzoltatunk: if forgat then // Az oktaédert az i - j szögfelező körül 140 fokkal elforgatjuk: let Forgató = Kvaternió( Vector( EGY, EGY, NULLA ), double(140) ) // Az alakzat minden egyes csúcspontján a forgatás elvégzése: for i = 0 to oPontok.Length - 1 do oPontok.[i] <- Forgató * oPontok.[i] * Forgató.Konj // Kék színnel megrajzoltatjuk az új helyzetnek megfelelő vázat: rajzToll <- new Pen( Color.Navy, float32 vastag ) if not színez then ÉlRajz( rajzToll, oktaéderÉlek, oPontok ) RáRajzol( Forgató, -350, 100, ÖT, ÖT ) // Ha két helyzetben ábrázoljuk az oktaédert, és drótvázat // rajzoltatunk, megsorszámozzuk a csúcsokat: PontsorszámotÍr( oPontok, Color.Magenta ) else // Kifestjük új helyzetében az oktaédert: LapRajz( rajzToll, oPontok, oktaéderLapok, Színek )

Page 93: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

91

Page 94: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

92

Page 95: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

93

Hexaéder

let Hexaéder( vastag : int, színez : bool, forgat : bool ) = rajz.BackColor <- Color.White Felirat( "Hexaéder (kocka)", 50, 50, 20, Color.DarkCyan ) HomogénKoordinátaHáttér( Color.Gray ) // j-re merőleges síkon lesz a kocka négyzet alapja: let SíkS : Plane = Plane( VÉGTELEN, KILENC, VÉGTELEN ) // Egy pontot veszünk fel ezen a síkon: let PontS : Kvaternió = Kvaternió( EGY, HÁROM, KILENC, HÁROM ) // Kirajzoltatás a működésmódtól függően: if színez then SíkRajz( Color.Silver, SíkS, ÖT ) elif not forgat then SíkRajz( Color.DarkKhaki, SíkS, ÖT ) PontRajz( Color.DarkRed, 2, PontS ) // A síkon a pontot háromszor 90 fokkal elforgatva előállítjuk az // alap négyzet további pontjait: hPontok.[0] <- PontS hPontok.[1] <- SíkS.Rot90Kvat * hPontok.[0] * SíkS.Rot90Kvat.Konj hPontok.[2] <- SíkS.Rot90Kvat * hPontok.[1] * SíkS.Rot90Kvat.Konj hPontok.[3] <- SíkS.Rot90Kvat * hPontok.[2] * SíkS.Rot90Kvat.Konj // Két szomszédos pont távolsága az oldalhossz: let oldalHossz = ( hPontok.[1] - hPontok.[0] ).abs // Az alap mindegyik csúcspontjában az alaplapra merőleges irányban // az oldalhosszat felmérve előállítjuk a további csúcsokat: hPontok.[4] <- Kvaternió( EGY, hPontok.[0].Vect - SíkS.NormVect.Scal( oldalHossz ) ) hPontok.[5] <- Kvaternió( EGY, hPontok.[1].Vect - SíkS.NormVect.Scal( oldalHossz ) ) hPontok.[6] <- Kvaternió( EGY, hPontok.[2].Vect - SíkS.NormVect.Scal( oldalHossz ) ) hPontok.[7] <- Kvaternió( EGY, hPontok.[3].Vect - SíkS.NormVect.Scal( oldalHossz ) )

Page 96: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

94

let mutable rajzToll = new Pen( Color.Red, float32 vastag ) // Piros színű drótvázat rajzoltatunk, illetve befestjük a kockát: if not színez then ÉlRajz( rajzToll, hexaéderÉlek, hPontok ) // Forgatás esetén midkét drótvázon sorszámozzuk a csúcsokat: if forgat then PontsorszámotÍr( hPontok, Color.Cyan ) else LapRajz( rajzToll, hPontok, hexaéderLapok, Színek ) // Ha a kockát el is kell forgatni, itt folytatjuk: if forgat then // A hexaédert a j - k szögfelező körül 180 fokkal elforgatjuk… // A forgatási kvaternió konstruktorát alkalmazzuk: ennek első // paramétere a tengelyt kijelölő vektor, második paramétere // pedig az elforgatás szöge: let Forgató = Kvaternió( Vector( NULLA, EGY, EGY ), double(180) ) // A kocka mindegyik csúcsára alkalmazzuk a forgatási // transzformációt: for i = 0 to hPontok.Length - 1 do hPontok.[i] <- Forgató * hPontok.[i] * Forgató.Konj // Kék színnel megrajzoltatjuk az új helyzetnek megfelelő vázat: rajzToll <- new Pen( Color.Navy, float32 vastag ) if not színez then ÉlRajz( rajzToll, hexaéderÉlek, hPontok ) RáRajzol( Forgató, 0, 150, double 10.5, KÉTÉSFÉL ) // Megsorszámozzuk a csúcsokat: PontsorszámotÍr( hPontok, Color.Magenta ) else // Kifestjük új helyzetében is a kockát: LapRajz( rajzToll, hPontok, hexaéderLapok, Színek )

Page 97: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

95

Page 98: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

96

Page 99: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

97

Dodekaéder

let Dodekaéder( vastag : int, színez : bool, forgat : bool ) = rajz.BackColor <- Color.White Felirat( "Dodekaéder", 50, 50, 20, Color.DarkCyan ) HomogénKoordinátaHáttér( Color.Gray ) // A tengelyek metszési pontjával síkot határozunk meg: let SíkS= new Plane( ÖT, MÁSFÉL, KÉTÉSFÉL ) // Egyenest adunk meg a két térbeli pontjának koordinátáival: let EgyenesT = new Line( -EGY, -EGY, -EGY, KETTŐ, EGY, HAT ) // Meghatározzuk az egyenes döféspontját a síkon: let döfésPont = SíkotDöfEgyenes( SíkS, EgyenesT ) // Üzemmódtól függően kirajzoltatunk: if színez then SíkRajz( Color.Silver, SíkS, KETTŐ ) elif not forgat then SíkRajz( Color.SkyBlue, SíkS, KETTŐ ) EgyenesRajz( Color.Blue, EgyenesT, TIZED ) PontRajz( Color.Red, 2, döfésPont ) // A döféspont az alap ötszög első csúcsa. Elforgatást végeztetünk a // síkon négyszer egymás után 72 fokkal. (A forgatás középpontja a // síknak az origóhoz legközelebbi - Sík.Near - pontja.) Ez a // szabályos ötszög a dodekaéder első oldallapja. dPontok.[0] <- döfésPont dPontok.[1] <- SíkS.Rot72Kvat * dPontok.[0] * SíkS.Rot72Kvat.Konj dPontok.[2] <- SíkS.Rot72Kvat * dPontok.[1] * SíkS.Rot72Kvat.Konj dPontok.[3] <- SíkS.Rot72Kvat * dPontok.[2] * SíkS.Rot72Kvat.Konj dPontok.[4] <- SíkS.Rot72Kvat * dPontok.[3] * SíkS.Rot72Kvat.Konj // Az ötszög oldalhossza (a), átlója (c), valamint az átlóhoz tartozó // egyenlő oldalú háromszög magassága (n) ( #Dodekaéder_adatai ): let a = ( dPontok.[1] - dPontok.[0] ).abs let c = a * ( EGY + GYÖKÖT ) / KETTŐ let n = c * GYÖKHÁROMPERKETTŐ // A drótvázas ábra tolla: let mutable rajzToll = new Pen( Color.Red, float32 vastag )

Page 100: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

98

// Az alaplapi csúcsokkal szomszédos legközelebbi csúcsok // helykoordinátájának (kvaterniójának) számítása: let (egyikPont, másikPont) = KétPonttólMegadottTávolságra(dPontok.[1], Plane( ( dPontok.[0] + dPontok.[2] ).Scal(FÉL), ( dPontok.[2] - dPontok.[0] ).Vect), a, n ) // A két megoldás közül az alaplap fölöttit választjuk: dPontok.[5] <- másikPont let (egyikPont, másikPont) = KétPonttólMegadottTávolságra(dPontok.[2], Plane( ( dPontok.[1] + dPontok.[3] ).Scal(FÉL), ( dPontok.[3] - dPontok.[1] ).Vect), a, n ) dPontok.[6] <- másikPont let (egyikPont, másikPont) = KétPonttólMegadottTávolságra(dPontok.[3], Plane( ( dPontok.[2] + dPontok.[4] ).Scal(FÉL), ( dPontok.[4] - dPontok.[2] ).Vect), a, n ) dPontok.[7] <- másikPont let (egyikPont, másikPont) = KétPonttólMegadottTávolságra(dPontok.[4], Plane( ( dPontok.[3] + dPontok.[0] ).Scal(FÉL), ( dPontok.[0] - dPontok.[3] ).Vect), a, n ) dPontok.[8] <- másikPont let (egyikPont, másikPont) = KétPonttólMegadottTávolságra(dPontok.[0], Plane( ( dPontok.[4] + dPontok.[1] ).Scal(FÉL), ( dPontok.[1] - dPontok.[4] ).Vect), a, n ) dPontok.[9] <- másikPont // Emlékeztető segédvonalak berajzolása a drótvázas ábrába: if not forgat && not színez then Összeköt( Pens.Cyan, dPontok.[4], dPontok.[9] ) Összeköt( Pens.Cyan, dPontok.[1], dPontok.[9] ) Összeköt( Pens.Cyan, dPontok.[1], dPontok.[4] ) Összeköt( Pens.Cyan, dPontok.[0], ( dPontok.[1] + dPontok.[4] ).Scal(FÉL)) // Az alaphoz illeszkedő oldallapi ötszögek síkjában az oldallapi // ötszögek ötödik csúcsa (amely a szomszédos csúcsoktól 'a' // távolságra van) helykoordinátájának (kvaterniójának) meghatározása: let (egyikPont, másikPont) = KétPonttólMegadottTávolságra(dPontok.[5], Plane( dPontok.[6], dPontok.[1], dPontok.[2] ), a, a ) // A két megoldás közül a megfelelő kiválasztása: dPontok.[10] <- egyikPont let (egyikPont, másikPont) = KétPonttólMegadottTávolságra(dPontok.[6], Plane( dPontok.[7], dPontok.[2], dPontok.[3] ), a, a ) dPontok.[11] <- egyikPont let (egyikPont, másikPont) = KétPonttólMegadottTávolságra(dPontok.[7],

Page 101: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

99

Plane( dPontok.[8], dPontok.[3], dPontok.[4] ), a, a ) dPontok.[12] <- egyikPont let (egyikPont, másikPont) = KétPonttólMegadottTávolságra(dPontok.[8], Plane( dPontok.[9], dPontok.[4], dPontok.[0] ), a, a ) dPontok.[13] <- egyikPont let (egyikPont, másikPont) = KétPonttólMegadottTávolságra(dPontok.[9], Plane( dPontok.[5], dPontok.[0], dPontok.[1] ), a, a ) dPontok.[14] <- egyikPont // A dodekaéder-test középpontjának meghatározása az utóbbi 10 pont // koordinátájának (kvaterniójának) átlagolásával: let mutable közepe = O for lI = 5 to 14 do közepe <- közepe + dPontok.[lI] közepe <- közepe.Scal(TIZED) // Az utolsó 5 pont kvaternióját a kiindulási alaplap 5 kvaterniójának // a testközépen keresztül való tükrözésével számítjuk ki: for lI = 15 to 19 do dPontok.[lI] <- KözéppontosTükrözés( közepe, dPontok.[lI - 15] ) // Üzemmódtól függően rajzoltatás: if not színez then ÉlRajz( rajzToll, dodekaéderÉlek, dPontok ) if forgat then PontsorszámotÍr( dPontok, Color.Cyan ) else PontRajz( Color.DarkCyan, 2, közepe ) else LapRajz( rajzToll, dPontok, dodekaéderLapok, Színek ) if forgat then // A dodekaédert a k tengely körül -200 fokkal elforgatjuk: let Forgató = Kvaternió(Vector(NULLA, NULLA, EGY), double(-200)) for i = 0 to dPontok.Length - 1 do dPontok.[i] <- Forgató * dPontok.[i] * Forgató.Konj // Kék színnel megrajzoltatjuk az új helyzetnek megfelelő vázat: rajzToll <- new Pen( Color.Navy, float32 vastag ) if not színez then ÉlRajz( rajzToll, dodekaéderÉlek, dPontok ) RáRajzol( Forgató, 50, 100, HAT, TÍZ ) // Ha két helyzetben ábrázoljuk a dodekaédert, és drótvázat // rajzoltatunk, megsorszámozzuk a csúcsokat: PontsorszámotÍr( dPontok, Color.Magenta ) else // Kifesti új helyzetében a dodekaédert: LapRajz( rajzToll, dPontok, dodekaéderLapok, Színek )

Page 102: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

100

Page 103: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

101

Page 104: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

102

Ikozaéder

let Ikozaéder( vastag : int, színez : bool, forgat : bool ) = rajz.BackColor <- Color.White Felirat( "Ikozaéder", 50, 50, 20, Color.DarkCyan ) HomogénKoordinátaHáttér( Color.Gray ) // Az ikozaéder egyik szabályos ötszög metszetének síkja: let SíkS = Plane( VÉGTELEN, VÉGTELEN, FÉL ) let SíkH = Plane( VÉGTELEN, VÉGTELEN, double –2.1 ) // k tengellyel párhuzamos egyenes, amely az egyik csúcsot a síkon // kijelöli (döfésPont): let EgyenesT = Line (-HÁROM, -HÁROM, NULLA, -HÁROM, -HÁROM, HÁROM ) let döfésPont = SíkotDöfEgyenes( SíkS, EgyenesT ) // Üzemmódtól függő rajzoltatás: if színez then SíkRajz( Color.Silver, SíkH, NÉGY ) elif not forgat then SíkRajz( Color.Silver, SíkH, NÉGY ) SíkRajz( Color.SkyBlue, SíkS, NÉGY ) EgyenesRajz( Color.Blue, EgyenesT, TIZED ) PontRajz( Color.Red, 2, döfésPont ) // Szabályos ötszög szerkesztése forgatásokkal: iPontok.[0] <- döfésPont iPontok.[1] <- SíkS.Rot72Kvat * iPontok.[0] * SíkS.Rot72Kvat.Konj iPontok.[2] <- SíkS.Rot72Kvat * iPontok.[1] * SíkS.Rot72Kvat.Konj iPontok.[3] <- SíkS.Rot72Kvat * iPontok.[2] * SíkS.Rot72Kvat.Konj iPontok.[4] <- SíkS.Rot72Kvat * iPontok.[3] * SíkS.Rot72Kvat.Konj // Az ötszög súlypontjának meghatározása a csúcsponti kvaterniók // átlagolásával: let ötszögSúlypontja = ( iPontok.[0] + iPontok.[1] + iPontok.[2] + iPontok.[3] + iPontok.[4] ).Scal( EGY / ÖT ) // Az alap ötszög csúcsainak középpontos tükrözésével a vele // párhuzamosan elhelyezkedő belső ötszög alapra vetített // csúcspontjainak meghatározása: for lI = 0 to 4 do iPontok.[5 + lI] <- KözéppontosTükrözés( ötszögSúlypontja, iPontok.[lI] )

Page 105: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

103

// Az ötszög oldalhossza (a); átlója (c); egy számítási segédmennyiség // (s); a szabályos ötszög magassága (b); a szabályos ötszög oldalához // tartozó egyenlő oldalú háromszög magassága (m); az ötszög körülírt // körének sugara (r); az ikozaéderből leszelhető ötszög alapú gúlák // magassága (q); végül a tetraéderben fellelhető egymással párhuzamos // síkokon elhelyezkedő szabályos ötszögek síkjának egymástól mért // távolsága (t) ( levezetés: #Dodekaéder_adatai, #Ikozaéder_adatai ): let a = ( iPontok.[1] - iPontok.[0] ).abs let c = a * ( EGY + GYÖKÖT ) / KETTŐ let s = ÖT + KETTŐ * GYÖKÖT let b = a / KETTŐ * Math.Sqrt( s ) let m = a * GYÖKHÁROMPERKETTŐ let r = a / KETTŐ * ( HÁROM + GYÖKÖT ) / Math.Sqrt( s ) let q = a / KETTŐ * Math.Sqrt( ( HAT + KETTŐ * GYÖKÖT ) / ( s ) ) let t = a / KETTŐ * Math.Sqrt( ( double 14 + HAT * GYÖKÖT ) / ( s ) ) // A drótváz rajzolásához toll: let mutable rajzToll = new Pen( Color.Red, float32 vastag ) // A kiindulási ötszög síkján megszerkesztett tükrözött segédpontok // (vetületi pontok) felhasználásával az alappal párhuzamos (tőle t // távolságra levő) pontok előállítása: let távVektor = SíkS.NormVect.Scal( t ) iPontok.[8] <- Kvaternió( EGY, iPontok.[8].Vect + távVektor ) iPontok.[9] <- Kvaternió( EGY, iPontok.[9].Vect + távVektor ) iPontok.[5] <- Kvaternió( EGY, iPontok.[5].Vect + távVektor ) iPontok.[6] <- Kvaternió( EGY, iPontok.[6].Vect + távVektor ) iPontok.[7] <- Kvaternió( EGY, iPontok.[7].Vect + távVektor ) // Ennek a szabályos ötszögnek is meghatározzuk a középpontját: let másikSúlypontja = ( iPontok.[5] + iPontok.[6] + iPontok.[7] + iPontok.[8] + iPontok.[9] ).Scal(ÖTÖD) // A szabályos ötszögekre mint alapokra épülő gúlák csúcsának // kiszámítása a q magasság méretű normális irányú vektornak az egyik // súlypont vektorához való hozzáadásával, illetve a másikéból való // levonással: let csúcsVektor = SíkS.NormVect.Scal( q ) iPontok.[10] <- Kvaternió( EGY, ötszögSúlypontja.Vect – csúcsVektor) iPontok.[11] <- Kvaternió( EGY, másikSúlypontja.Vect + csúcsVektor )

Page 106: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

104

// Az ikozaéder előállítása a paramétereknek megfelelően: if not színez then // Csak az élek drótvázszerű kihúzása: ÉlRajz( rajzToll, ikozaéderÉlek, iPontok ) // ÉlRajzKézi( rajzToll, ikozaéderÉlek, iPontok, [| 3; 4; 6 |] ) if forgat then // Ha két helyzetben ábrázoljuk az ikozaédert, és drótvázat // rajzoltatunk, megsorszámozzuk a csúcsokat: PontsorszámotÍr( iPontok, Color.Cyan ) else // A felületek festésével: LapRajz( rajzToll, iPontok, ikozaéderLapok, Színek ) // LapRajzKézi( rajzToll, iPontok, ikozaéderLapok, // [| 3; 4; 6 |], Színek) if forgat then // Az ikozaédert a j tengely körül 130 fokkal elforgatjuk: let Forgató = Kvaternió(Vector(NULLA, EGY, NULLA), double(130)) // Az ikozaéder mindegyik csúcsára alkalmazzuk a forgató // transzformációt: for i = 0 to iPontok.Length - 1 do iPontok.[i] <- Forgató * iPontok.[i] * Forgató.Konj // Kék színnel megrajzoltatjuk az új helyzetnek megfelelő vázat: rajzToll <- new Pen( Color.Navy, float32 vastag ) if not színez then ÉlRajz( rajzToll, ikozaéderÉlek, iPontok ) RáRajzol( Forgató, -500, 200, KILENC, NYOLC ) PontsorszámotÍr( iPontok, Color.Magenta ) else // Kifesti új helyzetében az ikozaédert: LapRajz( rajzToll, iPontok, ikozaéderLapok, Színek ) Az ikozaédert mutató négy ábrát követő oldalakon mellékeljük az élek rajzoltatását végző függvény

egy egyszerűsített megvalósítását, amelynél a háttérbe helyezendő csúcsok (amelyekhez menő

éleket vékonyítjuk) sorszámát paraméterként adjuk át (mint a fentiekben – a lap tetején –

magyarázat sorként a kódban elhelyezett függvényhívás esetén). Hasonlóképpen a felületek

színezését végző függvénynek is megmutatjuk egy olyan változatát, amely „kézi” módon állítja be a

felületek láthatóságát, azaz az előző függvényhez hasonlóan ez is átveszi azt a tömböt, amely az

eltakart csúcsokat tartalmazza (hívása a lap közepén látható). Végül közreadjuk azt a függvényt is,

amelyre az előző függvények hivatkoznak, és amely a KoordinátaHáttér létrehozását végzi.

Page 107: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

105

Page 108: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

106

Page 109: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

107

let ÉlRajzKézi( fToll : Pen, fÉlek : ( int * int ) array, fPontok : Kvaternió array, fTakart : int array ) = let mutable lI : int = 0 let mutable lJ : int = 0 let mutable lK : int = 0 let mutable lHátsó : bool = false // A kvaternióval megadott csúcspontok helyett a továbbiakban a rajz // síkjában érvényes pontkoordinátákat használjuk: let lPontok : PointF array = Array.zeroCreate fPontok.Length for lI = 0 to fPontok.Length - 1 do let (lX, lY, lZ) = KvaternióPont(fPontok.[lI]) lPontok.[lI].X <- float32 lX lPontok.[lI].Y <- float32 lY // Megrajzoltatjuk a paraméterként átadott tollal valamennyi, a // szabályos testhez tartozóan felsorolt élet. Pontosabban: a toll // színével, de egységnyi vonalvastagsággal rajzoltatunk, ha az él // takart pontból indul, illetve abba érkezik, csak a többi él // esetében hagyjuk meg a megadott vonalvastagságot. for lI = 0 to fÉlek.Length - 1 do let ( lEzt, lEzzel ) = fÉlek.[lI] lHátsó <- false lJ <- 0 while not lHátsó && lJ < fTakart.Length do lK <- fTakart.[lJ] if lEzt = lK || lEzzel = lK then lHátsó <- true lJ <- lJ + 1 if lHátsó then ábra.DrawLine( new Pen(fToll.Color), lPontok.[lEzt], lPontok.[lEzzel] ) else ábra.DrawLine( fToll, lPontok.[lEzt], lPontok.[lEzzel] ) // Az átadott (legfeljebb 5 csúcsponttal meghatározott) lap takart-e: let Rajzolja( ( A, B, C, D, E ) : int * int * int * int * int, fTakart : int array ) : bool = // Belső függvény, amely azt nézi, hogy a paraméterként átadott // sorszámú csúcs fedett (takart)-e: let Takart( csúcsSorszám : int ) : bool = let mutable lI : int = 0

Page 110: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

108

let mutable lTakart: bool = false // Negatív csúcssorszám áll az "üres" helyeken: if csúcsSorszám > 0 then lI <- 0 while lI < fTakart.Length && not lTakart do if csúcsSorszám = fTakart.[lI] then lTakart <- true lI <- lI + 1 lTakart not ( Takart(A) || Takart(B) || Takart(C) || Takart(D) || Takart(E) ) // A lapok színezését végző függvény, amely paraméterként a takart csúcsok // listáját (és persze a lapok színeinek tömbjét) megkapja: let LapRajzKézi( fToll : Pen, fPontok : Kvaternió array, fLapok: ( int * int * int * int * int ) array, fTakart : int array, fSzínek : Color array ) = // Belső függvény, amely az indexek listáján szereplő soszámú // csúcspontok által meghatározott lapokat színezi. let LapFestő( fEcset : Brush, lIndexek : int array ) = let mutable lI : int = 0 let rajzPontok : Point array = Array.zeroCreate ( lIndexek.Length + 1 ) for lI = 0 to lIndexek.Length - 1 do let ( pontX, pontY, pontZ ) = KvaternióPont( fPontok.[lIndexek.[lI]] ) rajzPontok.[lI].X <- pontX rajzPontok.[lI].Y <- pontY rajzPontok.[lIndexek.Length] <- rajzPontok.[0] ábra.FillPolygon( fEcset, rajzPontok ) let mutable lI as int = 0 // A szabályos test mindegyik lapját vizsgálja. Amelyik lapnak van // olyan csúcsa, amely a takart csúcsok listáján szerepel, azt a // lapot nem festi a program. A lapok színezésére egyébként a színek // közül a lap sorszámának megfelelő sorszámút választja. for lI = 0 to fLapok.Length - 1 do // A lap csúcsainak ötösét áttölti az ( A, B, C, D, E ) ötösbe: let ( A, B, C, D, E ) = fLapok.[lI] if Rajzolja( ( A, B, C, D, E ), fTakart ) then if E < 0 then if D < 0 then LapFestő( new SolidBrush( fSzínek.[lI]), [| A; B; C |] ) else LapFestő( new SolidBrush( fSzínek.[lI]), [| A; B; C; D |] ) else

Page 111: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

109

LapFestő( new SolidBrush( fSzínek.[lI]), [| A; B; C; D; E |] ) // Függvény, amely a rajzokon a koordinátarendszert elhelyezi: let HomogénKoordinátaHáttér( ByVal változóSzín : Color ) = // Színek és tollak definíciója: let egységSzín : Color = változóSzín let tengelySzín : Color = változóSzín let tengelyToll : Pen = new Pen( tengelySzín, float32 3 ) let vékonyToll : Pen = new Pen( változóSzín, float32 1 ) let pirosToll : Pen = new Pen( Color.Red, float32 1 ) // Fő tengelyszakaszok rajzoltatása: ábra.DrawLine( tengelyToll, tér4Ox, tér4Oy, tér4Rx, tér4Ry ) ábra.DrawLine( tengelyToll, tér4Ox, tér4Oy, tér4Ix, tér4Iy ) ábra.DrawLine( tengelyToll, tér4Ox, tér4Oy, tér4Jx, tér4Jy ) ábra.DrawLine( tengelyToll, tér4Ox, tér4Oy, tér4Kx, tér4Ky ) // Tengelyek (vékony vonalas) meghosszabbítása: ábra.DrawLine( vékonyToll, tér4Ox, tér4Oy, 2 * tér4Ox - tér4Rx, 2 * tér4Oy - tér4Ry ) ábra.DrawLine( vékonyToll, tér4Ox, tér4Oy, 2 * tér4Ox - tér4Ix, 2 * tér4Oy - tér4Iy ) ábra.DrawLine( vékonyToll, tér4Ox, tér4Oy, tér4Ox + (tér4Ox - tér4Jx) / 2, tér4Oy + (tér4Oy - tér4Jy) / 2 ) ábra.DrawLine( vékonyToll, tér4Ox, tér4Oy, tér4Ox + (tér4Ox - tér4Kx) / 2, tér4Oy + (tér4Oy - tér4Ky) / 2 ) // Tengelyfeliratok elhelyezése: Felirat( "valós tengely", tér4Rx, tér4Ry - 20, 16, tengelySzín ) Felirat( "képzetes (i) tengely", tér4Ix - 50, tér4Iy - 30, 16, tengelySzín ) Felirat( "képzetes (j) tengely", tér4Jx - 40, tér4Jy - 30, 16, tengelySzín ) Felirat( "képzetes (k) tengely", tér4Kx - 150, tér4Ky - 30, 16, tengelySzín ) // Tengelyek (kis vonalkás) keresztülhúzása az egységeknél: let egyRx = tér4Ox + int(double(tér4Rx - tér4Ox) / SKÁLA) let egyRy = tér4Oy + int(double(tér4Ry - tér4Oy) / SKÁLA) KeresztülHúz( tengelyToll, 6.0, egyRx, egyRy, tér4Ix, tér4Iy ) let egyIx = tér4Ox + int(double(tér4Ix - tér4Ox) / SKÁLA) let egyIy = tér4Oy + int(double(tér4Iy - tér4Oy) / SKÁLA) KeresztülHúz( tengelyToll, 6.0, egyIx, egyIy, tér4Rx, tér4Ry ) let egyJx = tér4Ox + int(double(tér4Jx - tér4Ox) / SKÁLA) let egyJy = tér4Oy + int(double(tér4Jy - tér4Oy) / SKÁLA) KeresztülHúz( tengelyToll, 6.0, egyJx, egyJy, tér4Rx, tér4Ry )

Page 112: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

110

let egyKx = tér4Ox + int(double(tér4Kx - tér4Ox) / SKÁLA) let egyKy = tér4Oy + int(double(tér4Ky - tér4Oy) / SKÁLA) KeresztülHúz( tengelyToll, 6.0, egyKx, egyKy, tér4Ix, tér4Iy ) // Homogén koordinátatengelyek berajzolása Base0-hoz: ábra.DrawLine(pirosToll, egyRx, egyRy, egyRx + tér4Ix - tér4Ox, egyRy + tér4Iy - tér4Oy ) ábra.DrawLine(pirosToll, egyRx, egyRy, egyRx + tér4Jx - tér4Ox, egyRy + tér4Jy - tér4Oy ) ábra.DrawLine(pirosToll, egyRx, egyRy, egyRx + tér4Kx - tér4Ox, egyRy + tér4Ky - tér4Oy ) ábra.DrawLine(pirosToll, egyRx, egyRy, egyRx + tér4Ox - tér4Ix, egyRy + tér4Oy - tér4Iy ) ábra.DrawLine(pirosToll, egyRx, egyRy, egyRx + 2 *(tér4Ox - tér4Jx)\ 3, egyRy + 2 *(tér4Oy - tér4Jy)\ 3 ) ábra.DrawLine(pirosToll, egyRx, egyRy, egyRx + (tér4Ox - tér4Kx) \ 2, egyRy + (tér4Oy - tér4Ky) \ 2 ) // Az egységekhez (és az origo ponthoz) felirat készíttetése: SzimplaFelirat( "1", egyRx, egyRy - 30, 20, egységSzín ) KvaternióFelirat( "i", egyIx - 10, egyIy - 30, 20, egységSzín ) KvaternióFelirat( "j", egyJx - 10, egyJy - 40, 20, egységSzín ) KvaternióFelirat( "k", egyKx - 5, egyKy - 35, 20, egységSzín ) KvaternióFelirat( "o", tér4Ox - 12, tér4Oy, 20, Color.Magenta )

Page 113: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

111

Konkáv testek rajzolása

Ha nem csak konvex testekkel foglalkozunk, a szabályos testekből érdekes , kinövésekkel rendelkező

testekhez juthatunk. Például kézenfekvő, hogy a dodekaéder némelyik szabályos ötszög lapjához

másik dodekaédert ragasszunk, így akár dodekaéderekből alkotott hosszú láncokat is készíthetünk.

Viszont, ha megkötjük a kezünket azzal, hogy csak középpontosan szimmetrikus, minden korábbi

lapja irányában ugyanúgy folytatott, önmagába nem harapó, „szabályos” testeket rajzoltatunk, akkor

háromszögre célszerűen tetraédert, négyzetre az oktaéder felének megfelelő testet, ötszögre pedig

az ikozaédernél szerkesztett gúlát helyezünk folytatásképpen. Ezzel olyan testhez jutunk, amelyik

konkáv, de csupa egybevágó szabályos háromszög határolja. Ilyen testekre mutatunk pár példát

ebben a fejezetben. Láthatják, hogy ezek térbeli forgatása is nagyon egyszerű a kvaterniós leírás

esetén. A láthatóság (takarás) kérdése már nem ilyen egyszerű. Konvex testeknél kimondhattuk,

hogy az az él, amelynek legalább egyik végén olyan takart pont található, amely nem a kontúron van,

tehát „teljesen takart”, nem látható. Konkáv testek esetén egy élnek lehet látható és nem látható

szakasza. Hasonlóképpen a lapoknál: „teljesen takart” (nem a kontúron található) csúcsot tartalmazó

lap nem látható konvex testeknél, de az ilyen lapnak is lehet látható része, ha a test nem konvex.

A következő két oldalon a szabályos testek mindegyikét úgy bővítjük, hogy – bár konkávak lesznek –

csupa egybevágó háromszög (tetraédernél 12, oktaédernél, hexaédernél 24, dodekaédernél és

ikozaédernél 60) borítja azokat. (A láthatóságot csak részben tudjuk figyelembe venni.)

Page 114: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

112

Page 115: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

113

Page 116: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

114

A komplex számok alkalmazásaként síkbeli fraktálokat rajzoltattunk. Térbeli fraktálokat jól tudunk

kezelni a kvaterniókkal. Például az alábbiakban egy, a síkbeli Koch görbéhez hasonló térbeli fraktált

rajzoltatunk. (Ott egy szakasz harmadolásával indítottunk, és a szakasz harmadát helyettesítettük

háromszöggel, itt pedig minden egyes lap negyedét helyettesítjük tetraéderrel.)

Tetraéderből indulunk ki. Ennek mindegyik oldallapján kijelöljük az oldalfelező pontokat. A

felezőpontokat egymással összekötve az oldallapokat négy darab fele akkora méretű háromszögre

bontjuk fel. A középső, szabályos háromszög helyére egy ugyanolyan alapú, kifelé emelkedő

tetraédert helyezünk. Ezt az eljárást egymás után többször végrehajthatjuk az előállított testet

határoló valamennyi háromszög mindegyik oldalának ismételt felezésével és a középső háromszög

tetraéderrel helyettesítésével. Az alábbi ábrákon először a legbonyolultabb képet mutatjuk, majd

utána (visszafelé) az előző két lépést. (A kiindulási ábra, persze, a piros tetraéder volt.)

Page 117: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

115

Page 118: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

116

Elforgatott pontokból a forgatási tengely és szög meghatározása

a forgató kvaternió ( #Forgatási_transzformáció ), amelyben

egységvektor a forgatás tengelye, szög pedig az elforgatás szögének fele, azt a kérdést vetjük fel,

hogy és ismeretében ki tudjuk-e számítani x, y, z-t, valamint meg tudjuk-e határozni –t?

Mindenekelőtt a vektoriális szorzat kvaternióműveletekkel való leírását másoljuk ide:

( )

Most emlékezzünk a forgatás bevezető gondolatmenetére! A pontot, amelyet elforgatunk, jobbról

szorozzuk egy kvaternióval, ugyanakkor az elforgatott pontot balról szorozzuk ugyanazzal a

kvaternióval. Azt állítottuk, hogy a két szorzat egy (egymással megegyező) átmeneti pont:

Az előző képletbe írjuk be ezt az összefüggést azért, hogy a kvaternió-szorzás elvégzése előtt

kiemelésre legyünk képesek:

( ) ( )

A középső kifejezés kvaternió-szorzását elvégezzük. Figyelembe vesszük, hogy

hiszen (mivel kvaternió a kvaternió elforgatottja, ezek valós része megegyezik) a kvaterniók

különbsége képzetes vektoraik különbsége. Ezután a vektoriális szorzatokat összevonjuk.

⟨ ( ) ⟩ ( ) ( ) ( )

⟨ ( ) ⟩ ( ) ( )

Egyrészt bevezetjük a és a ,

másrészt a ( ) jelölést. Behelyettesítünk:

( ) ( ) |

|

A valós rész egyetlen, bal oldali tagjának a jobb oldalon 0 felel meg ( de ):

( )

Majd kvaternió-komponensenként összehasonlítjuk a bal oldali és a jobb oldali együtthatókat:

( )

( )

( )

Page 119: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

117

Legyen akkor egyenleteink:

A fenti homogén lineáris egyenletrendszer egyenletei nem lineárisan függetlenek egymástól, így a

triviálistól eltérő megoldás létezik. Több összetartozó pontpár segítségével ebből a homogén lineáris

egyenletrendszerből megoldást nyerhetünk. ( x, y, z -t egyértelműsíti az egységvektorrá alakítás.)

Bíztatóbb (és egyszerűbb) a geometriai módszer. Az elforgatás során egy-egy pont helyváltoztatási

vektora a forgatási tengelyre merőleges. Két ilyen helyváltoztatási vektor vektoriális szorzata

megadja a tengelyirányt. A tengely az origón megy át, ezért a tengely egyenesének egyenletét

meghatározhatjuk. A pontok a tengelyre merőleges síkban mozognak a forgatás során. Felvehetünk

egy ilyen síkot, meghatározhatjuk a sík tengelypontját. Az eredeti pontot a tengelyponttal összekötő

irány és az elforgatott pontot a tengelyponttal összekötő irány hajlásszöge a forgatás szöge.

Az alábbiakban láthatják az F# programot, továbbá a programban példaként felhasznált esetnek

(tetraéder forgatása) megfelelő rajzot.

let ForgatásiTengely(színez : bool) = let tÚjPontok : Kvaternió array = [| O; O; O; O|] Felirat("Tetraéder", 200, 100, 20, Color.DarkCyan) HomogénKoordinátaHáttér( Color.Gray ) // Például szolgáló test ( #Tetraéder_szerkesztése ): let SíkS = Plane(EGY, ÖT, HÁROM) let EgyenesT = Line(-EGY, NÉGY, NÉGY, HAT, NÉGY, NÉGY) let döfésPont = SíkotDöfEgyenes(SíkS, EgyenesT) tPontok.[0] <- döfésPont tPontok.[1] <- SíkS.Rot120Kvat * tPontok.[0] * SíkS.Rot120Kvat.Konj tPontok.[2] <- SíkS.Rot120Kvat * tPontok.[1] * SíkS.Rot120Kvat.Konj let síkSúlypontja = (tPontok.[0] + tPontok.[1] + tPontok.[2]).Scal(EGY / HÁROM) tPontok.[3] <- Kvaternió(EGY, síkSúlypontja.Vect + SíkS.NormVect.Scal((tPontok.[1] - tPontok.[0]).abs * Math.Sqrt(KETTŐ / HÁROM))) let mutable rajzToll = new Pen(Color.Red, float32 4) ÉlRajz(rajzToll, tetraéderÉlek, tPontok) PontsorszámotÍr(tPontok, Color.Cyan)

Page 120: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

118

// Forgató kvaternió létrehozása (konstruktorral) és a forgatás // végrehajtása (i, j szögfelezője a tengelyirány, 75° a szög): let Forgató = Kvaternió( Vector(GYÖKKETTŐPERKETTŐ, GYÖKKETTŐPERKETTŐ, NULLA), double 75 ) for lI = 0 to tPontok.Length-1 do tÚjPontok.[lI] <- Forgató * tPontok.[lI] * Forgató.Konj rajzToll <- new Pen(Color.Navy, float32 4) ÉlRajz(rajzToll, tetraéderÉlek, tÚjPontok) PontsorszámotÍr(tÚjPontok, Color.Magenta) // Fordított művelet: a tengely és a szög meghatározása: // A tengely irányvektora két pont (az 1. és a 2. sorszámú) // elmozdulásvektorának vektoriális szorzata: let tengelyIrány = ((tPontok.[1].Vect - tÚjPontok.[1].Vect) * (tPontok.[3].Vect - tÚjPontok.[3].Vect)).Unit // A tengely az origón megy át, így a tengely egyenes előállítható: let tengelyEgyenes = Line(O, tengelyIrány) EgyenesRajz(Color.Indigo, tengelyEgyenes, MÁSFÉL) // Az alakzat egyik (3. sorszámú) pontjára a tengelyre merőleges // síkot fektetünk: let egyikSík = Plane(tPontok.[3], tengelyIrány) // A sík tengelypontja a sík és a tengely egyenesének döféspontjaként // határozható meg: let döfi = SíkotDöfEgyenes(egyikSík, tengelyEgyenes) // Az alakzat adott síkhoz tartozó nem elforgatott pontja és a // döféspont egy irányvektort szolgáltat: let vektorElőtte = tÚjPontok.[3].Vect - döfi.Vect // Az alakzat adott síkhoz tartozó elforgatott pontja és a // döféspont egy második irányvektort szolgáltat: let vektorUtána = tPontok.[3].Vect - döfi.Vect // Az irányvektorok vektoriális szorzatának abszolút értékéből a // forgatási szög szinusza, majd a forgatási szög kiadódik: let szög = ÍVBŐLFOK * Math.Asin((vektorElőtte * vektorUtána).abs / vektorElőtte.abs / vektorUtána.abs)

Page 121: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

119

Felirat("Forgatási tengely: " + tengelyIrány.iComp.ToString("0.000") + " * i + " + tengelyIrány.jComp.ToString("0.000") + " * j + " + tengelyIrány.kComp.ToString("0.000") + " * k", 100, 150, 16, Color.Brown) Felirat("Forgatás szöge: " + szög.ToString("0.000"), 100, 200, 16, Color.Brown) Természetesen ennél a számításnál is előfordulhat, hogy nem kapunk eredményt. Ennek oka például

az lehet, hogy a kiválasztott két pont egymással párhuzamos pályán mozdult el az elforgatás során,

akkor a tengely irányvektora ezzel a két ponttal nem számítható. (Persze, betehetünk a programba

olyan feltételes kifejezést, amely ezt a vizsgálatot is elvégzi, és olyankor, ha ez bekövetkezik, másik

kiindulási pontot választ.)

Page 122: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

120

Konvergencia-fraktálok

A komplex konvergencia-fraktálok a kvaterniókkal ugyanúgy megfigyelhetők. Például a valós tengely

és az képzetes tengely által meghatározott koordináta-síkon ( ; ).

Másrészt itt megvizsgáljuk a térbeli (kvaterniókon alapuló) fraktál számítását is. Természetesen a

kvaterniók négyzet-függvényét használva a Mandelbrot halmaz térbeli megfelelőjét láthatnánk.

Mivel ennek ábrázolása nem egyszerű feladat, az alábbiakban ennek síkmetszeteit mutatjuk meg. A

metsző síkok felvétele, persze, önkényes. Arra törekedünk, hogy fantáziánk segítségével a „fekete

testet” a metszetek alapján minél jobban el tudjuk képzelni.

Az alábbi ábrán a metszősíkok pontjainak halmazát leíró képleteket és a metszősíkok elhelyezését

mutató rajzot láthatnak. Az összetartozást a színezéssel próbáltuk érzékeltetni.

A további lapokon pedig azokat az ábrákat láthatják, amelyeket az egyes metszősíkokon állíthatunk

elő. (Mindegyik metszet fölött a sorozat elemeinek előállítására szolgáló képletet látják.)

Page 123: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

121

Page 124: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

122

Page 125: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

123

Page 126: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

124

Page 127: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

125

Page 128: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

126

Page 129: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

127

Page 130: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

128

Természetesen – mivel az és k szerepe a kvaternióknál teljesen szimmetrikus, helyett a

vagy a koordináta alkalmazásával pontosan ugyanilyen metszetekhez jutnánk. (Az alakzat

szimmetriatengelye a valós tengely.)

Az alábbi rajz vázlatosan mutatja az egyes komplex számsíkokon látható Mandelbrot halmazokat. A

lila az és a valós, a sötétvörös a és a valós, a zöld a és a valós tengely által kifeszített síkon

mutatja (csupán jelzés értékűen) a Mandelbrot halmaz elhelyezkedését.

Bizony, a kvaternió konvergencia-fraktálok kezelése nem olyan egyszerű, mint a komplex

konvergencia-fraktáloké! Maga a Mandelbrot-halmaznak megfelelő rücskös test térbeli képe esetleg

megrajzolható volna, de csak metszetek készítésével (a réteg-röntgenek mintájára) tudnánk olyan

látványos rajzokat készíteni, mint komplex esetben (ahogyan ezt az előző ábrákon láthatják).

Elképzelhető, hogy más utat kellene választani! Esetleg olyan síkbeli ábrákat kellene készíteni,

amelyeknél az egyik tengely a valós rész, a másik pedig a képzetes rész abszolút értéke. (Hiszen a

képzetes rész három komponensének teljesen szimmetrikus a hatása.)

Azt mindenesetre kipróbáltam, hogy – – illetve – metszősíkban a konvergencia-

halmaznak nincs kükönleges szerkezete, körszimmetrikus ábrákat kapunk. Másrészt a fekete kör

mérete függ a valós összetevő (mint paraméter) értékétől.

Page 131: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

129

Összefoglalás

A kvaterniókat felhasználásuk szempontjából több csoportba sorolhatjuk: lehetnek a szokásos

háromdimenziós tereinkhez tartozó térkvaterniók, síkkvaterniók valamint forgató kvaterniók.

A forgató kvaterniót célszerűen mindig egység kvaternióként választjuk (egyébként a forgatás során

számított szorzat összetevőit a forgató kvaternió abszolút értékének négyzetével is osztani kellene).

Egység forgató kvaternió valós része a forgatási szög felének koszinusza, képzetes része ugyanezen

szög szinuszával szorozva a forgatás tengelyének egységvektora. Forgató kvaternió minden egység

vektorkvaternió (tisztán képzetes kvaternió) is, amely a vektor irányának megfelelő, origón átmenő

tengely körül 180°-kal forgat, illetve (mert ez ugyanazt jelenti) e tengelyre vonatkozólag tengelyes

tükrözést végez. Két ilyen, egység vektorkvaternióval kijelölt tengelyű tükrözés egymásutánja a két

vektor szögének duplájával elvégzett forgatást eredményez. Ez az elforgatás végrehajtható a két

vektorkvaternió összeszorzásával nyert forgató kvaternióval is.

A síkkvaterniók nem a térkvaterniók kétdimenziós szűkítései, hanem a tér síkjai leírására szolgáló

négy együttható összefogására szolgáló szerkezetek. Olyan kvaterniók, amelyeknek képzetes vektor

része egységvektor, valós része pedig negatív szám. (Origón átmenő sík esetén a valós rész nulla.

Ebben az esetben – formálisan – a síkkvaternió vektorkvaternió is.) A síkkvaterniók valós része a sík

origótól mért negatív távolsága, képzetes vektor részük pedig a sík normális egységvektora. A

kvaternió ilyen „normálása” azt biztosítja, hogy a sík normálvektora az origótól kifelé mutat.

A térkvaterniók, illetve pontkvaterniók esetén az azonos valós részűeket tekinthetjük egy világhoz

tartozóknak (ha a valós tengely az idő, egyazon pillanathoz tartozóknak). Ezeknek a kvaternióknak a

képzetes részével a szokásos vektorműveleteket végezhetjük el. Az ugyanazon háromdimenziós

térhez tartozó vektorok kvaternióit (amelyek tehát azonos valós részűek) a forgatási transzformáció

sem választja el egymástól, hiszen valós részük a forgatás után is ugyanaz marad. Beláttuk azt is,

hogy a forgató transzformáció elvégzése során az elforgatott kvaternió valós része nem befolyásolja

az eredmény képzetes részét, az elforgatott vektort.

A térbeli pozíció leírására szolgáló homogén koordinákat ebben az anyagban mindig az 1 kiegészítő

koordinátával tároljuk a kvaterniókban. A homogén koordinátás háromdimenziós teret ezért nem a

kvaternió origóhoz helyezve, hanem a valós 1 egységhez (Base0-hoz) eltolva mutatjuk. Ennek az

eltolt háromdimenziós térnek a koordinátatengelyeit rajzainkon vékony piros vonallal tüntetjük fel.

(Persze, a rajzon ábrázolt síkok esetén is a fentiekben mondott origótól mért távolság alatt ilyenkor a

Base0-tól mért távolságot értjük.) Ebből az eltolt térből „kilógnának” az ideális pontok, amelyeknek

0 a kiegészítő homogén koordinátájuk, de ezeket úgysem tudnánk a háromdimenziós, véges tereink

képén megmutatni. (Nagyon szerencsés viszont, hogy az tengely, tengely és a tengely

ideális – végtelen távoli – pontjának kvaterniója éppen BaseI, BaseJ és BaseK-val egyezik meg.)

Az anyag első összeállítása során a perspektivikus leképezés tanulmányozásakor döbbentem rá arra,

hogy a tér pontjaihoz rendelhető homogén koordináták tárolására éppen négy helyre van szükség! A

kiegészítő, negyedik koordinátát el lehet helyezni egy kvaternió valós részében is! Ezért az anyagot

Page 132: Ormai Lóránt: Kvaterniók és alkalmazásuk

Kvaterniók és alkalmazásuk

130

teljesen átdolgoztam. Két új fejezetet is beépítettem a dolgozatba. Lényeges változtatásokat

hajtottam végre a Kvaternió_műveletek modulon is: a végtelen értékek kezelésére a homogén

koordinátarendszer ideális pontjainak megfelelően új tulajdonságokat, metódusokat,

konstruktorokat készítettem, továbbá beépítettem a vektor és a kvaternió osztályhoz egyenlőséget

vizsgáló relációkat, illetve függvényeket, végül programoztam a négydimenziós skaláris és a

négydimenziós vektoriális szorzás függyényt is. Kibővítettem a Kvaterniós_szerkesztések modult is,

ugyanis több függvénynek elkészítettem azt a változatát is, amely a homogén koordinátás

kvaterniókon alapul.

Volt, ahol javítanom kellett. Machanikusan nem lehetett minden kvaternió 0 valós értékét 1-re

cserélni. Például a háromdimenziós térbeli középpontos tükrözést kvaternió origóra vett tükrözéssel

lehetett (bár elvileg hibásan, de jó eredményt adóan) programozni, most csak a kvaternió vektor

részével végzem el a tükrözést (ez bárhová eltolt háromdimenziós tér esetén is jól működik).

A forgatásokkal nincsen probléma. Féltem volna egy olyan változtatástól, amikor kvaterniók valós

részébe önkényesen 1-et írunk, ha nem mutattam volna ki korábban, hogy a valós rész értéke nem

változik a forgatási transzformáció során, valamint, hogy a valós rész értéke a forgatásnál nem hat a

képzetes részre. Másrészt a forgatás tengelye például sík esetén az origóból a Near pontba mutatott

(iránya a sík normálvektorának iránya volt). Most ez csak annyiban módosult, hogy az origó helyett

Base0 ponton megy át a tengely, és a sík Near pontja esetén is a kvaternió valós részének értéke 1.

A homogén koordináták tárolására a kvaternió kiválóan alkalmas. A kvaternió valós és képzetes

részének megkülönböztetése is jól illik a homogén koordináták kiegészítő és valódi részének

elkülönítésére. A sík homogén koordinátás négydimenziós együtthatóvektora, a síkkvaternió pedig a

kvaternió részek szerinti felbontásban is teljesen értelmes: a valós rész a sík Base0-tól mért

távolsága, a képzetes rész a sík normális egységvektora.

A térbeli szerkesztések elvégzésére kellemesen használható eszközt nyertünk. A kvaterniókban a

pontokat mindig homogén koordinátákkal tárolhatjuk. Az egyeneseket egy pont és egy nyújtott-

zsugorított, tükrözött irányvektor kettőseként kezelhetjük. A síkoknál pedig homogén koordinátás és

Plane objektumos alakjuk között lavírozhatunk aszerint, hogy mikor milyen kezelésre van szükség. A

négydimenziós homogén koordinátás szorzásokkal kézenfekvő a három pontra illeszkedő sík, illetve a

három sík metszéspontjának meghatározása, de nagyon egyszerű annak vizsgálata is, hogy egy pont

illeszkedik-e egy síkra, illetve annak számítása, hogy milyen távol van egy pont egy síktól.

A térbeli forgatást el lehet végeztetni vektorok és mátrixok segítségével is. Viszont a kvaternióval

megvalósított forgatáshoz kevesebb adat meghatározására és tárolására van szükség, és a forgató

kvaternió előállítása, valamint a forgatás végrehajtása is egyszerűbb. (Ebben az anyagban csupán

utaluk arra, hogy a transzformációs mátrix a forgatás elvégzésére a kvaternió konkurense, homogén

koordináták alkalmazása pedig a transzformációs mátrix egységesebb használatát eredményezheti.)

Budapest, 2013. február 5. (Vissza a #TARTALOMra )

Page 133: Ormai Lóránt: Kvaterniók és alkalmazásuk

Az elektronikus könyv kiadója a Kossuth Kiadó. Felelős kiadó Kocsis András Sándor Az elektronikus könyv szerzői és kiadói jogvédelem alatt áll. Illetéktelen másolása, reprodukálása, módosítása a szerző és a kiadó írásbeli hozzájárulása nélkül tilos. Az elektronikus kiadáshoz fűződő minden jog fenntartva. © Ormai Lóránt 2012 © Kossuth Kiadó 2012 ISBN 978-963-09-7458-5 Az elektronikus kiadásban közreműködött Grőb Krisztián, Pekó Zsolt Projektvezető Földes László www.multimediaplaza.com www.kossuth.hu