Feladat egy egyváltozós valós függvény kirajzolása különféle megjelenítési módszerekkel. Például:
· pontokkal,
· folytonos szakaszokkal,
· téglalapokkal,
· … .
Az elvárásokat legjobban az alábbi, futás során keletkezett ábrasor fejezi ki,
|
1.
ábra. Egy futási
kép – a sinus függvény. |
valamint egy (teljesebb) próba: Fv1Rajz.exe.
Alapadatok:
· f – függvénykapcsolat; f: Df ® Rf,
· Df – (az „érdekes”) értelmezési tartomány = [minx..maxx]
· Rf – (az „érdekes”) értékkészlet = [miny..maxy]
|
2. ábra. A transzformáció „ránézésre”. |
A (lineáris) transzformáció ellenőrzése 2, különböző pontra:
· Az origó leképezése: F((0,0)) = (o0+0,s0–0) = (o0,s0)
· A bal-felső sarok leképezése: F((-o0,s0)) = (o0–o0,s0–s0) = (0,0)
1. túl szűk/tág az értékkészlete
2. túl szűk/tág az értelmezési tartománya
3. függőlegesen alul/felül kilóg
4. vízszintesen balra/jobbra kilóg
Léptékezés (1-2.-re), az origó eltolása (3-4.-re).
Nyújtás (a teljes [0..maxO]´[0..maxS] képernyőre)
· X-irányban: nx = (maxO+1) / (maxx–minx)
· Y-irányban: ny = (maxS+1) / (maxy–miny)
A számlálóbeli +1 a képernyő-koordinátarendszer egész léptékének köszönhető: annyi darab pont van az oszlopokban, illetve a sorokban.
Origóeltolás · o0 = -minx*nx ·
s0
= maxy*ny
Így a keresett F leképezés: · o(x) = o0+x*nx · s(y) = s0-y*ny |
|
Az algoritmus ötlete pofonegyszerű:
1. generáljuk le az ábrázolandó függvényt egy ún. függvénytáblába, valamekkora lépésközzel, célszerűen: ekvidisztáns alappontok felett, növekvő x-ek mentén;
2. majd (most már függvény kiszámítási szabályától függetlenül) rajzoljuk ki a függvénytáblában lévő pontokon nyugvó függvénygörbét, valamilyen módszerrel.
Algoritmikus adatok és egyéb kellékek:
Függvény f(Konstans x:Valós):Valós
… a kirajzolandó függvényt leíró
függvényeljárás …
Típus
TKoordináták =
Tömb(1..MaxN:Valós) [koordináták]
TFüggvényTábla = Rekord(
n:Egész,
x,y:TKoordináták [1],
minY,maxY:Valós)
Változó [globálisak]
ft:TFüggvényTábla [egy fv. gráfjának
pontjai]
mag,szél:Egész [az aktuális képernyő ablak méretei]
Változó [az egyes módszerekhez, lokálisak]
o0,s0:Egész [az origó helye a képernyőn]
nx,ny:Valós [nyújtási tényezők]
dx:Egész [oszlopszélesség]
eo,es:Egész [az előzőleg kirajzolt pont]
Eljárás PontRajzolás(Konstans x,y:Valós):
[Ef: helyes o0,s0, nx,ny]
Változó
o,s:Egész
o:=o0+Kerekít(x*nx);
s:=s0-Kerekít(y*ny)
Pont(o,s)
[eo:=o; es:=s] [2]
Eljárás vége.
[A
Pont eljárás ún. grafikus primitív, Turbo Grafikában [3]:
PutPixel.]
Eljárás SzakaszRajzolás(Konstans hozX,hozY:Valós):
[Ef: az (eo,es) pont már ki van
rajzolva, s ez az aktuális]
Változó
s,o:Egész
o:=o0+Kerekít(hozX*nx);
s:=s0-Kerekít(hozY*ny)
Szakasz(eo,es,o,s)
eo:=o; es:=s
Eljárás vége.
[A
Szakasz eljárás ún. grafikus primitív, TG-ben: Line.]
Eljárás TéglaRajzolás(Konstans x,y:Valós):
[Ef: dx=az oszlopok szélessége (Egész)]
Változó
s,o:Egész
o:=o0+Kerekít(x*nx);
s:=s0-Kerekít(y*ny)
Tégla(o,s,o+dx,o0-sgn(y) [ne érjen rá az x-tengelyre! [4]] )
Eljárás vége.
[A
Tégla eljárás ún. grafikus primitív, TG-ben: Bar.]
Nem figyeljük a „képernyőre miként kerülést”… az origó középen, léptékezés nincs. A „ránézésre” transzformáció méltó párja:
…
o0:=maxO Dix 2; s0:=maxS Div 2
Ciklus i=1-től ft.n-ig
PontRajzolás(ft.x(i),ft.y(i))
Ciklus vége
…
[ef: ft.x szigorúan növekvően rendezett tömb Þ
"iÎ(1..ft.n): ft.x(1)<ft.x(i)<ft.x(ft.n)]
…
nx:=(maxO+1)/(ft.x(ft.n)-ft.x(1));
ny:=(maxS+1)/(ft.maxY-ft.minY)
o0:=-nx*ft.x(1); s0:=maxS+ny*minY
Ciklus i=1-től ft.n-ig
PontRajzolás(ft.x(i),ft.y(i))
Ciklus vége
…
[ef: ft.x szigorúan növekvően rendezett tömb]
…
nx:=(maxO+1)/(ft.x(ft.n)-ft.x(1)); ny:=(maxS+1)/(ft.maxY-ft.minY)
o0:=-nx*ft.x(1); s0:=maxS+ny*minY
Ha ny>nx akkor
ny:=nx különben nx:=ny [egyszerűbben:
nx,ny:=Min(nx,ny)]
Ciklus i=1-től ft.n-ig
PontRajzolás(ft.x(i),ft.y(i))
Ciklus vége
…
Hogyan lehetne megvalósítani egy ábrán több függvény kirajzolását, ha ügyelni kell az arányokra (az egymáshoz s esetleg: saját magukhoz)? Fölteheti, hogy a függvények értelmezési tartománya ugyanaz, sőt az alappontok is megegyeznek.
[ef:
ft.x szigorúan növekvően rendezett tömb]
…
[a 3.2 vagy a 3.3 inicializálása] …
PontRajzolás(ft.x(1),ft.y(1))
Ciklus i=2-től ft.n-ig
SzakaszRajzolás(fv.x(i),ft.y(i))
Ciklus vége
…
[ef:
ft.x szigorúan növekvően rendezett, és azonos különbségű
tömb]
…
[a 3.2 vagy a 3.3 inicializálása] …
Ciklus i=1-től ft.n-ig
TéglaRajzolás(ft.x(i),ft.y(i))
Ciklus vége
…
Ez ötvözi a szakaszokkal és a téglákkal történő rajzolást. Hátránya, általában kevesebb segítséget nyújtanak a programnyelvek. [5] Elemibb műveletekkel (szakaszrajzolás és tartományszínezés) persze nem nagy munka árán megoldható.
Az algoritmizálás és kódolás: hf.
Számos módszer közül választhatunk. Legkézenfekvőbb, hogy az N pontra ráfektethetünk egy N-1-ed fokú polinomot. Másik szokásos módszer (ill. inkább módszercsalád), hogy a szomszédos pontokra valahanyadfokú (de egyedenként paraméterezett) polinomot illesztünk úgy, hogy az adott pontokon átmenjenek, sőt –hogy ez az „átmenet” az egyik polinomból a másikba minél „simább” legyen– az érintők egyenlőségét is meg szokták követelni.
Az algoritmizálás és kódolás: hf.
Segítségképpen alább egy könnyen megvalósítható módszerről ötletelek:
1. Az 1-3 pontra ráfektetünk egy parabolaívet. (Ez egyértelműen elvégezhető.)
2. A 3.-tól az egymást követő pontpárokra úgy fektetünk parabolaíveket, hogy az a bal oldali szomszédjához „érintőlegesen” is illeszkedjen.
3. A parabolaíveket fi(x)=aix2+bix+c alakban írjuk föl. Az Ax=b vektoregyenletet kell megoldanunk, úgy hogy az A együtthatómátrixot tartalmazza azon lineáris egyenletek alappontok x-eiből számított konstansait, amelyek az 1-2. alapján készültek. Az Ax=b egyenlet túl oldali vektorának elemei (b) részben az y-okból fog állni, részben egyéb megfontolásokból származnak.
4. Képezzük az együtthatómátrix inverzét (A-1).
5. Megoldjuk az egyenletet: x=A-1b.
Megjegyzem: van számos hatékony módszer a numerikus matematikában lineáris egyenletrendszerek megoldására; így a mátrix invertálás valójában nem kardinális. Ilyen módszer például a Gauss-Jordan elimináció. (Ez az x vektor mellett az A-1-et is előállítja, bár minket csak az x érdekel.) [6]
Nézzünk egy konkrét példát!
Ha 4 pontra kell fektetni görbeíveket, akkor 2 parabolaívvel oldható meg a probléma. Az első 3 pontra egyértelműen fektethetünk egyet, majd a 3. és 4. pontra azzal a plusz feltétellel, hogy ez utóbbi érintője a 3. pontban egyezzen meg az első ugyanezen pontbeli érintőjével. Azaz a keresett 6 ismeretlen paraméterre az alábbi egyenleteket állíthatjuk föl.
f1(x1)=y1, f1(x2)=y2, f1(x3)=y3
f2(x3)=y3, f2(x4)=y4,
f1'(x3)-f2'(x3)=0
A részletszámítások nélkülözésével, csak az egyes lépések végeredményét mutatja az alábbi ábra.
Tehát megkaptuk a két parabolaív paramétereit:
f1(x)=-3x2+6x+1 xÎ[x1..x3], f2(x)=9x2-42x+49 xÎ[x3..x4]
Hogy érjünk is ezzel az izzasztó eredménnyel valamit, az adott x-pontok között finomabb lépésekben is számíthatjuk az interpolált értékeket. Ezáltal kevés pont esetén is „szép” görbével tudjuk megjeleníteni az adott görbeívet.
Lássuk, mit kapunk a kirajzolás után a fenti négy pontra illeszkedő függvénygörbe gyanánt!
Ahhoz képest, hogy eredetileg összesen csak négy pont
volt adva (pirossal szakaszokkal jelölve), elégedettek lehetünk a látvánnyal
(kékkel jelölve). Itt összesen 8 köztes pontot számoltunk az egyes rögzített
pontok között.
Az itt körvonalazott módszer (kirajzolás nélküli, a keretbe egy az egyben nem bielliszthető) megvalósítását is megtalálhatja: GaussJorda.zip.
Lásd Fv1RajzK.htm, Fv1RajzK.pas.
A 3 tervezett és fentebb kidolgozott megoldást tartalmazzák
az alábbi fájlok: Fv1Rajz.htm, Fv1Rajz.pas.
Házi feladatként érdemes a 3.6-ban vagy a 3.7-ben
körvonalazott megjelenítési módokat megvalósítani. A 3.6-hoz lényegileg minden
ismert (az anyag alapján). A 3.7-ben körvonalazott módszer (a hivatkozott
programban megvalósított kóddarab felhasználásával) már viszonylag könnyen
elkészíthető. „Csupán” az adott szomszédos pontokat összekötő parabolaíveket
finomabb lépésközű „alpontokra” kell illeszteni, (pontokkal, szakaszokkal vagy
téglákkal) kirajzolni.
[1] Az x-koordináták növekvően rendezettek Þ minX=ft.x(1), maxX=ft.x(ft.n).
[2] A SzakaszRajzolás eljárás majd épít arra, hogy az utoljára kirajzolt pont helye meglegyen az (eo,es)-ben.
[3] Akár Turbo Pascalban, akár Free Pascalban a Graph unit tartalmazza a grafikus „szókincset”.
[4] Meggondolandó az x=0, és az y=0 esete!
[5] Üdítő kivétel a Turbo Grafika e célra kiváló két eljárása:
Procedure DrawPoly(Const db:Word; Const pontok);
Procedure
FillPoly(Const db:Word; Const pontok);
{a pontok: db darab a Graph unitban
definiált PointType típusú adat kezdőcíme}
Type
PointType=Record x,y:Integer End;
[6] Ennek kipróbálására szíves figyelmükbe ajánlom:
GaussJordan.exe programot, illetve a forrását: GaussJordan.pas!